fix(schema): enforce catalog and interface completeness

This commit is contained in:
玉澜
2026-07-13 14:01:26 +08:00
parent f77232d7c1
commit d41ea586bf
13 changed files with 199 additions and 22 deletions
+1
View File
@@ -76,6 +76,7 @@ type embeddedMCPMetadata struct {
}
type embeddedMCPMetadataCoverage struct {
SurfaceScope string `json:"surface_scope,omitempty"`
SourceServices int `json:"source_services,omitempty"`
SnapshotServices int `json:"snapshot_services,omitempty"`
MissingServices []string `json:"missing_services,omitempty"`
@@ -1,6 +1,6 @@
{
"version": 1,
"source_hash": "sha256:b9fd6c4de190c413312561049ed2bbb80b272aad127ba2c0a5082364761012ee",
"source_hash": "sha256:b0788dd79d817258e2fd5869e4b32eff5ab81b6d20f34e7eb131446670402003",
"surface_hash": "sha256:75c63f35d9b15dfb4df09136bfd250906751cfeb7f14bce08cf092097859baad",
"coverage": {
"surface_products": 20,
@@ -1,6 +1,6 @@
{
"version": 1,
"source_hash": "sha256:b9fd6c4de190c413312561049ed2bbb80b272aad127ba2c0a5082364761012ee",
"source_hash": "sha256:b0788dd79d817258e2fd5869e4b32eff5ab81b6d20f34e7eb131446670402003",
"surface_hash": "sha256:75c63f35d9b15dfb4df09136bfd250906751cfeb7f14bce08cf092097859baad",
"source_files": 101,
"hint_files": 13,
+3 -1
View File
@@ -140,6 +140,7 @@ func TestRuntimeSchemaReportsEmbeddedInterfaceMetadata(t *testing.T) {
SourceRevision: "revision-test",
SourceHash: "sha256:interface-test",
Coverage: embeddedMCPMetadataCoverage{
SurfaceScope: "source_revision",
SourceTools: 10,
SurfaceTools: 2,
MatchedTools: 1,
@@ -159,7 +160,8 @@ func TestRuntimeSchemaReportsEmbeddedInterfaceMetadata(t *testing.T) {
if summary["source"] != "cli-registry" || summary["source_hash"] != "sha256:interface-test" || summary["tool_count"] != 1 {
t.Fatalf("interface metadata summary = %#v", summary)
}
if summary["source_revision"] != "revision-test" || summary["coverage"] == nil {
coverage, _ := summary["coverage"].(embeddedMCPMetadataCoverage)
if summary["source_revision"] != "revision-test" || coverage.SurfaceScope != "source_revision" {
t.Fatalf("interface metadata provenance = %#v", summary)
}
+3 -2
View File
@@ -1,12 +1,12 @@
{
"version": 1,
"source_hash": "sha256:3e93a6610ff04533eff6758bdc71c758ed052f873a8206ccfaf35912706d581f",
"source_hash": "sha256:a2ad77f7d8c0816a0e553a5ee5306c6ca0f2139edb83207c35eefecd2411f72f",
"surface_hash": "sha256:75c63f35d9b15dfb4df09136bfd250906751cfeb7f14bce08cf092097859baad",
"catalog": {
"agent_metadata": {
"products_with_metadata": 20,
"source": "embedded-skill-metadata",
"source_hash": "sha256:b9fd6c4de190c413312561049ed2bbb80b272aad127ba2c0a5082364761012ee",
"source_hash": "sha256:b0788dd79d817258e2fd5869e4b32eff5ab81b6d20f34e7eb131446670402003",
"surface_hash": "sha256:75c63f35d9b15dfb4df09136bfd250906751cfeb7f14bce08cf092097859baad",
"surface_products": 20,
"surface_tools": 538,
@@ -26,6 +26,7 @@
"snapshot_services": 53,
"source_services": 54,
"source_tools": 969,
"surface_scope": "source_revision",
"surface_tools": 504,
"unmatched_tools": 85
},
+62 -9
View File
@@ -37,7 +37,8 @@ type runtimeSchemaExclusionGroup struct {
var embeddedRuntimeSchemaExclusionsJSON []byte
// RuntimeSchemaCompletenessReport compares the public executable Cobra leaves
// with the commands represented by runtime Schema annotations.
// with a reviewed Schema command set, such as runtime annotations or the final
// generated Catalog.
type RuntimeSchemaCompletenessReport struct {
Covered []string
Excluded []string
@@ -100,10 +101,70 @@ func ValidateEmbeddedRuntimeSchemaCompleteness(root *cobra.Command) error {
return nil
}
// ValidateSchemaCatalogDeliveryCompleteness enforces that every public Cobra
// leaf is queryable from the newly built Catalog snapshot (or has an exact,
// reviewed exclusion). It deliberately validates the in-memory build result,
// never the previously embedded Catalog, so stale generated data cannot prove
// its own completeness.
func ValidateSchemaCatalogDeliveryCompleteness(root *cobra.Command, snapshot SchemaCatalogSnapshot) error {
if _, err := ApplyEmbeddedManualSchemaHints(root); err != nil {
return err
}
exclusions, err := EmbeddedRuntimeSchemaExclusions()
if err != nil {
return err
}
report := SchemaCatalogDeliveryCompleteness(root, snapshot, exclusions)
if len(report.Missing) > 0 {
return fmt.Errorf("public Cobra leaves missing from final Schema Catalog or reviewed exclusions: %s", strings.Join(report.Missing, ", "))
}
if len(report.InvalidExclusions) > 0 {
return fmt.Errorf("invalid runtime schema exclusions: %s", strings.Join(report.InvalidExclusions, ", "))
}
if len(report.StaleExclusions) > 0 {
return fmt.Errorf("stale runtime schema exclusions: %s", strings.Join(report.StaleExclusions, ", "))
}
return nil
}
// RuntimeSchemaCompleteness scans the real command tree in the reverse
// direction: every public executable leaf must either carry a Schema identity
// or have a reviewed exclusion with a non-empty reason.
func RuntimeSchemaCompleteness(root *cobra.Command, exclusions []RuntimeSchemaExclusion) RuntimeSchemaCompletenessReport {
coveredPaths := map[string]bool{}
for _, entry := range collectRuntimeSchemaEntries(root) {
addSchemaCoveredPath(coveredPaths, entry.CLIPath)
addSchemaCoveredPath(coveredPaths, entry.PrimaryCLIPath)
for _, alias := range entry.Aliases {
addSchemaCoveredPath(coveredPaths, alias)
}
}
return runtimeSchemaCompletenessAgainstPaths(root, exclusions, coveredPaths)
}
// SchemaCatalogDeliveryCompleteness scans the real command tree against paths
// present in the final generated snapshot. A runtime annotation alone is not
// sufficient: the command must survive the reviewed surface filter and be
// addressable through its primary CLI path or an alias.
func SchemaCatalogDeliveryCompleteness(root *cobra.Command, snapshot SchemaCatalogSnapshot, exclusions []RuntimeSchemaExclusion) RuntimeSchemaCompletenessReport {
coveredPaths := map[string]bool{}
for _, detail := range snapshot.Tools {
addSchemaCoveredPath(coveredPaths, schemaString(detail["cli_path"]))
addSchemaCoveredPath(coveredPaths, schemaString(detail["primary_cli_path"]))
for _, alias := range schemaStringSlice(detail["aliases"]) {
addSchemaCoveredPath(coveredPaths, alias)
}
}
return runtimeSchemaCompletenessAgainstPaths(root, exclusions, coveredPaths)
}
func addSchemaCoveredPath(coveredPaths map[string]bool, rawPath string) {
if path := normalizeSchemaCLIPath(rawPath); path != "" {
coveredPaths[path] = true
}
}
func runtimeSchemaCompletenessAgainstPaths(root *cobra.Command, exclusions []RuntimeSchemaExclusion, coveredPaths map[string]bool) RuntimeSchemaCompletenessReport {
report := RuntimeSchemaCompletenessReport{}
exclusionByPath := make(map[string]RuntimeSchemaExclusion, len(exclusions))
for _, exclusion := range exclusions {
@@ -118,14 +179,6 @@ func RuntimeSchemaCompleteness(root *cobra.Command, exclusions []RuntimeSchemaEx
seenPublic := map[string]bool{}
usedExclusions := map[string]bool{}
coveredPaths := map[string]bool{}
for _, entry := range collectRuntimeSchemaEntries(root) {
coveredPaths[normalizeSchemaCLIPath(entry.CLIPath)] = true
coveredPaths[normalizeSchemaCLIPath(entry.PrimaryCLIPath)] = true
for _, alias := range entry.Aliases {
coveredPaths[normalizeSchemaCLIPath(alias)] = true
}
}
walkPublicRunnableLeaves(root, func(leaf *cobra.Command) {
path := normalizeSchemaCLIPath(strings.Join(commandPathParts(leaf), " "))
if path == "" {
+70
View File
@@ -57,3 +57,73 @@ func TestRuntimeSchemaCompletenessRejectsInvalidAndStaleExclusions(t *testing.T)
t.Fatalf("missing = %v", report.Missing)
}
}
func TestSchemaCatalogDeliveryCompletenessRejectsAnnotatedLeafOutsideSurface(t *testing.T) {
root := &cobra.Command{Use: "dws"}
product := &cobra.Command{Use: "sample"}
covered := &cobra.Command{Use: "covered", Run: func(*cobra.Command, []string) {}}
omitted := &cobra.Command{Use: "omitted", Run: func(*cobra.Command, []string) {}}
AttachRuntimeSchema(covered, "sample", "covered", "test")
AttachRuntimeSchema(omitted, "sample", "omitted", "test")
product.AddCommand(covered, omitted)
root.AddCommand(product)
annotationReport := RuntimeSchemaCompleteness(root, nil)
if !reflect.DeepEqual(annotationReport.Covered, []string{"sample covered", "sample omitted"}) || len(annotationReport.Missing) != 0 {
t.Fatalf("annotation report = %#v", annotationReport)
}
snapshot, err := BuildSchemaCatalogSnapshot(root, SchemaCatalogBuildOptions{
AllowedCanonicalPaths: map[string]bool{"sample.covered": true},
})
if err != nil {
t.Fatalf("BuildSchemaCatalogSnapshot: %v", err)
}
report := SchemaCatalogDeliveryCompleteness(root, snapshot, nil)
if !reflect.DeepEqual(report.Covered, []string{"sample covered"}) {
t.Fatalf("covered = %v", report.Covered)
}
if !reflect.DeepEqual(report.Missing, []string{"sample omitted"}) {
t.Fatalf("missing = %v", report.Missing)
}
}
func TestSchemaCatalogDeliveryCompletenessAcceptsExactReviewedExclusion(t *testing.T) {
root := &cobra.Command{Use: "dws"}
product := &cobra.Command{Use: "sample"}
covered := &cobra.Command{Use: "covered", Run: func(*cobra.Command, []string) {}}
excluded := &cobra.Command{Use: "excluded", Run: func(*cobra.Command, []string) {}}
product.AddCommand(covered, excluded)
root.AddCommand(product)
snapshot := SchemaCatalogSnapshot{Tools: map[string]map[string]any{
"sample.covered": {"primary_cli_path": "sample covered"},
}}
report := SchemaCatalogDeliveryCompleteness(root, snapshot, []RuntimeSchemaExclusion{{
CLIPath: "sample excluded", Reason: "reviewed test exclusion", Reviewed: true,
}})
if !reflect.DeepEqual(report.Covered, []string{"sample covered"}) ||
!reflect.DeepEqual(report.Excluded, []string{"sample excluded"}) ||
len(report.Missing) != 0 || len(report.StaleExclusions) != 0 {
t.Fatalf("delivery report = %#v", report)
}
}
func TestSchemaCatalogDeliveryCompletenessAcceptsDeliveredAlias(t *testing.T) {
root := &cobra.Command{Use: "dws"}
product := &cobra.Command{Use: "sample"}
legacy := &cobra.Command{Use: "legacy", Run: func(*cobra.Command, []string) {}}
product.AddCommand(legacy)
root.AddCommand(product)
snapshot := SchemaCatalogSnapshot{Tools: map[string]map[string]any{
"sample.current": {
"primary_cli_path": "sample current",
"aliases": []string{"sample legacy"},
},
}}
report := SchemaCatalogDeliveryCompleteness(root, snapshot, nil)
if !reflect.DeepEqual(report.Covered, []string{"sample legacy"}) || len(report.Missing) != 0 {
t.Fatalf("delivery report = %#v", report)
}
}
+1
View File
@@ -4,6 +4,7 @@
"source_revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8",
"source_hash": "sha256:17251f74a4f76142457cc0e87251ecb86c1e3bda0cdf2edc02f351589716ecbc",
"coverage": {
"surface_scope": "source_revision",
"source_services": 54,
"snapshot_services": 53,
"missing_services": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"version": 2,
"source_catalog_hash": "sha256:3e93a6610ff04533eff6758bdc71c758ed052f873a8206ccfaf35912706d581f",
"source_catalog_hash": "sha256:a2ad77f7d8c0816a0e553a5ee5306c6ca0f2139edb83207c35eefecd2411f72f",
"historical_binding_count": 311,
"migrations": {
"contact.get_dept_info_by_dept_id --id": "contact.get_dept_info_by_dept_id --dept",
@@ -67,6 +67,9 @@ func main() {
if err != nil {
fail(err)
}
if err := cli.ValidateSchemaCatalogDeliveryCompleteness(root, snapshot); err != nil {
fail(fmt.Errorf("validate final Catalog delivery completeness: %w", err))
}
encoded, err := json.MarshalIndent(snapshot, "", " ")
if err != nil {
fail(fmt.Errorf("encode catalog: %w", err))
+40 -4
View File
@@ -18,7 +18,7 @@ fi
surface_count="$(wc -l <"$tmp/surface" | tr -d ' ')"
catalog_count="$(jq -r '.tools | length' internal/cli/schema_catalog.json)"
catalog_product_count="$(jq -r '.catalog.count' internal/cli/schema_catalog.json)"
interface_surface_count="$(jq -r '.coverage.surface_tools' internal/cli/schema_mcp_metadata.json)"
mcp_snapshot_surface_count="$(jq -r '.coverage.surface_tools' internal/cli/schema_mcp_metadata.json)"
agent_surface_count="$(jq -r '.coverage.surface_tools' internal/cli/schema_agent_metadata/index.json)"
agent_product_count="$(jq -r '.coverage.products_with_metadata' internal/cli/schema_agent_metadata/index.json)"
agent_selection_count="$(jq -r '[.coverage.tools_with_use_when, .coverage.tools_with_avoid_when, .coverage.tools_with_examples, .coverage.tools_with_interface_mode] | min' internal/cli/schema_agent_metadata/index.json)"
@@ -26,9 +26,43 @@ if [ "$catalog_count" != "$surface_count" ] ||
[ "$agent_surface_count" != "$surface_count" ] ||
[ "$agent_product_count" != "$catalog_product_count" ] ||
[ "$agent_selection_count" != "$surface_count" ]; then
printf 'schema counts disagree: surface=%s catalog=%s products=%s interface=%s agent=%s/%s\n' \
printf 'current schema counts disagree: surface=%s catalog=%s products=%s agent=%s/%s\n' \
"$surface_count" "$catalog_count" "$catalog_product_count" \
"$interface_surface_count" "$agent_product_count" "$agent_surface_count" >&2
"$agent_product_count" "$agent_surface_count" >&2
exit 1
fi
if ! jq -e '
.version == 1 and
((.source_revision // "") | length) > 0 and
.coverage.surface_scope == "source_revision" and
.coverage.source_services == (.coverage.snapshot_services + (.coverage.missing_services | length)) and
.coverage.surface_tools == (.coverage.matched_tools + .coverage.unmatched_tools) and
.coverage.source_tools >= .coverage.surface_tools and
.coverage.matched_tools == (.tools | length) and
.coverage.aliased_tools <= .coverage.matched_tools
' internal/cli/schema_mcp_metadata.json >/dev/null; then
printf 'MCP source-revision snapshot coverage is inconsistent: snapshot_surface=%s\n' \
"$mcp_snapshot_surface_count" >&2
exit 1
fi
if ! jq -e '
.coverage.source_tools == (.tools | length) and
.coverage.eligible_tools == (.tools | length) and
.coverage.matched_tools == (.tools | length) and
.coverage.unmatched_tools == 0
' skills/mono/schema-hints/selection-review.json >/dev/null; then
printf '%s\n' 'reviewed Agent selection coverage is stale' >&2
exit 1
fi
if ! jq -e --arg surface_count "$surface_count" '
.coverage.source_tools == ($surface_count | tonumber) and
.coverage.matched_tools == (.tools | length) and
all(.tools[]; ((.interface_mode // "") | length) > 0)
' skills/mono/schema-hints/runtime-surface-completeness.json >/dev/null; then
printf '%s\n' 'runtime-surface interface disposition coverage is stale' >&2
exit 1
fi
@@ -61,6 +95,8 @@ if ! jq -e --arg surface_count "$surface_count" '
(if .interface_mode == "mcp" then .interface_ref != null and .availability == "available" else true end) and
(if .interface_mode == "composite" or .interface_mode == "local" then .interface_ref == null and .availability == "available" and ((.interface_reason // "") | length) > 0 else true end) and
(if .interface_mode == "unavailable" then .interface_ref == null and .availability == "unavailable" and ((.interface_reason // "") | length) > 0 else true end) and
((((.agent_source_refs // []) | index("skills/mono/schema-hints/selection-review.json")) != null) or
(((.agent_source_refs // []) | index("skills/mono/schema-hints/runtime-surface-completeness.json")) != null)) and
(if .effect == "destructive" then .risk == "high" and .confirmation == "user_required" else true end) and
(if .risk == "high" then .confirmation == "user_required" else true end)
)
@@ -162,7 +198,7 @@ if rg -n '\.ListTools\(' internal/app internal/cli --glob '*.go'; then
fi
go test ./internal/cli \
-run '^(TestEmbeddedSchemaCatalog.*|TestSchemaUsesEmbeddedCatalogWithoutRuntimeLoad|TestWalkLeafCommandsTraversesAnnotatedHiddenSubtree)$' \
-run '^(TestEmbeddedSchemaCatalog.*|TestSchemaCatalogDeliveryCompleteness.*|TestSchemaUsesEmbeddedCatalogWithoutRuntimeLoad|TestWalkLeafCommandsTraversesAnnotatedHiddenSubtree)$' \
-count=1
go test ./internal/app \
-run '^(TestEmbeddedSchemaContractMapsToExecutableTree|TestRuntimeSchemaCompletenessCoversPublicCommandTree|TestRegisterPluginHTTPServerDoesNotProbeEndpoint|TestRegisterStdioServerFromManifestDoesNotStartProcess)$' \
+10
View File
@@ -52,6 +52,16 @@ The missing `notify` MCP service is separately dispositioned in
`internal/cli/schema_mcp_service_review.json`; it is outside the public command
surface and must not trigger runtime discovery.
`internal/cli/schema_mcp_metadata.json.coverage.surface_tools` describes only
the immutable MCP import at its declared `source_revision`; it is not the
current CLI/Catalog tool count. Its `coverage.surface_scope` must remain
`source_revision`, and policy verifies the snapshot's internal matched and
unmatched arithmetic. Current Catalog interface coverage is instead proved for
every generated tool: each tool must have one valid `interface_mode` /
availability disposition and retain provenance to `selection-review.json` or
`runtime-surface-completeness.json`. This makes newly added CLI tools explicit
without rewriting historical MCP evidence.
Interface metadata may enrich type and description, but MCP `required` never promotes an optional Cobra flag. CLI required/one-of/conditional rules must be represented by current Cobra markers, typed runtime constraints, or reviewed parameter hints.
```json
@@ -7,9 +7,9 @@
"channel": "open-source"
},
"coverage": {
"source_tools": 504,
"eligible_tools": 504,
"matched_tools": 504,
"source_tools": 505,
"eligible_tools": 505,
"matched_tools": 505,
"unmatched_tools": 0
},
"tools": {