Merge remote-tracking branch 'origin/main' into codex/fix-im-reliability
This commit is contained in:
@@ -59,7 +59,7 @@ RUNTIME_CONTRACT_END = "<!-- DWS_RUNTIME_CONTRACT_END -->"
|
||||
# here only after verifying that the product skill has its own reviewed routing
|
||||
# section and intent table; compacting a sparse skill without an alternative
|
||||
# route would make its shortcuts harder to discover.
|
||||
COMPACT_PRODUCT_SERVICES = {"chat"}
|
||||
COMPACT_PRODUCT_SERVICES = {"chat", "doc"}
|
||||
|
||||
|
||||
def md_escape(value: Any) -> str:
|
||||
@@ -121,7 +121,10 @@ def runtime_contract_block() -> str:
|
||||
|
||||
|
||||
def mono_overview(items: list[dict[str, Any]]) -> str:
|
||||
counts = Counter(item["service"] for item in items)
|
||||
# The source collector cannot see a small number of commands whose final
|
||||
# canonical names are normalized at runtime. Overview counts come from the
|
||||
# reviewed public catalog so they cannot under-report those declarations.
|
||||
counts = Counter(service for service, _ in load_public_catalog())
|
||||
rows = []
|
||||
for service, count in sorted(counts.items()):
|
||||
path = SERVICE_TO_SKILL.get(service)
|
||||
@@ -163,10 +166,18 @@ def product_section(service: str, rows: list[dict[str, Any]]) -> str:
|
||||
|
||||
|
||||
def compact_product_section(service: str, rows: list[dict[str, Any]]) -> str:
|
||||
# Compact skills intentionally do not depend on the source parser's ability
|
||||
# to recover every runtime-normalized declaration. The reviewed public
|
||||
# catalog is the count authority for this non-enumerating overview.
|
||||
public_count = sum(1 for item_service, _ in load_public_catalog() if item_service == service)
|
||||
if service == "doc":
|
||||
discovery = """已知意图按下方路由。"""
|
||||
else:
|
||||
discovery = """已知意图直接使用下方的优先路由、意图表或任务 reference;命令已选中时直接执行,只在参数/安全语义不确定时读取 leaf Schema,在当前 Cobra flags 不确定时读取 leaf Help。"""
|
||||
return f"""{PRODUCT_START}
|
||||
## Shortcut 发现(按需)
|
||||
|
||||
`{md_escape(service)}` 当前有 {len(rows)} 条公开 shortcut,完整清单保留在 Runtime Catalog 与 Schema,不在高频产品根 Skill 中重复展开。已知意图直接使用下方的优先路由、意图表或任务 reference;命令已选中时直接执行,只在参数/安全语义不确定时读取 leaf Schema,在当前 Cobra flags 不确定时读取 leaf Help。
|
||||
`{md_escape(service)}` 当前有 {public_count} 条公开 shortcut,完整清单保留在 Runtime Catalog 与 Schema,不在高频产品根 Skill 中重复展开。{discovery}
|
||||
|
||||
仅当现有路由和 reference 都无法定位低频能力时,才执行 `dws shortcut list --service {md_escape(service)} --format json` 做最后回退;不要为已知高频意图加载完整 Shortcut Catalog 或产品级 Schema。
|
||||
{PRODUCT_END}"""
|
||||
@@ -194,6 +205,7 @@ def update_runtime_contract(check: bool) -> list[Path]:
|
||||
changed = []
|
||||
targets = [
|
||||
ROOT / "skills" / "multi" / "dingtalk-chat" / "SKILL.md",
|
||||
ROOT / "skills" / "multi" / "dingtalk-doc" / "SKILL.md",
|
||||
SHARED_SKILL,
|
||||
]
|
||||
for path in targets:
|
||||
|
||||
@@ -7,12 +7,14 @@ cd "$ROOT"
|
||||
python3 scripts/gen_skill_shortcut_sections.py --check
|
||||
|
||||
chat_skill="skills/multi/dingtalk-chat/SKILL.md"
|
||||
doc_skill="skills/multi/dingtalk-doc/SKILL.md"
|
||||
event_skill="skills/multi/dingtalk-misc/references/event.md"
|
||||
mono_skill="skills/mono/SKILL.md"
|
||||
runtime_contract="skills/multi/dingtalk-shared/references/runtime-contract.md"
|
||||
chat_target_bytes=10000
|
||||
chat_max_overage_percent=5
|
||||
chat_max_bytes=$((chat_target_bytes * (100 + chat_max_overage_percent) / 100))
|
||||
doc_max_bytes=9000
|
||||
event_max_bytes=10000
|
||||
runtime_contract_max_bytes=3000
|
||||
|
||||
@@ -23,6 +25,13 @@ if [ "$chat_bytes" -gt "$chat_max_bytes" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
doc_bytes="$(wc -c < "$doc_skill" | tr -d ' ')"
|
||||
if [ "$doc_bytes" -gt "$doc_max_bytes" ]; then
|
||||
printf '%s\n' \
|
||||
"skill context budget exceeded: $doc_skill is ${doc_bytes} bytes (max ${doc_max_bytes})" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
event_bytes="$(wc -c < "$event_skill" | tr -d ' ')"
|
||||
if [ "$event_bytes" -gt "$event_max_bytes" ]; then
|
||||
printf '%s\n' \
|
||||
@@ -51,6 +60,20 @@ if [ "$shortcut_rows" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
doc_shortcut_rows="$(
|
||||
awk '
|
||||
/<!-- VISIBLE_SHORTCUTS_START -->/ { in_block = 1; next }
|
||||
/<!-- VISIBLE_SHORTCUTS_END -->/ { in_block = 0 }
|
||||
in_block && /^\| `dws doc \+/ { count++ }
|
||||
END { print count + 0 }
|
||||
' "$doc_skill"
|
||||
)"
|
||||
if [ "$doc_shortcut_rows" -ne 0 ]; then
|
||||
printf '%s\n' \
|
||||
"skill context budget exceeded: $doc_skill re-expanded $doc_shortcut_rows shortcut rows" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for required_heading in \
|
||||
"## 最小 DWS 执行契约" \
|
||||
"## Golden Route" \
|
||||
@@ -65,6 +88,67 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
for required_heading in \
|
||||
"## 最小 DWS 执行契约" \
|
||||
"## Golden Route" \
|
||||
"## 关键结果语义" \
|
||||
"## 按需加载" \
|
||||
"## 错误最短路径"
|
||||
do
|
||||
if ! grep -Fq "$required_heading" "$doc_skill"; then
|
||||
printf '%s\n' \
|
||||
"skill value regression: $doc_skill is missing $required_heading" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
for required_route in \
|
||||
"+search" \
|
||||
"+fetch" \
|
||||
"+create" \
|
||||
"+update" \
|
||||
"+checkpoint-update" \
|
||||
"+export"
|
||||
do
|
||||
if ! grep -Fq -- "$required_route" "$doc_skill"; then
|
||||
printf '%s\n' \
|
||||
"skill route regression: $doc_skill is missing $required_route" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
for forbidden_route in \
|
||||
"## 标准 SOP" \
|
||||
"doc_create_and_write.py" \
|
||||
"只在任务命中时读取一个精确 reference" \
|
||||
"MUST 先用 Read" \
|
||||
"先用 Read 工具完整读取"
|
||||
do
|
||||
if grep -Fq "$forbidden_route" "$doc_skill"; then
|
||||
printf '%s\n' \
|
||||
"skill route regression: $doc_skill restored legacy route: $forbidden_route" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
for required_reference_rule in \
|
||||
"Golden Route 已给出命令且参数足够时,禁止读取 reference" \
|
||||
"才最多读取一个 reference" \
|
||||
"\`+create\`、\`+fetch\`、\`+update\` append/overwrite、\`+export\`、\`+import\` 禁止读取 reference"
|
||||
do
|
||||
if ! grep -Fq "$required_reference_rule" "$doc_skill"; then
|
||||
printf '%s\n' \
|
||||
"skill reference budget regression: $doc_skill is missing $required_reference_rule" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if grep -Fq "../dingtalk-shared/SKILL.md" "$doc_skill"; then
|
||||
printf '%s\n' \
|
||||
"skill context regression: $doc_skill requires full dingtalk-shared cold-start loading" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for required_route in \
|
||||
"+dm" \
|
||||
"+send-to-group" \
|
||||
@@ -108,4 +192,4 @@ if grep -Fq "充分阅读产品参考文件" "$mono_skill"; then
|
||||
fi
|
||||
|
||||
printf '%s\n' \
|
||||
"skill context budget: ok (chat_bytes=$chat_bytes target=$chat_target_bytes max=$chat_max_bytes allowance=${chat_max_overage_percent}% event_bytes=$event_bytes event_max=$event_max_bytes runtime_contract_bytes=$runtime_contract_bytes runtime_contract_max=$runtime_contract_max_bytes shortcut_rows=$shortcut_rows)"
|
||||
"skill context budget: ok (chat_bytes=$chat_bytes target=$chat_target_bytes max=$chat_max_bytes allowance=${chat_max_overage_percent}% doc_bytes=$doc_bytes doc_max=$doc_max_bytes event_bytes=$event_bytes event_max=$event_max_bytes runtime_contract_bytes=$runtime_contract_bytes runtime_contract_max=$runtime_contract_max_bytes shortcut_rows=$shortcut_rows doc_shortcut_rows=$doc_shortcut_rows)"
|
||||
|
||||
@@ -527,7 +527,8 @@ func checkToolCompatibility(toolPath string, oldTool, newTool toolSchema) []stri
|
||||
}
|
||||
if oldTool.Constraints != newTool.Constraints &&
|
||||
!compatibleHiddenSiblingConstraintExpansion(oldTool, newTool) &&
|
||||
!compatibleAdditiveConstraintEvolution(oldTool, newTool) {
|
||||
!compatibleAdditiveConstraintEvolution(oldTool, newTool) &&
|
||||
!compatibleReviewedConstraintTransition(toolPath, oldTool, newTool) {
|
||||
failures = append(failures, fmt.Sprintf("schema tool %q changed constraints", toolPath))
|
||||
}
|
||||
if !compatiblePositionals(oldTool.Positionals, newTool.Positionals) {
|
||||
@@ -584,6 +585,31 @@ var reviewedInterfaceRefRedirect = map[string]map[string]string{
|
||||
},
|
||||
}
|
||||
|
||||
// reviewedConstraintTransition enumerates exact constraint changes that were
|
||||
// explicitly reviewed even though the compatibility gate cannot infer their
|
||||
// business semantics. Keep this narrow: removing a constraint can expose a new
|
||||
// runtime route, so arbitrary removals must not pass as harmless drift.
|
||||
var reviewedConstraintTransition = map[string]map[string]string{
|
||||
// PR #933 aligned the Shortcut and Schema with the existing doc import
|
||||
// runtime: omitting both targets imports into the default root. Keeping the
|
||||
// historical require_one_of made the documented Golden Route unreachable.
|
||||
"doc/doc.shortcut_import": {
|
||||
`{"require_one_of":[["folder","workspace"]]}`: "",
|
||||
},
|
||||
}
|
||||
|
||||
func compatibleReviewedConstraintTransition(toolPath string, oldTool, newTool toolSchema) bool {
|
||||
transitions, ok := reviewedConstraintTransition[toolPath]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
want, ok := transitions[oldTool.Constraints]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return want == newTool.Constraints
|
||||
}
|
||||
|
||||
// compatibleInterfaceRefRedirect accepts repointing a tool at a different
|
||||
// backing RPC when the migration is an explicitly reviewed entry in
|
||||
// reviewedInterfaceRefRedirect **and** the CLI-facing contract is provably
|
||||
|
||||
@@ -712,6 +712,37 @@ func TestCrossPlatformCoverageSchemaCompatAdditiveConstraintEvolution(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageSchemaCompatReviewedConstraintTransition(t *testing.T) {
|
||||
const toolPath = "doc/doc.shortcut_import"
|
||||
oldTool := toolSchema{Constraints: `{"require_one_of":[["folder","workspace"]]}`}
|
||||
newTool := oldTool
|
||||
newTool.Constraints = ""
|
||||
|
||||
if !compatibleReviewedConstraintTransition(toolPath, oldTool, newTool) {
|
||||
t.Fatal("reviewed doc import target removal must be accepted")
|
||||
}
|
||||
if failures := checkToolCompatibility(toolPath, oldTool, newTool); len(failures) != 0 {
|
||||
t.Fatalf("reviewed doc import constraint transition failed: %v", failures)
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
path string
|
||||
old string
|
||||
new string
|
||||
}{
|
||||
{name: "unlisted tool", path: "doc/doc.other", old: oldTool.Constraints, new: ""},
|
||||
{name: "unlisted source", path: toolPath, old: `{"require_one_of":[["folder","workspace","name"]]}`, new: ""},
|
||||
{name: "unlisted target", path: toolPath, old: oldTool.Constraints, new: `{}`},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if compatibleReviewedConstraintTransition(test.path, toolSchema{Constraints: test.old}, toolSchema{Constraints: test.new}) {
|
||||
t.Fatal("unreviewed constraint transition unexpectedly passed")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clearing a property through the reviewed mapping exclusion table is the one
|
||||
// accepted shape, mirroring the interface_type retirement allowance. A leaf
|
||||
// whose backing RPC moved to a nested payload has no honest flat property to
|
||||
|
||||
Reference in New Issue
Block a user