diff --git a/AGENTS.md b/AGENTS.md index 3ae5d065..d69e7499 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -184,9 +184,9 @@ that works through `dws ` but cannot be found through the matching `dws schema` lookup is a contract failure unless it has a reviewed exact exclusion. -Production `RegisterSchemaHints` maps must stay empty after ParamDecl -migration. Temporary hint injection is allowed only inside unit-test fixtures -that exercise precedence edges. +`RegisterSchemaHints` / `ToolSchemaHint` overlays are fully removed. Parameter +and selection facts must be declared on the owning leaf (`ParamDecl` / +`Schema`) or via `ProductDecl`; do not reintroduce overlay registries. For Agent-authored selection edits: @@ -222,8 +222,8 @@ Human-authored inputs: Contract/Safety (or remaining `runtime_gate` annotate) requires a user gate (for example `confirm_delete`, `typed_yes`, `confirm_dangerous`). 3. **Parameter facts** are declared on the leaf (`ParamDecl` / - `Schema.Parameters` / FlagSpec). Do not add production - `RegisterSchemaHints` overlays or metadata tool parameter rows. + `Schema.Parameters` / FlagSpec). Do not reintroduce HintFile or + `RegisterSchemaHints` overlays. ### Authoring @@ -364,14 +364,15 @@ Preserve all candidates and the selected source in provenance, and fail same-precedence conflicts rather than silently merging them. `required` is the exception. Cobra `MarkFlagRequired` is a hard floor: the -final Agent projection must keep `required=true` and cannot be lowered by -manual/hint overlays. Overlays may still raise an optional flag to required. -`cli_required` continues to mirror the executable Cobra marker. +final Agent projection must keep `required=true` and cannot be lowered by a +lower-precedence source. A higher-precedence declaration may still raise an +optional flag to required. `cli_required` continues to mirror the executable +Cobra marker. -For command text, reviewed `ToolSchemaHint` wins first, then command-specific -Cobra Help, then MCP metadata. Generic RPC prose may remain an unselected -provenance candidate (and parameter-level `interface_description`); it must not -overwrite a specialized leaf's title or description. +For command text, ContractFinal / native declaration wins first, then +command-specific Cobra Help, then MCP metadata. Generic RPC prose may remain an +unselected provenance candidate (and parameter-level `interface_description`); +it must not overwrite a specialized leaf's title or description. For every delivered `ToolSpec` and `ParameterSpec` field, the provenance winner value must exactly equal the delivered value. Checking only source, diff --git a/docs/schema-dynamic-endpoint-design.md b/docs/schema-dynamic-endpoint-design.md index eac7cb4a..ba36e99b 100644 --- a/docs/schema-dynamic-endpoint-design.md +++ b/docs/schema-dynamic-endpoint-design.md @@ -203,15 +203,15 @@ JSON 输入契约、禁用旧 native materialization 符号,并从 Registry 实现中的参数字段顺序固定为: ```text -reviewed manual > versioned binding > command constraint > typed metadata - > native/Cobra contract > ToolSchemaHint > MCP metadata - > inference/default +versioned binding > command constraint > typed metadata + > native/Cobra contract (ParamDecl / ContractFinal) + > MCP metadata > inference/default ``` 命令 `title` / `description` 使用独立但同样确定的文本顺序: ```text -reviewed ToolSchemaHint > command-specific Cobra Help > MCP metadata > inference +ContractFinal / native declaration > command-specific Cobra Help > MCP metadata > inference ``` 因此多个 CLI leaf 复用同一个 RPC 时,通用 RPC 文案只能作为未选中的 diff --git a/internal/app/schema_agent_examples_dry_run_test.go b/internal/app/schema_agent_examples_dry_run_test.go index b59e93dc..ec7e1bc2 100644 --- a/internal/app/schema_agent_examples_dry_run_test.go +++ b/internal/app/schema_agent_examples_dry_run_test.go @@ -152,14 +152,7 @@ func manualAgentExampleShouldExerciseDryRun(execution cli.ManualAgentExampleExec func manualAgentExampleExecutionPlan(t testing.TB) cli.ManualAgentExampleExecutionPlan { t.Helper() - hints, err := cli.LoadAgentHintsFromSelectionForValidation(nil) - if err != nil { - t.Fatalf("LoadAgentHintsFromSelectionForValidation() error = %v", err) - } contractRoot := NewRootCommand() - if _, err := cli.ApplyEmbeddedManualSchemaHints(contractRoot); err != nil { - t.Fatalf("ApplyEmbeddedManualSchemaHints() error = %v", err) - } effective, err := cli.BuildEffectiveCommandRegistry(contractRoot) if err != nil { t.Fatalf("BuildEffectiveCommandRegistry() error = %v", err) @@ -175,7 +168,7 @@ func manualAgentExampleExecutionPlan(t testing.TB) cli.ManualAgentExampleExecuti if err := cli.ValidateReviewedDryRunCapabilityDelivery(registry); err != nil { t.Fatalf("ValidateReviewedDryRunCapabilityDelivery() error = %v", err) } - plan, err := cli.BuildManualAgentExampleExecutionPlan(bound, registry, hints) + plan, err := cli.BuildAgentExampleExecutionPlan(bound, registry) if err != nil { t.Fatalf("BuildManualAgentExampleExecutionPlan() error = %v", err) } diff --git a/internal/app/schema_agent_selection_contract_test.go b/internal/app/schema_agent_selection_contract_test.go index 268f70e8..b57f866d 100644 --- a/internal/app/schema_agent_selection_contract_test.go +++ b/internal/app/schema_agent_selection_contract_test.go @@ -29,12 +29,7 @@ func TestManualAgentSelectionScenariosCoverEveryExecutableSchemaTool(t *testing. if err != nil { t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) } - hints, err := cli.LoadAgentHintsFromSelectionForValidation(nil) - if err != nil { - t.Fatalf("LoadAgentHintsFromSelectionForValidation() error = %v", err) - } - - fixture, report, err := cli.BuildManualAgentSelectionEvalFixture(bound, hints) + fixture, report, err := cli.BuildAgentSelectionEvalFixture(bound) if err != nil { t.Fatalf("BuildManualAgentSelectionEvalFixture() error = %v", err) } diff --git a/internal/app/schema_agent_selection_live_test.go b/internal/app/schema_agent_selection_live_test.go index ef4bc9c8..b10d1f22 100644 --- a/internal/app/schema_agent_selection_live_test.go +++ b/internal/app/schema_agent_selection_live_test.go @@ -133,15 +133,11 @@ func manualAgentSelectionLiveFixture(t testing.TB) (cli.ManualAgentSelectionFixt if err != nil { t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) } - hints, err := cli.LoadAgentHintsFromSelectionForValidation(nil) + fixture, _, err := cli.BuildAgentSelectionEvalFixture(bound) if err != nil { - t.Fatalf("LoadAgentHintsFromSelectionForValidation() error = %v", err) + t.Fatalf("BuildAgentSelectionEvalFixture() error = %v", err) } - fixture, _, err := cli.BuildManualAgentSelectionEvalFixture(bound, hints) - if err != nil { - t.Fatalf("BuildManualAgentSelectionEvalFixture() error = %v", err) - } - return fixture, hints + return fixture, cli.ManualAgentHintSet{} } func selectManualAgentSelectionLiveCases(t testing.TB, cases []cli.ManualAgentSelectionCase) []cli.ManualAgentSelectionCase { diff --git a/internal/app/schema_contract_test.go b/internal/app/schema_contract_test.go index 70542b35..5fc8623f 100644 --- a/internal/app/schema_contract_test.go +++ b/internal/app/schema_contract_test.go @@ -370,9 +370,6 @@ func schemaContractStringSlice(value any) []string { // delivered set must always equal the public EffectiveCommandRegistry set. func schemaContractPayloadForBoundCanonicals(t *testing.T, root *cobra.Command, canonicals ...string) cli.SchemaSnapshotPayload { t.Helper() - if _, err := cli.ApplyEmbeddedManualSchemaHints(root); err != nil { - t.Fatalf("apply manual Schema hints: %v", err) - } effective, err := cli.BuildEffectiveCommandRegistry(root) if err != nil { t.Fatalf("build effective CommandRegistry: %v", err) diff --git a/internal/cli/api_coverage_more_test.go b/internal/cli/api_coverage_more_test.go index 0e57d498..ca78a362 100644 --- a/internal/cli/api_coverage_more_test.go +++ b/internal/cli/api_coverage_more_test.go @@ -62,7 +62,7 @@ func TestCrossPlatformCoverageRuntimeAnnotationAPIsCoverage(t *testing.T) { } } -func TestCrossPlatformCoverageRuntimeRegistriesAndSchemaHintsCoverage(t *testing.T) { +func TestCrossPlatformCoverageRuntimeRegistriesCoverage(t *testing.T) { originalConstraints := runtimeSchemaConstraintsByCanonical originalParameters := runtimeSchemaParameterMetadataByCanonical t.Cleanup(func() { @@ -96,21 +96,6 @@ func TestCrossPlatformCoverageRuntimeRegistriesAndSchemaHintsCoverage(t *testing t.Fatal("cloneRuntimeSchemaStringMap(nil) != nil") } assertPanics(t, func() { RegisterRuntimeSchemaParameterMetadata("sample.run", metadata) }) - - registry := newSchemaHintRegistry() - assertPanics(t, func() { registry.RegisterProduct("", nil) }) - assertPanics(t, func() { registry.RegisterProduct("sample", map[string]ToolSchemaHint{"": {}}) }) - registry.RegisterProduct(" sample ", map[string]ToolSchemaHint{"run": {Title: "Run"}, "other.path": {Title: "Other"}}) - assertPanics(t, func() { registry.RegisterProduct("sample", map[string]ToolSchemaHint{"run": {}}) }) - if _, ok := registry.Lookup(" "); ok { - t.Fatal("Lookup(empty) succeeded") - } - if hint, ok := registry.Lookup(" sample.run "); !ok || hint.Title != "Run" { - t.Fatalf("Lookup(sample.run) = %#v, %v", hint, ok) - } - if canonicalHintPath("sample", "") != "" || canonicalHintPath("sample", "other.path") != "other.path" || canonicalHintPath("sample", "run") != "sample.run" { - t.Fatal("canonicalHintPath mismatch") - } } func assertPanics(t *testing.T, fn func()) { diff --git a/internal/cli/contract_final_test.go b/internal/cli/contract_final_test.go index 9a81a256..d9f3883c 100644 --- a/internal/cli/contract_final_test.go +++ b/internal/cli/contract_final_test.go @@ -321,7 +321,7 @@ func TestCrossPlatformCoverageRuntimeToolSpecFromContractFinalSafetyAnnotationFa func TestCrossPlatformCoverageRuntimeToolSpecFromContractFinalParameterResolutionError(t *testing.T) { oldParameters := resolveRuntimeParameters t.Cleanup(func() { resolveRuntimeParameters = oldParameters }) - resolveRuntimeParameters = func(*cobra.Command, string, map[string]ParameterSchemaHint, map[string]embeddedMCPParamMeta, RuntimeSchemaConstraints) ([]ParameterSpec, error) { + resolveRuntimeParameters = func(*cobra.Command, string, map[string]embeddedMCPParamMeta, RuntimeSchemaConstraints) ([]ParameterSpec, error) { return nil, errors.New("parameters failed") } entry := runtimeSchemaEntry{ diff --git a/internal/cli/param_aliases.go b/internal/cli/param_aliases.go index d98e7ac6..930a3762 100644 --- a/internal/cli/param_aliases.go +++ b/internal/cli/param_aliases.go @@ -159,7 +159,7 @@ type realFlag struct { // morphed name to the real flags that share that morph. func realFlagsByMorph(leaf *cobra.Command) map[string][]realFlag { byMorph := make(map[string][]realFlag) - visitManualAgentCommandFlags(leaf, func(flag *pflag.Flag) { + visitAgentExampleCommandFlags(leaf, func(flag *pflag.Flag) { if flag == nil || flag.Name == "help" { return } diff --git a/internal/cli/runtime_schema.go b/internal/cli/runtime_schema.go index 3a23a5a1..0a01e118 100644 --- a/internal/cli/runtime_schema.go +++ b/internal/cli/runtime_schema.go @@ -652,16 +652,6 @@ func collectRuntimeSchemaEntriesFromBound(bound BoundCommandRegistry) ([]runtime return entries, nil } -func runtimeSchemaHintForEntry(entry runtimeSchemaEntry) ToolSchemaHint { - if hint := schemaHintForCanonicalPath(entry.ProductID + "." + entry.ToolName); !isZeroToolSchemaHint(hint) { - return hint - } - if entry.SourceProductID != "" && entry.SourceProductID != entry.ProductID { - return schemaHintForCanonicalPath(entry.SourceProductID + "." + entry.ToolName) - } - return ToolSchemaHint{} -} - func embeddedMCPMetadataForEntryFrom(entry runtimeSchemaEntry, agentMetadata embeddedAgentMetadata, mcpMetadata embeddedMCPMetadata) (embeddedMCPToolMetadata, bool) { paths := []string{ entry.PrimaryCLIPath, @@ -693,12 +683,6 @@ func embeddedMCPMetadataForEntryFrom(entry runtimeSchemaEntry, agentMetadata emb return embeddedMCPToolMetadata{}, false } -func isZeroToolSchemaHint(hint ToolSchemaHint) bool { - return strings.TrimSpace(hint.Title) == "" && - strings.TrimSpace(hint.Description) == "" && - len(hint.Parameters) == 0 -} - func runtimeSchemaAnnotations(cmd *cobra.Command) (productID, toolName, source string) { if cmd == nil || cmd.Annotations == nil { return "", "", "" @@ -801,9 +785,6 @@ func runtimeToolTextMetadataFromMetadata(entry runtimeSchemaEntry, metadata runt titleCandidates = append(titleCandidates, runtimeSchemaStringCandidate(embeddedMeta.Title, "mcp_metadata")) descriptionCandidates = append(descriptionCandidates, runtimeSchemaStringCandidate(embeddedMeta.Description, "mcp_metadata")) } - hint := runtimeSchemaHintForEntry(entry) - titleCandidates = append(titleCandidates, runtimeSchemaStringCandidate(hint.Title, "tool_schema_hint")) - descriptionCandidates = append(descriptionCandidates, runtimeSchemaStringCandidate(hint.Description, "tool_schema_hint")) titleWinner, err := resolveRuntimeSchemaField("title", titleCandidates...) if err != nil { return "", "", "", nil, err @@ -1144,7 +1125,7 @@ func runtimeSchemaParameterMappingCandidates(snapshot schemaParameterBindingSnap // source may intentionally raise or lower type/mapping/description semantics. // required is different: Cobra MarkFlagRequired is a hard floor that cannot be // lowered by manual/hint overlays (see resolveRequiredProjection). -func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hints map[string]ParameterSchemaHint, embeddedParams map[string]embeddedMCPParamMeta, constraints RuntimeSchemaConstraints) ([]ParameterSpec, error) { +func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, embeddedParams map[string]embeddedMCPParamMeta, constraints RuntimeSchemaConstraints) ([]ParameterSpec, error) { if cmd == nil { return nil, nil } @@ -1160,15 +1141,6 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint if resolveErr != nil || flag == nil || flag.Hidden || flag.Name == "help" || isGenericPayloadFlag(flag) { return } - manual, manualReason, _, err := runtimeManualSchemaParameter(cmd, flag.Name) - if err != nil { - resolveErr = fmt.Errorf("flag --%s: %w", flag.Name, err) - return - } - manualProperty := runtimeSchemaFieldCandidate{} - if manual.Property != nil { - manualProperty = runtimeSchemaManualCandidate(*manual.Property, true, manualReason) - } bindingProperty, excludedProperty, err := runtimeSchemaParameterMappingCandidates(bindingSnapshot, canonicalPath, flag.Name) if err != nil { resolveErr = fmt.Errorf("flag --%s: %w", flag.Name, err) @@ -1176,7 +1148,6 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint } propertyWinner, err := resolveRuntimeSchemaField("property", excludedProperty, - manualProperty, bindingProperty, runtimeSchemaStringCandidate(firstFlagAnnotation(flag, runtimeSchemaFlagBindingPropertyAnnotation), "versioned_parameter_binding"), runtimeSchemaStringCandidate(firstFlagAnnotation(flag, runtimeSchemaFlagPropertyAnnotation), "native_annotation"), @@ -1187,29 +1158,15 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint return } property, _ := propertyWinner.Value.(string) - hint, _, hasHint := lookupParameterSchemaHint(hints, property, flag.Name) embeddedParam, hasEmbeddedParam := embeddedMCPParamMeta{}, false if strings.TrimSpace(property) != "" { embeddedParam, hasEmbeddedParam = lookupEmbeddedMCPParam(embeddedParams, property, flag.Name) } flagName := flag.Name - if hasHint && strings.TrimSpace(hint.FlagName) != "" { - hintFlagName := strings.TrimSpace(hint.FlagName) - if hintFlagName != flag.Name { - resolveErr = fmt.Errorf("flag --%s: tool_schema_hint flag_name %q does not identify the existing Cobra flag", flag.Name, hintFlagName) - return - } - } paramType := runtimeFlagCLIType(flag) - manualInterfaceType := runtimeSchemaFieldCandidate{} - if manual.InterfaceType != nil { - manualInterfaceType = runtimeSchemaManualCandidate(*manual.InterfaceType, true, manualReason) - } interfaceTypeWinner, err := resolveRuntimeSchemaField("interface_type", - manualInterfaceType, runtimeSchemaStringCandidate(firstFlagAnnotation(flag, runtimeSchemaFlagTypeAnnotation), "native_annotation"), - runtimeSchemaStringCandidate(hint.Type, "tool_schema_hint"), runtimeSchemaStringCandidate(embeddedParam.Type, "mcp_metadata"), runtimeSchemaStringCandidateAtRank(paramType, "cobra_flag_type", runtimeSchemaRankInference, "fallback"), ) @@ -1219,15 +1176,9 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint } interfaceType, _ := interfaceTypeWinner.Value.(string) - manualDescription := runtimeSchemaFieldCandidate{} - if manual.Description != nil { - manualDescription = runtimeSchemaManualCandidate(*manual.Description, true, manualReason) - } descriptionWinner, err := resolveRuntimeSchemaField("description", - manualDescription, runtimeSchemaStringCandidate(firstFlagAnnotation(flag, runtimeSchemaFlagDescriptionAnnotation), "native_annotation"), runtimeSchemaStringCandidate(flag.Usage, "cobra_usage"), - runtimeSchemaStringCandidate(hint.Description, "tool_schema_hint"), runtimeSchemaStringCandidate(embeddedParam.Description, "mcp_metadata"), runtimeSchemaCandidate("", true, "default"), ) @@ -1241,16 +1192,8 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint interfaceDescription = strings.TrimSpace(embeddedParam.Description) } - // Required uses field-level safe merge: overlays may raise required, but + // Required uses field-level safe merge: higher sources may raise required, but // Cobra MarkFlagRequired cannot be projected away as optional. - manualRequired := runtimeSchemaFieldCandidate{} - if manual.Required != nil { - manualRequired = runtimeSchemaManualCandidate(*manual.Required, true, manualReason) - } - hintRequired := runtimeSchemaFieldCandidate{} - if hasHint && hint.Required != nil { - hintRequired = runtimeSchemaCandidate(*hint.Required, true, "tool_schema_hint") - } constraintRequired := runtimeSchemaFieldCandidate{} if runtimeSchemaRequireOneOfContains(constraints, flag.Name, flagName, property) { constraintRequired = runtimeSchemaCandidate(false, true, "require_one_of_constraint") @@ -1270,7 +1213,6 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint } cobraHardRequired := runtimeFlagCobraHardRequired(flag) requiredWinner, err := resolveRequiredProjection(cobraHardRequired, - manualRequired, constraintRequired, runtimeSchemaCandidate(true, typedRequired, "typed_parameter_metadata"), runtimeSchemaAnnotatedBoolCandidate(flag, runtimeSchemaFlagMetadataRequiredAnnotation, "typed_parameter_metadata"), @@ -1278,7 +1220,6 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint runtimeSchemaCandidate(true, cobraHardRequired, "cobra_hard_required"), runtimeSchemaCandidate(false, cobraDefaultOptional, "cobra_nonzero_default"), runtimeSchemaCandidate(usageRequired, usageRequired, "usage_required_inference"), - hintRequired, mcpRequired, runtimeSchemaCandidate(false, true, "default"), ) @@ -1324,16 +1265,10 @@ func runtimeCommandParameterSpecs(cmd *cobra.Command, canonicalPath string, hint parameter.InterfaceType = interfaceType fieldProvenance["interface_type"] = runtimeSchemaFieldProvenance(interfaceTypeWinner) } - manualRequiredWhen := runtimeSchemaFieldCandidate{} - if manual.RequiredWhen != nil { - manualRequiredWhen = runtimeSchemaManualCandidate(*manual.RequiredWhen, true, manualReason) - } requiredWhenWinner, err := resolveRuntimeSchemaField("required_when", - manualRequiredWhen, runtimeSchemaStringCandidate(metadata.RequiredWhen[flag.Name], "typed_parameter_metadata"), runtimeSchemaStringCandidate(firstFlagAnnotation(flag, runtimeSchemaFlagMetadataRequiredWhenAnnotation), "typed_parameter_metadata"), runtimeSchemaStringCandidate(firstFlagAnnotation(flag, runtimeSchemaFlagRequiredWhenAnnotation), "native_annotation"), - runtimeSchemaStringCandidate(hint.RequiredWhen, "tool_schema_hint"), runtimeSchemaStringCandidate(embeddedParam.RequiredWhen, "mcp_metadata"), runtimeSchemaCandidate("", true, "default"), ) @@ -1423,8 +1358,8 @@ func runtimeSchemaJSONString(value string) json.RawMessage { // re-interpreting any source. var runtimeCommandParameterSpecsForPayload = runtimeCommandParameterSpecs -func runtimeCommandParameters(cmd *cobra.Command, canonicalPath string, hints map[string]ParameterSchemaHint, embeddedParams map[string]embeddedMCPParamMeta, constraints RuntimeSchemaConstraints) (map[string]any, error) { - specs, err := runtimeCommandParameterSpecsForPayload(cmd, canonicalPath, hints, embeddedParams, constraints) +func runtimeCommandParameters(cmd *cobra.Command, canonicalPath string, embeddedParams map[string]embeddedMCPParamMeta, constraints RuntimeSchemaConstraints) (map[string]any, error) { + specs, err := runtimeCommandParameterSpecsForPayload(cmd, canonicalPath, embeddedParams, constraints) if err != nil { return nil, err } diff --git a/internal/cli/runtime_schema_coverage_more_test.go b/internal/cli/runtime_schema_coverage_more_test.go index c856142e..2842da45 100644 --- a/internal/cli/runtime_schema_coverage_more_test.go +++ b/internal/cli/runtime_schema_coverage_more_test.go @@ -48,11 +48,9 @@ func TestCrossPlatformCoverageRuntimeSchemaLoaderAndAnnotationEdges(t *testing.T func TestCrossPlatformCoverageCollectRuntimeSchemaEntriesErrorsAndOrdering(t *testing.T) { originalValidate := validateReviewedParameterBindings originalRegistry := loadReviewedCommandRegistry - originalManual := loadReviewedManualSchemaHints t.Cleanup(func() { validateReviewedParameterBindings = originalValidate loadReviewedCommandRegistry = originalRegistry - loadReviewedManualSchemaHints = originalManual }) validateReviewedParameterBindings = func() error { return errors.New("bindings failed") } @@ -67,9 +65,6 @@ func TestCrossPlatformCoverageCollectRuntimeSchemaEntriesErrorsAndOrdering(t *te Visibility: SchemaVisibilityPublic, Source: "reviewed_registry", ReviewReason: "test binding failure", }}}, nil } - loadReviewedManualSchemaHints = func() (ManualSchemaHintSnapshot, error) { - return ManualSchemaHintSnapshot{Schema: manualSchemaHintSchemaRef, Version: manualSchemaHintVersion}, nil - } if _, err := collectRuntimeSchemaEntries(&cobra.Command{Use: "dws"}); err == nil { t.Fatal("missing Cobra path should fail binding") } @@ -90,17 +85,6 @@ func TestCrossPlatformCoverageCollectRuntimeSchemaEntriesErrorsAndOrdering(t *te } func TestCrossPlatformCoverageRuntimeSchemaMetadataLookupEdges(t *testing.T) { - // Fixture-only hint injection: production RegisterSchemaHints stays empty. - originalHints := defaultSchemaHintRegistry - t.Cleanup(func() { defaultSchemaHintRegistry = originalHints }) - defaultSchemaHintRegistry = newSchemaHintRegistry() - defaultSchemaHintRegistry.RegisterProduct("source", map[string]ToolSchemaHint{ - "run": {Title: "source title"}, - }) - hint := runtimeSchemaHintForEntry(runtimeSchemaEntry{ProductID: "target", SourceProductID: "source", ToolName: "run"}) - if hint.Title != "source title" { - t.Fatalf("source product hint = %#v", hint) - } if _, ok := embeddedMCPMetadataForEntryFrom(runtimeSchemaEntry{}, embeddedAgentMetadata{}, embeddedMCPMetadata{Tools: map[string]embeddedMCPToolMetadata{}}); ok { t.Fatal("empty lookup unexpectedly matched") } @@ -195,13 +179,13 @@ func TestCrossPlatformCoverageRuntimeCommandParameterErrorEdges(t *testing.T) { runtimeCommandParameterSpecsForPayload = originalPayloadSpecs }) - if specs, err := runtimeCommandParameterSpecs(nil, "sample.run", nil, nil, RuntimeSchemaConstraints{}); err != nil || specs != nil { + if specs, err := runtimeCommandParameterSpecs(nil, "sample.run", nil, RuntimeSchemaConstraints{}); err != nil || specs != nil { t.Fatalf("nil command specs = %#v, err = %v", specs, err) } schemaParameterBindingData = func() (schemaParameterBindingSnapshot, error) { return schemaParameterBindingSnapshot{}, errors.New("load failed") } - if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "load failed") { + if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "load failed") { t.Fatalf("binding load error = %v", err) } schemaParameterBindingData = func() (schemaParameterBindingSnapshot, error) { @@ -219,21 +203,13 @@ func TestCrossPlatformCoverageRuntimeCommandParameterErrorEdges(t *testing.T) { MappingExclusions: map[string]string{"sample.run --value": " "}, }, nil } - if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "mapping exclusion") { + if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "mapping exclusion") { t.Fatalf("mapping exclusion error = %v", err) } schemaParameterBindingData = func() (schemaParameterBindingSnapshot, error) { return schemaParameterBindingSnapshot{Bindings: map[string]map[string]string{}, MappingExclusions: map[string]string{}}, nil } - cmd.Annotations = map[string]string{ - runtimeManualSchemaParameterKey(runtimeSchemaManualParameterAnnotation, "value"): "{", - } - if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "reviewed manual") { - t.Fatalf("manual hint error = %v", err) - } - cmd.Annotations = nil - for _, target := range []string{"property", "interface_type", "description", "required", "required_when", "format", "enum", "example"} { resolveRuntimeSchemaField = func(field string, candidates ...runtimeSchemaFieldCandidate) (runtimeSchemaFieldCandidate, error) { if field == target { @@ -241,26 +217,22 @@ func TestCrossPlatformCoverageRuntimeCommandParameterErrorEdges(t *testing.T) { } return resolveRuntimeSchemaCandidate(field, candidates...) } - if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), target) { + if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), target) { t.Fatalf("%s resolution error = %v", target, err) } } resolveRuntimeSchemaField = originalResolver - badHint := map[string]ParameterSchemaHint{"value": {FlagName: "other"}} - if _, err := runtimeCommandParameterSpecs(cmd, "sample.run", badHint, nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "does not identify") { - t.Fatalf("hint flag mismatch error = %v", err) - } - if specs, err := runtimeCommandParameterSpecs(&cobra.Command{Use: "empty"}, "sample.empty", nil, nil, RuntimeSchemaConstraints{}); err != nil || specs != nil { + if specs, err := runtimeCommandParameterSpecs(&cobra.Command{Use: "empty"}, "sample.empty", nil, RuntimeSchemaConstraints{}); err != nil || specs != nil { t.Fatalf("empty specs = %#v, err = %v", specs, err) } - if payload, err := runtimeCommandParameters(nil, "", nil, nil, RuntimeSchemaConstraints{}); err != nil || payload != nil { + if payload, err := runtimeCommandParameters(nil, "", nil, RuntimeSchemaConstraints{}); err != nil || payload != nil { t.Fatalf("empty payload = %#v, err = %v", payload, err) } - runtimeCommandParameterSpecsForPayload = func(*cobra.Command, string, map[string]ParameterSchemaHint, map[string]embeddedMCPParamMeta, RuntimeSchemaConstraints) ([]ParameterSpec, error) { + runtimeCommandParameterSpecsForPayload = func(*cobra.Command, string, map[string]embeddedMCPParamMeta, RuntimeSchemaConstraints) ([]ParameterSpec, error) { return []ParameterSpec{{Name: "bad", Example: json.RawMessage("{")}}, nil } - if _, err := runtimeCommandParameters(cmd, "sample.run", nil, nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "serialize Schema parameter") { + if _, err := runtimeCommandParameters(cmd, "sample.run", nil, RuntimeSchemaConstraints{}); err == nil || !strings.Contains(err.Error(), "serialize Schema parameter") { t.Fatalf("payload serialization error = %v", err) } diff --git a/internal/cli/schema_agent_examples.go b/internal/cli/schema_agent_examples.go new file mode 100644 index 00000000..51889942 --- /dev/null +++ b/internal/cli/schema_agent_examples.go @@ -0,0 +1,698 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "fmt" + "sort" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// AgentExampleMode controls only how an already contract-validated example is +// exercised. Contract validation is the default. dry_run is used only when the +// final ToolSpec publishes an explicit reviewed capability; contract_only +// remains a precise reviewed exception for such a capability whose runtime +// preconditions cannot be exercised safely and deterministically in the +// isolated test process. +type AgentExampleMode = ManualAgentExampleMode + +// ManualAgentExampleMode is retained as the historical name for AgentExampleMode +// so dry-run tests and policy scripts keep stable identifiers. +type ManualAgentExampleMode string + +const ( + ManualAgentExampleModeContract ManualAgentExampleMode = "contract" + ManualAgentExampleModeDryRun ManualAgentExampleMode = "dry_run" + ManualAgentExampleModeContractOnly ManualAgentExampleMode = "contract_only" +) + +// ManualAgentExampleReasonCode is a closed taxonomy for reviewed contract-only +// exceptions to an explicit dry-run capability. +type ManualAgentExampleReasonCode string + +const ( + ManualAgentExampleReasonLocalState ManualAgentExampleReasonCode = "local_state" + ManualAgentExampleReasonStatefulPreflight ManualAgentExampleReasonCode = "stateful_preflight" +) + +// ManualAgentExampleDisposition narrows one exact example with an explicit +// typed dry-run capability to contract-only. Index is a pointer so a missing +// field cannot silently select example zero. +// +// Dispositions are no longer authored via HintFiles. They remain available as +// an in-test / future ContractFinal extension surface; production ContractFinal +// Selection currently does not declare them, so the delivery plan treats every +// example as default-typed (contract or dry_run from ToolSpec.DryRun). +type ManualAgentExampleDisposition struct { + Index *int `json:"index"` + Mode ManualAgentExampleMode `json:"mode"` + ReasonCode ManualAgentExampleReasonCode `json:"reason_code"` + Reason string `json:"reason"` + Reviewed bool `json:"reviewed"` +} + +// ManualAgentExampleExecution is one resolved example and its effective test mode. +type ManualAgentExampleExecution struct { + CanonicalPath string + Index int + Example string + Mode ManualAgentExampleMode + DryRun *DryRunSpec + ReasonCode ManualAgentExampleReasonCode + Reason string + Source ManualAgentExampleDispositionSource +} + +// ManualAgentExampleDispositionSource distinguishes the normal typed-contract +// classification from narrow reviewed exceptions. +type ManualAgentExampleDispositionSource string + +const ( + ManualAgentExampleDispositionDefault ManualAgentExampleDispositionSource = "default" + ManualAgentExampleDispositionReviewedManual ManualAgentExampleDispositionSource = "reviewed_manual" +) + +// ManualAgentExampleExecutionPlan is a stable, typed report used by the +// exhaustive real-Cobra dry-run test. +type ManualAgentExampleExecutionPlan struct { + Examples []ManualAgentExampleExecution + Total int + Contract int + DryRun int + ContractOnly int + ReviewedContractOnly int + ContractOnlyByReason map[ManualAgentExampleReasonCode]int +} + +// ValidateAgentExampleDelivery is the final generation gate. It validates every +// bound tool's ContractFinal Selection.Examples against the assembled typed +// SchemaRegistry. +func ValidateAgentExampleDelivery(bound BoundCommandRegistry, registry SchemaRegistry) (ManualAgentExampleExecutionPlan, error) { + return BuildAgentExampleExecutionPlan(bound, registry) +} + +// ValidateEmbeddedManualAgentExampleDelivery is a compatibility alias for +// ValidateAgentExampleDelivery. +func ValidateEmbeddedManualAgentExampleDelivery(bound BoundCommandRegistry, registry SchemaRegistry) (ManualAgentExampleExecutionPlan, error) { + return ValidateAgentExampleDelivery(bound, registry) +} + +// BuildAgentExampleExecutionPlan validates every ContractFinal example against +// its real BoundCommand/Cobra contract. Runtime dry-run execution is opt-in and +// comes only from the final typed ToolSpec. +func BuildAgentExampleExecutionPlan(bound BoundCommandRegistry, registry SchemaRegistry) (ManualAgentExampleExecutionPlan, error) { + tools := make(map[string]ToolSpec, len(bound.Commands)) + for _, product := range registry.Products { + for _, tool := range product.Tools { + canonical := strings.TrimSpace(tool.Identity.CanonicalPath) + if canonical == "" { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("typed SchemaRegistry contains a tool with empty canonical path") + } + if _, duplicate := tools[canonical]; duplicate { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("typed SchemaRegistry contains duplicate tool %q", canonical) + } + tools[canonical] = tool + } + } + return buildAgentExampleExecutionPlan(bound, tools) +} + +// BuildManualAgentExampleExecutionPlan is a compatibility wrapper that ignores +// residual ManualAgentHintSet input and builds the plan from ContractFinal only. +func BuildManualAgentExampleExecutionPlan(bound BoundCommandRegistry, registry SchemaRegistry, _ ManualAgentHintSet) (ManualAgentExampleExecutionPlan, error) { + return BuildAgentExampleExecutionPlan(bound, registry) +} + +// ManualAgentHintSet is a retired HintFile projection type retained only so +// transitional call sites compile while they migrate to ContractFinal. It must +// stay empty in production. +type ManualAgentHintSet struct { + Revisions map[string]ManualAgentHintRevision `json:"revisions,omitempty"` + Products map[string]ManualAgentProductHint `json:"products,omitempty"` + Tools map[string]ManualAgentToolHint `json:"tools,omitempty"` +} + +// ManualAgentHintRevision is retained for transitional fixtures only. +type ManualAgentHintRevision struct { + GeneratedBy string `json:"generated_by"` + Model string `json:"model,omitempty"` + PromptVersion string `json:"prompt_version,omitempty"` + Reason string `json:"reason"` +} + +// ManualAgentProductHint is retained for transitional fixtures only. +type ManualAgentProductHint struct { + AgentSummary string `json:"agent_summary"` + UseWhen []string `json:"use_when"` + AvoidWhen []string `json:"avoid_when"` + Reviewed bool `json:"reviewed"` + Revision string `json:"revision"` + Reason string `json:"reason"` + Evidence []string `json:"evidence"` +} + +// ManualAgentToolHint is retained for transitional fixtures only. +type ManualAgentToolHint struct { + AgentSummary string `json:"agent_summary"` + UseWhen []string `json:"use_when"` + AvoidWhen []string `json:"avoid_when"` + Examples []string `json:"examples"` + ExampleDispositions []ManualAgentExampleDisposition `json:"example_dispositions,omitempty"` + Reviewed bool `json:"reviewed"` + Revision string `json:"revision"` + Reason string `json:"reason"` + Evidence []string `json:"evidence"` +} + +func buildAgentExampleExecutionPlan(bound BoundCommandRegistry, typedTools map[string]ToolSpec) (ManualAgentExampleExecutionPlan, error) { + plan := ManualAgentExampleExecutionPlan{ + ContractOnlyByReason: map[ManualAgentExampleReasonCode]int{}, + } + canonicalPaths := make([]string, 0, len(bound.Commands)) + for _, command := range bound.Commands { + canonical := strings.TrimSpace(command.CanonicalPath) + if !HasRuntimeContractFinal(command.PrimaryCommand) { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("bound tool %q has no ContractFinal declaration; Schema examples require leaf Schema.Selection", canonical) + } + canonicalPaths = append(canonicalPaths, canonical) + } + sort.Strings(canonicalPaths) + for _, canonical := range canonicalPaths { + spec, ok := bound.ByCanonical[canonical] + if !ok { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("example plan references unknown canonical tool %q", canonical) + } + selection := contractFinalSelectionHint(spec.PrimaryCommand) + var typedTool ToolSpec + if typedTools != nil { + var found bool + typedTool, found = typedTools[canonical] + if !found { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("example tool %q is missing from final typed SchemaRegistry", canonical) + } + } + if len(selection.Examples) == 0 { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("ContractFinal tool %s requires non-empty Selection.Examples", canonical) + } + if len(selection.Examples) > 2 { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("ContractFinal tool %s has %d examples; maximum is 2", canonical, len(selection.Examples)) + } + if err := validateAgentExampleDispositions(canonical, selection.Examples, selection.ExampleDispositions); err != nil { + return ManualAgentExampleExecutionPlan{}, err + } + dispositions := make(map[int]ManualAgentExampleDisposition, len(selection.ExampleDispositions)) + for _, disposition := range selection.ExampleDispositions { + dispositions[*disposition.Index] = disposition + } + paths := []agentExamplePath{{Path: spec.PrimaryCLIPath, Argv: strings.Fields(spec.PrimaryCLIPath), Command: spec.PrimaryCommand}} + for _, alias := range spec.AliasCommands { + paths = append(paths, agentExamplePath{Path: alias.Path, Argv: strings.Fields(alias.Path), Command: alias.Command}) + } + sort.Slice(paths, func(i, j int) bool { + if len(paths[i].Argv) != len(paths[j].Argv) { + return len(paths[i].Argv) > len(paths[j].Argv) + } + return paths[i].Path < paths[j].Path + }) + for index, example := range selection.Examples { + argv, err := tokenizeAgentExample(example) + if err != nil { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example has invalid argv syntax: %w", canonical, err) + } + if len(argv) < 2 || argv[0] != "dws" { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example must start with dws: %q", canonical, example) + } + for _, argument := range argv[1:] { + if argument == "--yes" || strings.HasPrefix(argument, "--yes=") { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example must not bypass confirmation with --yes", canonical) + } + if argument == "--help" || strings.HasPrefix(argument, "--help=") || argument == "-h" || strings.HasPrefix(argument, "-h=") { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example must demonstrate execution, not only --help", canonical) + } + } + remainder, matched, ok := matchAgentExamplePath(argv, paths) + if !ok { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example does not use its reviewed primary/alias path: %q", canonical, example) + } + if matched.Command == nil { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s reviewed path %q has no bound Cobra command", canonical, matched.Path) + } + constraints, err := strictCompatibilityConstraints(matched.Command, canonical) + if err != nil { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example for %q has invalid executable constraints: %w", canonical, matched.Path, err) + } + if typedTools != nil { + constraints.MutuallyExclusive = append(constraints.MutuallyExclusive, typedTool.Constraints.MutuallyExclusive...) + constraints.RequireOneOf = append(constraints.RequireOneOf, typedTool.Constraints.RequireOneOf...) + constraints.RequireTogether = append(constraints.RequireTogether, typedTool.Constraints.RequireTogether...) + constraints = normalizeRuntimeSchemaConstraints(constraints) + } + positionals, err := strictCompatibilityPositionals(matched.Command) + if err != nil { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example for %q has invalid executable positionals: %w", canonical, matched.Path, err) + } + if typedTools != nil { + positionals = mergeAgentExamplePositionals(positionals, typedTool.Positionals) + } + if err := validateAgentExampleCobraContract(matched.Command, remainder, constraints, positionals); err != nil { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example for %q: %w", canonical, matched.Path, err) + } + + execution := ManualAgentExampleExecution{ + CanonicalPath: canonical, + Index: index, + Example: example, + Mode: ManualAgentExampleModeContract, + Source: ManualAgentExampleDispositionDefault, + } + if typedTool.DryRun != nil { + dryRun := *typedTool.DryRun + execution.DryRun = &dryRun + execution.Mode = ManualAgentExampleModeDryRun + } + disposition, hasDisposition := dispositions[index] + if hasDisposition { + if typedTools != nil && execution.DryRun == nil { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("tool %s example disposition index %d narrows no explicit dry_run capability", canonical, index) + } + execution.Mode = disposition.Mode + execution.ReasonCode = disposition.ReasonCode + execution.Reason = strings.TrimSpace(disposition.Reason) + execution.Source = ManualAgentExampleDispositionReviewedManual + plan.ContractOnly++ + plan.ReviewedContractOnly++ + plan.ContractOnlyByReason[disposition.ReasonCode]++ + } else if execution.DryRun != nil { + plan.DryRun++ + } else { + plan.Contract++ + } + plan.Examples = append(plan.Examples, execution) + plan.Total++ + } + } + for canonical := range typedTools { + found := false + for _, command := range bound.Commands { + if strings.TrimSpace(command.CanonicalPath) == canonical { + found = true + break + } + } + if !found { + return ManualAgentExampleExecutionPlan{}, fmt.Errorf("final typed SchemaRegistry tool %q is missing from BoundCommandRegistry", canonical) + } + } + return plan, nil +} + +type agentExamplePath struct { + Path string + Argv []string + Command *cobra.Command +} + +func matchAgentExamplePath(argv []string, paths []agentExamplePath) ([]string, agentExamplePath, bool) { + if len(argv) == 0 || argv[0] != "dws" { + return nil, agentExamplePath{}, false + } + for index := range paths { + if paths[index].Argv == nil { + paths[index].Argv = strings.Fields(strings.TrimSpace(paths[index].Path)) + } + pathArgv := paths[index].Argv + if len(pathArgv) == 0 || len(argv) < len(pathArgv)+1 { + continue + } + matches := true + for offset := range pathArgv { + if argv[offset+1] != pathArgv[offset] { + matches = false + break + } + } + if matches { + return argv[len(pathArgv)+1:], paths[index], true + } + } + return nil, agentExamplePath{}, false +} + +func validateAgentExampleDispositions(canonical string, examples []string, dispositions []ManualAgentExampleDisposition) error { + seen := make(map[int]bool, len(dispositions)) + for _, disposition := range dispositions { + if disposition.Index == nil { + return fmt.Errorf("tool %s example disposition requires index", canonical) + } + index := *disposition.Index + if index < 0 || index >= len(examples) { + return fmt.Errorf("tool %s example disposition index %d is out of range for %d examples", canonical, index, len(examples)) + } + if seen[index] { + return fmt.Errorf("tool %s has duplicate example disposition index %d", canonical, index) + } + seen[index] = true + if !disposition.Reviewed { + return fmt.Errorf("tool %s example disposition index %d must be reviewed", canonical, index) + } + if disposition.Mode != ManualAgentExampleModeContractOnly { + return fmt.Errorf("tool %s example disposition index %d has invalid mode %q; only %q may narrow an explicit dry_run capability", canonical, index, disposition.Mode, ManualAgentExampleModeContractOnly) + } + if !validAgentExampleReasonCode(disposition.ReasonCode) { + return fmt.Errorf("tool %s example disposition index %d has invalid reason_code %q", canonical, index, disposition.ReasonCode) + } + if strings.TrimSpace(disposition.Reason) == "" { + return fmt.Errorf("tool %s example disposition index %d requires a non-empty reason", canonical, index) + } + } + return nil +} + +func validAgentExampleReasonCode(code ManualAgentExampleReasonCode) bool { + switch code { + case ManualAgentExampleReasonLocalState, ManualAgentExampleReasonStatefulPreflight: + return true + default: + return false + } +} + +func tokenizeAgentExample(input string) ([]string, error) { + var ( + argv []string + current strings.Builder + quote byte + tokenStarted bool + ) + flush := func() { + if tokenStarted { + argv = append(argv, current.String()) + current.Reset() + tokenStarted = false + } + } + for index := 0; index < len(input); index++ { + character := input[index] + if quote != 0 { + if character == quote { + quote = 0 + continue + } + if quote == '"' && character == '\\' { + if index+1 >= len(input) { + return nil, fmt.Errorf("trailing escape in double-quoted value") + } + index++ + current.WriteByte(input[index]) + continue + } + if quote == '"' && (character == '`' || character == '$') { + return nil, fmt.Errorf("shell expansion is not allowed") + } + current.WriteByte(character) + continue + } + + switch character { + case ' ', '\t': + flush() + case '\r', '\n': + return nil, fmt.Errorf("unquoted newline shell operator is not allowed") + case '\'', '"': + quote = character + tokenStarted = true + case '\\': + if index+1 >= len(input) { + return nil, fmt.Errorf("trailing escape") + } + index++ + current.WriteByte(input[index]) + tokenStarted = true + case '<': + placeholder, next, ok := agentExamplePlaceholderAt(input, index) + if !ok { + return nil, fmt.Errorf("shell redirection operator %q is not allowed", string(character)) + } + current.WriteString(placeholder) + tokenStarted = true + index = next + case '>', ';', '|', '&', '(', ')': + return nil, fmt.Errorf("shell operator %q is not allowed", string(character)) + case '`', '$': + return nil, fmt.Errorf("shell expansion is not allowed") + case '#': + return nil, fmt.Errorf("shell comments are not allowed") + default: + current.WriteByte(character) + tokenStarted = true + } + } + if quote != 0 { + return nil, fmt.Errorf("unterminated quoted value") + } + flush() + for _, argument := range argv { + if argument == "--" { + return nil, fmt.Errorf("the -- argument terminator is not allowed") + } + } + return argv, nil +} + +// ParseManualAgentExampleArgv exposes the shell-free argv parser used by +// example validation and dry-run tests. +func ParseManualAgentExampleArgv(input string) ([]string, error) { + return tokenizeAgentExample(input) +} + +func agentExamplePlaceholderAt(input string, start int) (string, int, bool) { + endOffset := strings.IndexByte(input[start+1:], '>') + if endOffset < 1 { + return "", start, false + } + end := start + 1 + endOffset + body := input[start+1 : end] + for index := 0; index < len(body); index++ { + character := body[index] + if strings.ContainsRune(" \t\r\n<>;&|`$()#'\"\\", rune(character)) { + return "", start, false + } + } + return input[start : end+1], end, true +} + +func validateAgentExampleCobraContract(command *cobra.Command, arguments []string, constraints RuntimeSchemaConstraints, positionalSpecs []RuntimeSchemaPositional) error { + if command == nil { + return fmt.Errorf("bound Cobra command is nil") + } + providedFacts := map[string]bool{} + positionals := make([]string, 0) + for index := 0; index < len(arguments); index++ { + argument := arguments[index] + if argument == "--" { + return fmt.Errorf("the -- argument terminator is not allowed") + } + if strings.HasPrefix(argument, "--") { + nameAndValue := strings.TrimPrefix(argument, "--") + name, _, hasValue := strings.Cut(nameAndValue, "=") + if name == "" { + return fmt.Errorf("invalid empty long flag") + } + if name == "help" { + return fmt.Errorf("must demonstrate execution, not only --help") + } + flag := runtimeCommandFlag(command, name) + if flag == nil { + return fmt.Errorf("uses unknown flag --%s", name) + } + providedFacts[flag.Name] = true + if !hasValue && flag.NoOptDefVal == "" { + if index+1 >= len(arguments) { + return fmt.Errorf("flag --%s requires a value", name) + } + index++ + } + continue + } + if !strings.HasPrefix(argument, "-") || argument == "-" { + positionals = append(positionals, argument) + continue + } + + shorthandsAndValue := strings.TrimPrefix(argument, "-") + shorthands, _, hasExplicitValue := strings.Cut(shorthandsAndValue, "=") + for offset := 0; offset < len(shorthands); offset++ { + shorthand := shorthands[offset : offset+1] + if shorthand[0] >= 0x80 { + return fmt.Errorf("uses invalid non-ASCII shorthand flag") + } + if shorthand == "h" { + return fmt.Errorf("must demonstrate execution, not only -h") + } + flag := runtimeCommandFlagByShorthand(command, shorthand) + if flag == nil { + return fmt.Errorf("uses unknown shorthand flag -%s", shorthand) + } + providedFacts[flag.Name] = true + if flag.NoOptDefVal == "" { + if offset+1 < len(shorthands) || hasExplicitValue { + break + } + if index+1 >= len(arguments) { + return fmt.Errorf("shorthand flag -%s requires a value", shorthand) + } + index++ + break + } + } + } + missingRequired := make([]string, 0) + visitAgentExampleCommandFlags(command, func(flag *pflag.Flag) { + if flag != nil && len(flag.Annotations[cobra.BashCompOneRequiredFlag]) > 0 && !providedFacts[flag.Name] { + missingRequired = append(missingRequired, "--"+flag.Name) + } + }) + if len(missingRequired) != 0 { + sort.Strings(missingRequired) + return fmt.Errorf("missing required flag(s): %s", strings.Join(missingRequired, ", ")) + } + missingPositionals := make([]string, 0) + for _, positional := range positionalSpecs { + provided := positional.Index >= 0 && positional.Index < len(positionals) + if positional.Variadic { + provided = positional.Index >= 0 && len(positionals) > positional.Index + } + if provided && strings.TrimSpace(positional.Name) != "" { + providedFacts[strings.TrimLeft(strings.TrimSpace(positional.Name), "-")] = true + } + if positional.Required && !provided { + missingPositionals = append(missingPositionals, strings.TrimSpace(positional.Name)) + } + } + if len(missingPositionals) != 0 { + sort.Strings(missingPositionals) + return fmt.Errorf("missing required positional argument(s): %s", strings.Join(missingPositionals, ", ")) + } + if err := validateAgentExampleConstraints(providedFacts, constraints); err != nil { + return err + } + if command.Args != nil && len(positionalSpecs) == 0 { + if err := command.Args(command, positionals); err != nil { + return fmt.Errorf("invalid positional arguments: %w", err) + } + } + return nil +} + +func mergeAgentExamplePositionals(groups ...[]RuntimeSchemaPositional) []RuntimeSchemaPositional { + byIdentity := map[string]RuntimeSchemaPositional{} + for _, group := range groups { + for _, positional := range group { + key := fmt.Sprintf("%d\x00%s", positional.Index, strings.TrimSpace(positional.Name)) + if _, exists := byIdentity[key]; !exists { + byIdentity[key] = positional + } + } + } + result := make([]RuntimeSchemaPositional, 0, len(byIdentity)) + for _, positional := range byIdentity { + result = append(result, positional) + } + sort.Slice(result, func(i, j int) bool { + if result[i].Index != result[j].Index { + return result[i].Index < result[j].Index + } + return result[i].Name < result[j].Name + }) + return result +} + +func validateAgentExampleConstraints(provided map[string]bool, constraints RuntimeSchemaConstraints) error { + for _, group := range constraints.RequireOneOf { + if agentExampleProvidedFlagCount(provided, group) == 0 { + return fmt.Errorf("missing require_one_of flags: %s", agentExampleFlagGroup(group)) + } + } + for _, group := range constraints.RequireTogether { + providedCount := agentExampleProvidedFlagCount(provided, group) + if providedCount != 0 && providedCount != len(group) { + return fmt.Errorf("incomplete require_together flags: %s", agentExampleFlagGroup(group)) + } + } + for _, group := range constraints.MutuallyExclusive { + if agentExampleProvidedFlagCount(provided, group) > 1 { + return fmt.Errorf("mutually_exclusive flags used together: %s", agentExampleFlagGroup(group)) + } + } + return nil +} + +func agentExampleProvidedFlagCount(provided map[string]bool, group []string) int { + count := 0 + seen := map[string]bool{} + for _, raw := range group { + name := strings.TrimLeft(strings.TrimSpace(raw), "-") + if name != "" && !seen[name] && provided[name] { + seen[name] = true + count++ + } + } + return count +} + +func agentExampleFlagGroup(group []string) string { + flags := make([]string, 0, len(group)) + for _, raw := range group { + if name := strings.TrimLeft(strings.TrimSpace(raw), "-"); name != "" { + flags = append(flags, "--"+name) + } + } + sort.Strings(flags) + return strings.Join(flags, ", ") +} + +func visitAgentExampleCommandFlags(command *cobra.Command, visit func(*pflag.Flag)) { + seen := map[string]bool{} + visitSet := func(flags *pflag.FlagSet) { + flags.VisitAll(func(flag *pflag.Flag) { + if seen[flag.Name] { + return + } + seen[flag.Name] = true + visit(flag) + }) + } + visitSet(command.Flags()) + for current := command; current != nil; current = current.Parent() { + visitSet(current.PersistentFlags()) + } +} + +func runtimeCommandFlagByShorthand(command *cobra.Command, shorthand string) *pflag.Flag { + if command == nil || len(shorthand) != 1 { + return nil + } + if flag := command.Flags().ShorthandLookup(shorthand); flag != nil { + return flag + } + for current := command; current != nil; current = current.Parent() { + if flag := current.PersistentFlags().ShorthandLookup(shorthand); flag != nil { + return flag + } + } + return nil +} diff --git a/internal/cli/schema_agent_selection.go b/internal/cli/schema_agent_selection.go index f4bd6688..f067ee4a 100644 --- a/internal/cli/schema_agent_selection.go +++ b/internal/cli/schema_agent_selection.go @@ -24,16 +24,16 @@ import ( "github.com/spf13/cobra" ) -var marshalManualAgentSelectionFixture = json.Marshal +var marshalAgentSelectionFixture = json.Marshal -const manualAgentSelectionFixtureVersion = 1 +const agentSelectionFixtureVersion = 1 -// ManualAgentSelectionCase is one reproducible model-evaluation assertion -// derived from reviewed Manual Agent hints. Positive cases require one exact +// AgentSelectionCase is one reproducible model-evaluation assertion derived +// from ContractFinal Selection prose. Positive cases require one exact // canonical result; negative cases only forbid the command that owns the // avoid_when text. CandidateCanonicals contains every bound tool in the same // product, in stable order. -type ManualAgentSelectionCase struct { +type AgentSelectionCase struct { ID string `json:"id"` ProductID string `json:"product_id"` Scenario string `json:"scenario"` @@ -42,61 +42,54 @@ type ManualAgentSelectionCase struct { CandidateCanonicals []string `json:"candidate_canonicals"` } -// ManualAgentSelectionFixture is the stable input to an optional live Agent -// evaluation. It is built from ProductDecl/ContractFinal selection prose -// (plus any residual selection HintFiles) and the real bound command tree; -// it is not a second authored hint source. -type ManualAgentSelectionFixture struct { - Version int `json:"version"` - Cases []ManualAgentSelectionCase `json:"cases"` +// ManualAgentSelectionCase is a compatibility alias for AgentSelectionCase. +type ManualAgentSelectionCase = AgentSelectionCase + +// AgentSelectionFixture is the stable input to an optional live Agent +// evaluation. It is built from ProductDecl/ContractFinal selection prose and +// the real bound command tree; it is not a second authored hint source. +type AgentSelectionFixture struct { + Version int `json:"version"` + Cases []AgentSelectionCase `json:"cases"` } -// ManualAgentSelectionReport records deterministic coverage and the exact -// fixture digest. It proves that all reviewed assertions are well-formed and +// ManualAgentSelectionFixture is a compatibility alias for AgentSelectionFixture. +type ManualAgentSelectionFixture = AgentSelectionFixture + +// AgentSelectionReport records deterministic coverage and the exact fixture +// digest. It proves that all reviewed assertions are well-formed and // executable; it deliberately does not claim that a language model understood // their natural-language meaning. -type ManualAgentSelectionReport struct { +type AgentSelectionReport struct { Tools int PositiveAssertions int NegativeAssertions int FixtureSHA256 string } -// BuildManualAgentSelectionEvalFixture turns every use_when and avoid_when -// entry into a typed, reproducible evaluation case and validates it against -// the exact BoundCommandRegistry. -// -// Deterministic CI can prove full coverage, real command binding, and absence -// of literal contradictory expectations. Semantic command choice belongs to -// an opt-in live-model evaluation that consumes this fixture; this function -// never substitutes string matching for Agent behavior. -func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints ManualAgentHintSet) (ManualAgentSelectionFixture, ManualAgentSelectionReport, error) { - fixture := ManualAgentSelectionFixture{Version: manualAgentSelectionFixtureVersion} - report := ManualAgentSelectionReport{} +// ManualAgentSelectionReport is a compatibility alias for AgentSelectionReport. +type ManualAgentSelectionReport = AgentSelectionReport + +// BuildAgentSelectionEvalFixture turns every ContractFinal use_when and +// avoid_when entry into a typed, reproducible evaluation case and validates it +// against the exact BoundCommandRegistry. +func BuildAgentSelectionEvalFixture(bound BoundCommandRegistry) (AgentSelectionFixture, AgentSelectionReport, error) { + fixture := AgentSelectionFixture{Version: agentSelectionFixtureVersion} + report := AgentSelectionReport{} expectedTools := make(map[string]bool, len(bound.Commands)) - declaredTools := map[string]bool{} candidatesByProduct := map[string][]string{} for _, command := range bound.Commands { canonical := strings.TrimSpace(command.CanonicalPath) expectedTools[canonical] = true - // Declared tools source selection assertions from the Contract final - // overlay (corecmd.SchemaDecl) instead of a hint-file row. - if HasRuntimeContractFinal(command.PrimaryCommand) { - declaredTools[canonical] = true + if !HasRuntimeContractFinal(command.PrimaryCommand) { + return fixture, report, fmt.Errorf("agent selection expected canonical %q has no ContractFinal declaration", canonical) } productID, _, ok := strings.Cut(canonical, ".") if !ok || strings.TrimSpace(productID) == "" { - return fixture, report, fmt.Errorf("agent_hints selection has invalid bound canonical path %q", canonical) + return fixture, report, fmt.Errorf("agent selection has invalid bound canonical path %q", canonical) } candidatesByProduct[productID] = append(candidatesByProduct[productID], canonical) } - hintTools := mapKeysManualAgentTools(hints.Tools) - for canonical := range declaredTools { - hintTools[canonical] = true - } - if err := validateManualAgentHintExactSet("selection tools", expectedTools, hintTools); err != nil { - return fixture, report, err - } for productID := range candidatesByProduct { sort.Strings(candidatesByProduct[productID]) } @@ -112,35 +105,32 @@ func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints Manu for _, canonical := range canonicals { command, ok := bound.ByCanonical[canonical] if !ok { - return fixture, report, fmt.Errorf("agent_hints selection expected canonical %q is missing from BoundCommandRegistry.ByCanonical", canonical) + return fixture, report, fmt.Errorf("agent selection expected canonical %q is missing from BoundCommandRegistry.ByCanonical", canonical) } - if err := validateManualAgentSelectionBinding(bound, canonical, command); err != nil { + if err := validateAgentSelectionBinding(bound, canonical, command); err != nil { return fixture, report, err } - hint, hintOK := hints.Tools[canonical] - if !hintOK && declaredTools[canonical] { - hint = contractFinalSelectionHint(command.PrimaryCommand) + selection := contractFinalSelectionHint(command.PrimaryCommand) + if len(selection.UseWhen) == 0 { + return fixture, report, fmt.Errorf("ContractFinal tool %s requires at least one positive use_when selection assertion", canonical) } - if len(hint.UseWhen) == 0 { - return fixture, report, fmt.Errorf("agent_hints tool %s requires at least one positive use_when selection assertion", canonical) - } - if len(hint.AvoidWhen) == 0 { - return fixture, report, fmt.Errorf("agent_hints tool %s requires at least one negative avoid_when selection assertion", canonical) + if len(selection.AvoidWhen) == 0 { + return fixture, report, fmt.Errorf("ContractFinal tool %s requires at least one negative avoid_when selection assertion", canonical) } productID, _, _ := strings.Cut(canonical, ".") candidates := candidatesByProduct[productID] - for index, scenario := range hint.UseWhen { - normalized := normalizeManualAgentSelectionScenario(scenario) + for index, scenario := range selection.UseWhen { + normalized := normalizeAgentSelectionScenario(scenario) if normalized == "" { - return fixture, report, fmt.Errorf("agent_hints tool %s has an empty normalized use_when selection assertion", canonical) + return fixture, report, fmt.Errorf("ContractFinal tool %s has an empty normalized use_when selection assertion", canonical) } if previous, exists := positiveExpectations[normalized]; exists { - return fixture, report, fmt.Errorf("agent_hints use_when scenario %q has conflicting literal expectations %q and %q", positiveDisplays[normalized], previous, canonical) + return fixture, report, fmt.Errorf("use_when scenario %q has conflicting literal expectations %q and %q", positiveDisplays[normalized], previous, canonical) } positiveExpectations[normalized] = canonical positiveDisplays[normalized] = strings.TrimSpace(scenario) - fixture.Cases = append(fixture.Cases, ManualAgentSelectionCase{ + fixture.Cases = append(fixture.Cases, AgentSelectionCase{ ID: fmt.Sprintf("%s/use_when/%d", canonical, index), ProductID: productID, Scenario: strings.TrimSpace(scenario), @@ -149,15 +139,15 @@ func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints Manu }) report.PositiveAssertions++ } - for index, scenario := range hint.AvoidWhen { - normalized := normalizeManualAgentSelectionScenario(scenario) + for index, scenario := range selection.AvoidWhen { + normalized := normalizeAgentSelectionScenario(scenario) if normalized == "" { - return fixture, report, fmt.Errorf("agent_hints tool %s has an empty normalized avoid_when selection assertion", canonical) + return fixture, report, fmt.Errorf("ContractFinal tool %s has an empty normalized avoid_when selection assertion", canonical) } if expected := positiveExpectations[normalized]; expected == canonical { - return fixture, report, fmt.Errorf("agent_hints tool %s has the same literal positive and negative selection scenario %q", canonical, strings.TrimSpace(scenario)) + return fixture, report, fmt.Errorf("ContractFinal tool %s has the same literal positive and negative selection scenario %q", canonical, strings.TrimSpace(scenario)) } - fixture.Cases = append(fixture.Cases, ManualAgentSelectionCase{ + fixture.Cases = append(fixture.Cases, AgentSelectionCase{ ID: fmt.Sprintf("%s/avoid_when/%d", canonical, index), ProductID: productID, Scenario: strings.TrimSpace(scenario), @@ -169,7 +159,7 @@ func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints Manu } report.Tools = len(canonicals) - digest, err := manualAgentSelectionFixtureDigest(fixture) + digest, err := agentSelectionFixtureDigest(fixture) if err != nil { return fixture, report, err } @@ -177,17 +167,25 @@ func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, hints Manu return fixture, report, nil } -// ValidateManualAgentSelectionContract is the lightweight generator-facing -// gate. Callers that run a semantic model evaluation should use -// BuildManualAgentSelectionEvalFixture and pass the returned cases to it. -func ValidateManualAgentSelectionContract(bound BoundCommandRegistry, hints ManualAgentHintSet) (ManualAgentSelectionReport, error) { - _, report, err := BuildManualAgentSelectionEvalFixture(bound, hints) +// BuildManualAgentSelectionEvalFixture is a compatibility wrapper that ignores +// residual ManualAgentHintSet input and builds from ContractFinal only. +func BuildManualAgentSelectionEvalFixture(bound BoundCommandRegistry, _ ManualAgentHintSet) (AgentSelectionFixture, AgentSelectionReport, error) { + return BuildAgentSelectionEvalFixture(bound) +} + +// ValidateAgentSelectionContract is the lightweight generator-facing gate. +func ValidateAgentSelectionContract(bound BoundCommandRegistry) (AgentSelectionReport, error) { + _, report, err := BuildAgentSelectionEvalFixture(bound) return report, err } -// contractFinalSelectionHint synthesizes the selection assertions of a -// declared tool from its Contract final overlay, so declared tools keep full -// semantic-eval coverage without a hint-file row. +// ValidateManualAgentSelectionContract is a compatibility wrapper. +func ValidateManualAgentSelectionContract(bound BoundCommandRegistry, _ ManualAgentHintSet) (AgentSelectionReport, error) { + return ValidateAgentSelectionContract(bound) +} + +// contractFinalSelectionHint synthesizes selection assertions of a declared +// tool from its ContractFinal overlay. func contractFinalSelectionHint(command *cobra.Command) ManualAgentToolHint { hint := ManualAgentToolHint{Reviewed: true, Revision: "contract", Reason: "Contract final declaration (corecmd.SchemaDecl)"} payload, ok := RuntimeContractFinal(command) @@ -202,35 +200,35 @@ func contractFinalSelectionHint(command *cobra.Command) ManualAgentToolHint { return hint } -func validateManualAgentSelectionBinding(bound BoundCommandRegistry, canonical string, command BoundCommandSpec) error { +func validateAgentSelectionBinding(bound BoundCommandRegistry, canonical string, command BoundCommandSpec) error { if command.CanonicalPath != canonical { - return fmt.Errorf("agent_hints selection canonical %q resolves to mismatched BoundCommandRegistry entry %q", canonical, command.CanonicalPath) + return fmt.Errorf("agent selection canonical %q resolves to mismatched BoundCommandRegistry entry %q", canonical, command.CanonicalPath) } if command.PrimaryCommand == nil { - return fmt.Errorf("agent_hints selection canonical %q has no bound primary Cobra command", canonical) + return fmt.Errorf("agent selection canonical %q has no bound primary Cobra command", canonical) } if !runnableSchemaLeaf(command.PrimaryCommand) { - return fmt.Errorf("agent_hints selection canonical %q primary path %q is not a runnable Cobra leaf", canonical, command.PrimaryCLIPath) + return fmt.Errorf("agent selection canonical %q primary path %q is not a runnable Cobra leaf", canonical, command.PrimaryCLIPath) } primaryPath := normalizeSchemaCLIPath(command.PrimaryCLIPath) if primaryPath == "" { - return fmt.Errorf("agent_hints selection canonical %q has an empty primary CLI path", canonical) + return fmt.Errorf("agent selection canonical %q has an empty primary CLI path", canonical) } byPath, ok := bound.ByCLIPath[primaryPath] if !ok || byPath.CanonicalPath != canonical { - return fmt.Errorf("agent_hints selection canonical %q primary path %q is not bound back to the same tool", canonical, primaryPath) + return fmt.Errorf("agent selection canonical %q primary path %q is not bound back to the same tool", canonical, primaryPath) } return nil } -func normalizeManualAgentSelectionScenario(value string) string { +func normalizeAgentSelectionScenario(value string) string { return strings.ToLower(strings.Join(strings.Fields(strings.TrimSpace(value)), " ")) } -func manualAgentSelectionFixtureDigest(fixture ManualAgentSelectionFixture) (string, error) { - data, err := marshalManualAgentSelectionFixture(fixture) +func agentSelectionFixtureDigest(fixture AgentSelectionFixture) (string, error) { + data, err := marshalAgentSelectionFixture(fixture) if err != nil { - return "", fmt.Errorf("marshal Manual Agent selection fixture: %w", err) + return "", fmt.Errorf("marshal Agent selection fixture: %w", err) } digest := sha256.Sum256(data) return "sha256:" + hex.EncodeToString(digest[:]), nil diff --git a/internal/cli/schema_agent_selection_test.go b/internal/cli/schema_agent_selection_test.go deleted file mode 100644 index 8c8b4231..00000000 --- a/internal/cli/schema_agent_selection_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "reflect" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestBuildManualAgentSelectionEvalFixtureUsesReviewedScenariosAndRealCommands(t *testing.T) { - bound := manualAgentSelectionBoundFixture() - hints := manualAgentHintSetFixture() - - fixture, report, err := BuildManualAgentSelectionEvalFixture(bound, hints) - if err != nil { - t.Fatalf("BuildManualAgentSelectionEvalFixture() error = %v", err) - } - if report.Tools != 1 || report.PositiveAssertions != 1 || report.NegativeAssertions != 1 { - t.Fatalf("selection report = %+v", report) - } - if report.FixtureSHA256 == "" || len(fixture.Cases) != 2 { - t.Fatalf("selection fixture = %+v, report = %+v", fixture, report) - } - positive, negative := fixture.Cases[0], fixture.Cases[1] - if positive.ExpectedCanonical != "sample.search_items" || positive.ForbiddenCanonical != "" { - t.Fatalf("positive case = %+v", positive) - } - if negative.ForbiddenCanonical != "sample.search_items" || negative.ExpectedCanonical != "" { - t.Fatalf("negative case = %+v", negative) - } - if !reflect.DeepEqual(positive.CandidateCanonicals, []string{"sample.search_items"}) { - t.Fatalf("positive candidates = %#v", positive.CandidateCanonicals) - } - - _, repeated, err := BuildManualAgentSelectionEvalFixture(bound, hints) - if err != nil || repeated.FixtureSHA256 != report.FixtureSHA256 { - t.Fatalf("repeated fixture digest = %q, err=%v; want %q", repeated.FixtureSHA256, err, report.FixtureSHA256) - } -} - -func TestCrossPlatformCoverageBuildManualAgentSelectionEvalFixtureRejectsFactualContractDrift(t *testing.T) { - tests := []struct { - name string - mutate func(*BoundCommandRegistry, *ManualAgentHintSet) - wantErr string - }{ - { - name: "missing reviewed tool", - mutate: func(_ *BoundCommandRegistry, hints *ManualAgentHintSet) { - delete(hints.Tools, "sample.search_items") - }, - wantErr: "selection tools do not exactly match", - }, - { - name: "unbound primary command", - mutate: func(bound *BoundCommandRegistry, _ *ManualAgentHintSet) { - item := bound.ByCanonical["sample.search_items"] - item.PrimaryCommand = nil - bound.ByCanonical["sample.search_items"] = item - }, - wantErr: "no bound primary Cobra command", - }, - { - name: "literal positive negative contradiction", - mutate: func(_ *BoundCommandRegistry, hints *ManualAgentHintSet) { - item := hints.Tools["sample.search_items"] - item.AvoidWhen = append([]string(nil), item.UseWhen...) - hints.Tools["sample.search_items"] = item - }, - wantErr: "same literal positive and negative", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - bound := manualAgentSelectionBoundFixture() - hints := manualAgentHintSetFixture() - test.mutate(&bound, &hints) - _, _, err := BuildManualAgentSelectionEvalFixture(bound, hints) - if err == nil || !strings.Contains(err.Error(), test.wantErr) { - t.Fatalf("error = %v, want containing %q", err, test.wantErr) - } - }) - } -} - -func TestBuildManualAgentSelectionEvalFixtureRejectsOneScenarioWithTwoExpectedTools(t *testing.T) { - bound := manualAgentSelectionBoundFixture() - root := bound.ByCanonical["sample.search_items"].PrimaryCommand.Root() - create := &cobra.Command{Use: "create", RunE: func(*cobra.Command, []string) error { return nil }} - exactSchemaCommand(root, "sample item").AddCommand(create) - createSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{ - CanonicalPath: "sample.create_item", - PrimaryCLIPath: "sample item create", - }, - PrimaryCommand: create, - } - bound.Commands = append(bound.Commands, createSpec) - bound.ByCanonical[createSpec.CanonicalPath] = createSpec - bound.ByCLIPath[createSpec.PrimaryCLIPath] = createSpec - - hints := manualAgentHintSetFixture() - search := hints.Tools["sample.search_items"] - createHint := search - createHint.AgentSummary = "Create a sample item" - createHint.Examples = []string{"dws sample item create"} - createHint.UseWhen = append([]string(nil), search.UseWhen...) - createHint.AvoidWhen = []string{"An existing sample item must be found"} - hints.Tools[createSpec.CanonicalPath] = createHint - - _, _, err := BuildManualAgentSelectionEvalFixture(bound, hints) - if err == nil || !strings.Contains(err.Error(), "conflicting literal expectations") { - t.Fatalf("error = %v, want literal expectation conflict", err) - } -} - -func manualAgentSelectionBoundFixture() BoundCommandRegistry { - _, leaf := manualSchemaHintTestTree() - spec := BoundCommandSpec{ - CommandSpec: CommandSpec{ - CanonicalPath: "sample.search_items", - PrimaryCLIPath: "sample item search", - }, - PrimaryCommand: leaf, - } - return BoundCommandRegistry{ - Commands: []BoundCommandSpec{spec}, - ByCanonical: map[string]BoundCommandSpec{spec.CanonicalPath: spec}, - ByCLIPath: map[string]BoundCommandSpec{spec.PrimaryCLIPath: spec}, - } -} - -func TestCrossPlatformCoverageContractFinalSelectionHintWithoutDeclaredSelection(t *testing.T) { - // No ContractFinal at all: the synthesized hint stays empty but reviewed. - hint := contractFinalSelectionHint(&cobra.Command{Use: "bare"}) - if !hint.Reviewed || hint.Revision != "contract" { - t.Fatalf("undeclared hint = %#v", hint) - } - if hint.AgentSummary != "" || hint.UseWhen != nil || hint.Examples != nil { - t.Fatalf("undeclared hint must carry no selection prose: %#v", hint) - } - - // ContractFinal without Selection: same early return. - cmd := &cobra.Command{Use: "declared"} - t.Cleanup(func() { ClearRuntimeContractFinalForTest(cmd) }) - RegisterRuntimeContractFinal(cmd, ContractFinalPayload{Title: "T"}) - hint = contractFinalSelectionHint(cmd) - if !hint.Reviewed || hint.AgentSummary != "" || hint.UseWhen != nil { - t.Fatalf("selection-less declared hint = %#v", hint) - } -} diff --git a/internal/cli/schema_canonical_path.go b/internal/cli/schema_canonical_path.go new file mode 100644 index 00000000..06d208d0 --- /dev/null +++ b/internal/cli/schema_canonical_path.go @@ -0,0 +1,60 @@ +// Copyright 2026 Alibaba Group +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "strings" + + "github.com/spf13/cobra" +) + +// SchemaVisibility is the reviewed CommandRegistry visibility class for a +// command identity. +type SchemaVisibility string + +const ( + SchemaVisibilityPublic SchemaVisibility = "public" + SchemaVisibilityCompat SchemaVisibility = "compat" + SchemaVisibilityInternal SchemaVisibility = "internal" +) + +// splitSchemaCanonicalPath splits "product.tool" identity keys used by the +// reviewed CommandRegistry and Schema assembly. +func splitSchemaCanonicalPath(path string) (string, string, bool) { + path = strings.TrimSpace(path) + productID, toolName, ok := strings.Cut(path, ".") + productID = strings.TrimSpace(productID) + toolName = strings.TrimSpace(toolName) + if !ok || productID == "" || toolName == "" || strings.ContainsAny(productID+toolName, " \t\r\n") { + return "", "", false + } + return productID, toolName, true +} + +// splitManualSchemaCanonicalPath is a compatibility alias. +func splitManualSchemaCanonicalPath(path string) (string, string, bool) { + return splitSchemaCanonicalPath(path) +} + +func publicRunnableSchemaLeaf(command *cobra.Command) bool { + if command == nil || !command.Runnable() || command.HasSubCommands() { + return false + } + for current := command; current != nil; current = current.Parent() { + if current.Hidden { + return false + } + } + return true +} diff --git a/internal/cli/schema_catalog_coverage_more_test.go b/internal/cli/schema_catalog_coverage_more_test.go deleted file mode 100644 index 76779f72..00000000 --- a/internal/cli/schema_catalog_coverage_more_test.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package cli - -import ( - "errors" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestCrossPlatformCoverageBuildSchemaCatalogSnapshotGateFailures(t *testing.T) { - registry, _ := validSnapshotAdapterFixture(t) - resolved := ResolvedSchemaBuild{root: &cobra.Command{Use: "root"}, registry: registry} - original := captureCatalogHooks() - t.Cleanup(original.restore) - installCatalogBuildNoops() - - if _, err := BuildSchemaCatalogSnapshot(resolved, SchemaCatalogBuildOptions{RegistryHash: "wrong"}); err == nil { - t.Fatal("wrong registry hash succeeded") - } - - failure := errors.New("gate failed") - tests := []struct { - name string - set func() - }{ - {"parameter bindings", func() { - buildCatalogValidateParameterBindings = func(BoundCommandRegistry, SchemaRegistry) error { return failure } - }}, - {"dry run", func() { buildCatalogValidateDryRun = func(SchemaRegistry) error { return failure } }}, - {"examples", func() { - buildCatalogValidateExamples = func(BoundCommandRegistry, SchemaRegistry) (ManualAgentExampleExecutionPlan, error) { - return ManualAgentExampleExecutionPlan{}, failure - } - }}, - {"completeness", func() { - buildCatalogValidateCompleteness = func(*cobra.Command, BoundCommandRegistry) error { return failure } - }}, - {"registry", func() { - buildCatalogValidateRegistry = func(SchemaRegistry, EffectiveCommandRegistry) error { return failure } - }}, - {"interfaces", func() { buildCatalogValidateInterfaces = func(SchemaRegistry) error { return failure } }}, - {"agent metadata", func() { buildCatalogValidateAgentMetadata = func(SchemaRegistry) error { return failure } }}, - {"provenance", func() { buildCatalogValidateProvenance = func(SchemaRegistry) error { return failure } }}, - {"delivery", func() { - buildCatalogValidateDelivery = func(SchemaRegistry, SchemaCatalogSnapshot) error { return failure } - }}, - {"final completeness", func() { - buildCatalogValidateFinalCompleteness = func(*cobra.Command, BoundCommandRegistry, SchemaCatalogSnapshot) error { return failure } - }}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - installCatalogBuildNoops() - tc.set() - if _, err := BuildSchemaCatalogSnapshot(resolved, SchemaCatalogBuildOptions{}); err == nil || !strings.Contains(err.Error(), "gate failed") { - t.Fatalf("gate error = %v", err) - } - }) - } - - installCatalogBuildNoops() - invalid := resolved - invalid.registry = SchemaRegistry{Products: []ProductSpec{{ID: " "}}} - if _, err := BuildSchemaCatalogSnapshot(invalid, SchemaCatalogBuildOptions{}); err == nil || !strings.Contains(err.Error(), "serialize typed") { - t.Fatalf("serialization error = %v", err) - } -} - -func TestCrossPlatformCoverageLoadSchemaCatalogSnapshotGateFailures(t *testing.T) { - _, base := validSnapshotAdapterFixture(t) - base.Version = SchemaCatalogSnapshotVersion - base.SourceHash = schemaCatalogSnapshotHash(base) - original := captureCatalogHooks() - t.Cleanup(original.restore) - installCatalogLoadNoops() - - badVersion := cloneSnapshotForCoverage(t, base) - badVersion.Version = 2 - if _, err := loadSchemaCatalogSnapshot(badVersion); err == nil { - t.Fatal("bad snapshot version succeeded") - } - if _, err := loadSchemaCatalogSnapshot(SchemaCatalogSnapshot{Version: SchemaCatalogSnapshotVersion}); err == nil { - t.Fatal("empty snapshot succeeded") - } - // source_hash equality is a build-time property (check-generated-drift.sh); - // the runtime loader only rejects a missing hash. A wrong but present hash - // succeeds because the embedded artifact cannot change at runtime. - missingHash := cloneSnapshotForCoverage(t, base) - missingHash.SourceHash = "" - if _, err := loadSchemaCatalogSnapshot(missingHash); err == nil { - t.Fatal("missing snapshot hash succeeded") - } - badTyped := cloneSnapshotForCoverage(t, base) - badTyped.Catalog["unknown"] = true - badTyped.SourceHash = schemaCatalogSnapshotHash(badTyped) - if _, err := loadSchemaCatalogSnapshot(badTyped); err == nil || !strings.Contains(err.Error(), "load typed") { - t.Fatalf("typed load error = %v", err) - } - - failure := errors.New("load gate failed") - installCatalogLoadNoops() - loadCatalogValidateInterfaces = func(SchemaRegistry) error { return failure } - if _, err := loadSchemaCatalogSnapshot(base); err == nil || !strings.Contains(err.Error(), "load gate failed") { - t.Fatalf("interface load gate = %v", err) - } - installCatalogLoadNoops() - loadCatalogValidateProvenance = func(SchemaRegistry) error { return failure } - if _, err := loadSchemaCatalogSnapshot(base); err == nil || !strings.Contains(err.Error(), "load gate failed") { - t.Fatalf("provenance load gate = %v", err) - } - - embedded := cloneSnapshotForCoverage(t, base) - embedded.Catalog["source"] = "embedded-command-catalog" - embedded.SourceHash = schemaCatalogSnapshotHash(embedded) - installCatalogLoadNoops() - if _, err := loadSchemaCatalogSnapshot(embedded); err != nil { - t.Fatalf("embedded catalog load without Agent metadata gate = %v", err) - } -} - -func TestCrossPlatformCoverageSchemaCatalogLookupAndConversionEdges(t *testing.T) { - if exactSchemaCommand(nil, "sample") != nil { - t.Fatal("nil root resolved a command") - } - root := &cobra.Command{Use: "root"} - child := &cobra.Command{Use: "child", Run: func(*cobra.Command, []string) {}} - root.AddCommand(child) - if exactSchemaCommand(root, "root missing") != nil || exactSchemaCommand(root, "root") != nil || exactSchemaCommand(root, "root child") != child { - t.Fatal("exact schema command resolution mismatch") - } - - if schemaMap("bad") != nil { - t.Fatal("non-map schema value converted") - } - converted := schemaMap(map[string]any{"valid": map[string]any{"x": true}, "bad": "x"}) - if len(converted) != 1 { - t.Fatalf("converted schema map = %#v", converted) - } - if got := firstNonEmptySchemaString(nil, " "); got != "" { - t.Fatalf("empty schema string = %q", got) - } - - invalidLoaded := loadedSchemaCatalog{Registry: SchemaRegistry{Products: []ProductSpec{{ID: " "}}}} - if _, err := schemaPayloadFromLoadedCatalog(invalidLoaded, nil); err == nil { - t.Fatal("invalid loaded overview succeeded") - } - - originalAll, originalOverview := renderEmbeddedSchemaAll, renderEmbeddedSchemaOverview - originalProduct, originalTool := renderSchemaProductSummary, renderSchemaToolSummary - t.Cleanup(func() { - renderEmbeddedSchemaAll, renderEmbeddedSchemaOverview = originalAll, originalOverview - renderSchemaProductSummary, renderSchemaToolSummary = originalProduct, originalTool - }) - failure := errors.New("render failed") - renderEmbeddedSchemaAll = func(SchemaRegistry) (map[string]any, error) { return nil, failure } - if _, err := embeddedSchemaAllPayload(); err == nil { - t.Fatal("embedded all render failure succeeded") - } - renderEmbeddedSchemaAll = originalAll - renderEmbeddedSchemaOverview = func(SchemaRegistry) (map[string]any, error) { return nil, failure } - if _, err := embeddedSchemaOverviewPayload(); err == nil { - t.Fatal("embedded overview render failure succeeded") - } - renderEmbeddedSchemaOverview = originalOverview - - loaded := embeddedSchemaCatalog() - productID := loaded.Registry.Products[0].ID - renderSchemaProductSummary = func(ProductSpec) (map[string]any, error) { return nil, failure } - if _, err := schemaPayloadFromLoadedCatalog(loaded, []string{productID}); err == nil { - t.Fatal("product summary render failure succeeded") - } - renderSchemaProductSummary = originalProduct - group := "" - for _, product := range loaded.Registry.Products { - for _, tool := range product.Tools { - parts := strings.Fields(tool.Identity.CLIPath) - if len(parts) > 2 { - group = strings.Join(parts[:len(parts)-1], " ") - break - } - } - if group != "" { - break - } - } - if group == "" { - t.Fatal("production Catalog has no group path fixture") - } - renderSchemaToolSummary = func(ToolSpec) (map[string]any, error) { return nil, failure } - if _, err := schemaPayloadFromLoadedCatalog(loaded, []string{group}); err == nil { - t.Fatal("group tool render failure succeeded") - } -} - -func TestEmbeddedSchemaLookupAcceptsCompatibleCLIPathSeparators(t *testing.T) { - loaded := embeddedSchemaCatalog() - for _, path := range []string{ - "dev app list", - "dev.app.list", - "dev/app/list", - } { - payload, err := schemaPayloadFromLoadedCatalog(loaded, []string{path}) - if err != nil { - t.Fatalf("schemaPayloadFromLoadedCatalog(%q) error = %v", path, err) - } - if got := schemaString(payload["canonical_path"]); got != "dev.list_dev_app" { - t.Fatalf("schemaPayloadFromLoadedCatalog(%q) canonical_path = %q, want %q", path, got, "dev.list_dev_app") - } - } -} - -type catalogHookSnapshot struct { - parameterBindings func(BoundCommandRegistry, SchemaRegistry) error - dryRun func(SchemaRegistry) error - examples func(BoundCommandRegistry, SchemaRegistry) (ManualAgentExampleExecutionPlan, error) - completeness func(*cobra.Command, BoundCommandRegistry) error - registry func(SchemaRegistry, EffectiveCommandRegistry) error - interfaces func(SchemaRegistry) error - agentMetadata func(SchemaRegistry) error - provenance func(SchemaRegistry) error - delivery func(SchemaRegistry, SchemaCatalogSnapshot) error - finalCompleteness func(*cobra.Command, BoundCommandRegistry, SchemaCatalogSnapshot) error - loadInterfaces func(SchemaRegistry) error - loadProvenance func(SchemaRegistry) error - loadAgentMetadata func(SchemaRegistry) error -} - -func captureCatalogHooks() catalogHookSnapshot { - return catalogHookSnapshot{ - buildCatalogValidateParameterBindings, buildCatalogValidateDryRun, buildCatalogValidateExamples, - buildCatalogValidateCompleteness, buildCatalogValidateRegistry, buildCatalogValidateInterfaces, - buildCatalogValidateAgentMetadata, buildCatalogValidateProvenance, buildCatalogValidateDelivery, - buildCatalogValidateFinalCompleteness, loadCatalogValidateInterfaces, loadCatalogValidateProvenance, - loadCatalogValidateAgentMetadata, - } -} - -func (hooks catalogHookSnapshot) restore() { - buildCatalogValidateParameterBindings = hooks.parameterBindings - buildCatalogValidateDryRun = hooks.dryRun - buildCatalogValidateExamples = hooks.examples - buildCatalogValidateCompleteness = hooks.completeness - buildCatalogValidateRegistry = hooks.registry - buildCatalogValidateInterfaces = hooks.interfaces - buildCatalogValidateAgentMetadata = hooks.agentMetadata - buildCatalogValidateProvenance = hooks.provenance - buildCatalogValidateDelivery = hooks.delivery - buildCatalogValidateFinalCompleteness = hooks.finalCompleteness - loadCatalogValidateInterfaces = hooks.loadInterfaces - loadCatalogValidateProvenance = hooks.loadProvenance - loadCatalogValidateAgentMetadata = hooks.loadAgentMetadata -} - -func installCatalogBuildNoops() { - buildCatalogValidateParameterBindings = func(BoundCommandRegistry, SchemaRegistry) error { return nil } - buildCatalogValidateDryRun = func(SchemaRegistry) error { return nil } - buildCatalogValidateExamples = func(BoundCommandRegistry, SchemaRegistry) (ManualAgentExampleExecutionPlan, error) { - return ManualAgentExampleExecutionPlan{}, nil - } - buildCatalogValidateCompleteness = func(*cobra.Command, BoundCommandRegistry) error { return nil } - buildCatalogValidateRegistry = func(SchemaRegistry, EffectiveCommandRegistry) error { return nil } - buildCatalogValidateInterfaces = func(SchemaRegistry) error { return nil } - buildCatalogValidateAgentMetadata = func(SchemaRegistry) error { return nil } - buildCatalogValidateProvenance = func(SchemaRegistry) error { return nil } - buildCatalogValidateDelivery = func(SchemaRegistry, SchemaCatalogSnapshot) error { return nil } - buildCatalogValidateFinalCompleteness = func(*cobra.Command, BoundCommandRegistry, SchemaCatalogSnapshot) error { return nil } -} - -func installCatalogLoadNoops() { - loadCatalogValidateInterfaces = func(SchemaRegistry) error { return nil } - loadCatalogValidateProvenance = func(SchemaRegistry) error { return nil } - loadCatalogValidateAgentMetadata = func(SchemaRegistry) error { return nil } -} diff --git a/internal/cli/schema_cobra_binding.go b/internal/cli/schema_cobra_binding.go index b1dfd5c6..99d4eef0 100644 --- a/internal/cli/schema_cobra_binding.go +++ b/internal/cli/schema_cobra_binding.go @@ -159,14 +159,6 @@ func BindEffectiveCommandRegistry(root *cobra.Command, effective EffectiveComman for _, alias := range item.Aliases { bound.ByCLIPath[alias] = item } - // Migrate reviewed hint facts onto undeclared leaves without replacing - // their Execute/RunE bodies. Already-declared ContractFinal (e.g. LeafSpec - // Schema) wins and is left untouched — execution stays the control variable. - if !HasRuntimeContractFinal(item.PrimaryCommand) { - if decl, ok := lookupSchemaHintDecl(item.CanonicalPath); ok { - attachSchemaHintDecl(item.PrimaryCommand, decl) - } - } // Index Contract-declared dry_run capabilities at bind time: every // process that resolves the command tree gets the reviewed set, not // only processes that also run Schema assembly. @@ -569,14 +561,6 @@ func effectiveCompatibilityFlagContracts(command *cobra.Command, canonicalPath s // binding order-independent while still exposing path-specific drift. func effectiveCompatibilityFlagAnnotations(flag *pflag.Flag, bindings map[string]string, metadata RuntimeSchemaParameterMetadata) map[string][]string { annotations := cloneCompatibilityFlagAnnotations(flag.Annotations) - // Reviewed Manual Schema hints are canonical ToolSpec projection inputs, - // not executable facts owned by each Cobra path. They are intentionally - // attached to and resolved from the primary command once; a compatibility - // leaf remains a navigation view of that same ToolSpec. Keep every other - // native/typed annotation in the equivalence check so real command drift - // still fails closed. - delete(annotations, runtimeSchemaManualParameterAnnotation) - delete(annotations, runtimeSchemaManualReasonAnnotation) if len(annotations) == 0 { annotations = nil } @@ -898,11 +882,5 @@ func validateCommandRegistryAnnotation(command *cobra.Command, path string, spec return fmt.Errorf("schema command registry %s path %q conflicts with native annotation %s", spec.CanonicalPath, path, nativeCanonical) } } - if manualProduct, manualTool, _, ok := runtimeManualSchemaIdentity(command); ok { - manualCanonical := strings.TrimSpace(manualProduct + "." + manualTool) - if manualCanonical != spec.CanonicalPath { - return fmt.Errorf("schema command registry %s path %q conflicts with reviewed manual identity %s", spec.CanonicalPath, path, manualCanonical) - } - } return nil } diff --git a/internal/cli/schema_cobra_binding_coverage_more_test.go b/internal/cli/schema_cobra_binding_coverage_more_test.go index 2c636c29..905cc783 100644 --- a/internal/cli/schema_cobra_binding_coverage_more_test.go +++ b/internal/cli/schema_cobra_binding_coverage_more_test.go @@ -160,9 +160,7 @@ func TestCrossPlatformCoverageCompatibilityLeafContractDependencyAndMetadataEdge command.Flags().String("id", "", "id") flag := command.Flags().Lookup("id") flag.Annotations = map[string][]string{ - runtimeSchemaManualParameterAnnotation: {"remove"}, - runtimeSchemaManualReasonAnnotation: {"remove"}, - "keep": {"value"}, + "keep": {"value"}, } metadata := RuntimeSchemaParameterMetadata{ Required: []string{" id "}, @@ -361,9 +359,4 @@ func TestCrossPlatformCoverageCompatibilityPathAndHelperRemainingEdges(t *testin if err := validateCommandRegistryAnnotation(incomplete, "sample run", spec); err == nil || !strings.Contains(err.Error(), "incomplete native") { t.Fatalf("incomplete annotation error = %v", err) } - manual := &cobra.Command{} - annotateManualSchemaIdentity(manual, "other.run", "reason") - if err := validateCommandRegistryAnnotation(manual, "sample run", spec); err == nil || !strings.Contains(err.Error(), "manual identity") { - t.Fatalf("manual annotation error = %v", err) - } } diff --git a/internal/cli/schema_cobra_binding_test.go b/internal/cli/schema_cobra_binding_test.go deleted file mode 100644 index 2bc9b8d9..00000000 --- a/internal/cli/schema_cobra_binding_test.go +++ /dev/null @@ -1,1032 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package cli - -import ( - "reflect" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -// TestBindEffectiveCommandRegistryIndexesDeclaredDryRun pins bind-time -// capability indexing: any process resolving the command tree gets the -// Contract-declared dry_run in the reviewed set, without running assembly. -func TestBindEffectiveCommandRegistryIndexesDeclaredDryRun(t *testing.T) { - clearDeclaredDryRunCapabilitiesForTest() - t.Cleanup(clearDeclaredDryRunCapabilitiesForTest) - - root := commandRegistryTestRoot("item get") - leaf := exactSchemaCommand(root, "item get") - RegisterRuntimeContractFinal(leaf, ContractFinalPayload{ - DryRun: &DryRunSpec{PreviewKind: DryRunPreviewInvocation}, - }) - t.Cleanup(func() { ClearRuntimeContractFinalForTest(leaf) }) - - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item get", - CanonicalPath: "item.get_item", - Reason: "Confirms the reviewed primary identity", - Reviewed: true, - }}, - } - effective, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err != nil { - t.Fatalf("buildEffectiveCommandRegistry() error = %v", err) - } - if _, err := BindEffectiveCommandRegistry(root, effective); err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - capabilities, err := loadReviewedDryRunCapabilities() - if err != nil { - t.Fatal(err) - } - spec, ok := capabilities["item.get_item"] - if !ok || spec.PreviewKind != DryRunPreviewInvocation { - t.Fatalf("declared dry-run capability after bind = %#v, ok=%v", spec, ok) - } -} - -func TestBuildEffectiveCommandRegistryMergesReviewedManualCommands(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy", "helper add") - annotateTestCompatibilityPair(exactSchemaCommand(root, "item get"), exactSchemaCommand(root, "item legacy")) - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{ - { - CLIPath: "item get", - CanonicalPath: "item.get_item", - Reason: "Confirms the reviewed primary identity", - Reviewed: true, - }, - { - CLIPath: "item legacy", - CanonicalPath: "item.get_item", - Reason: "Reviewed compatibility alias", - Reviewed: true, - }, - { - CLIPath: "helper add", - CanonicalPath: "helper.add_helper", - Reason: "Reviewed existing helper command", - Reviewed: true, - }, - }, - } - - effective, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err != nil { - t.Fatalf("buildEffectiveCommandRegistry() error = %v", err) - } - if got := len(effective.Commands); got != 2 { - t.Fatalf("command count = %d, want 2", got) - } - item := effective.ByCanonical["item.get_item"] - if strings.Join(item.Aliases, ",") != "item legacy" { - t.Fatalf("item aliases = %#v", item.Aliases) - } - helper := effective.ByCanonical["helper.add_helper"] - if helper.Source != "reviewed_manual_hint" || helper.ReviewReason != "Reviewed existing helper command" || helper.Visibility != SchemaVisibilityPublic { - t.Fatalf("manual helper source = %#v", helper) - } - if effective.SourceHash() == reviewed.SourceHash() { - t.Fatal("manual-only command was omitted from the effective registry hash") - } - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry(effective) error = %v", err) - } - if got := bound.ByCLIPath["helper add"].CanonicalPath; got != "helper.add_helper" { - t.Fatalf("manual-only bound canonical = %q", got) - } - registry, err := AssembleSchemaRegistryFromBound(bound) - if err != nil { - t.Fatalf("AssembleSchemaRegistryFromBound(effective) error = %v", err) - } - index, err := registry.Index() - if err != nil { - t.Fatalf("manual-only registry index error = %v", err) - } - if _, ok := index.Resolve("helper.add_helper"); !ok { - t.Fatal("manual-only command was dropped before final SchemaRegistry delivery") - } - snapshot, err := registry.ToSnapshotPayload() - if err != nil { - t.Fatalf("manual-only snapshot serialization error = %v", err) - } - if _, ok := snapshot.Tools["helper.add_helper"]; !ok { - t.Fatal("manual-only command was dropped from the Catalog full-tool projection") - } -} - -func TestBuildEffectiveCommandRegistryRejectsManualIdentityConflict(t *testing.T) { - root := commandRegistryTestRoot("item get") - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item get", - CanonicalPath: "item.delete_item", - Reason: "Conflict fixture", - Reviewed: true, - }}, - } - - _, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err == nil || !strings.Contains(err.Error(), "conflicts with command registry canonical path") { - t.Fatalf("error = %v", err) - } -} - -func TestBuildEffectiveCommandRegistryRejectsPhantomManualCommand(t *testing.T) { - root := commandRegistryTestRoot("item get") - reviewed := mustCommandRegistry(t, nil) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item missing", - CanonicalPath: "item.missing_item", - Reason: "Phantom fixture", - Reviewed: true, - }}, - } - - _, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err == nil || !strings.Contains(err.Error(), "does not resolve to an existing Cobra command") { - t.Fatalf("error = %v", err) - } -} - -func TestBuildEffectiveCommandRegistryRejectsManualAliasCreation(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item legacy", - CanonicalPath: "item.get_item", - Reason: "Alias must be reviewed in the registry", - Reviewed: true, - }}, - } - - _, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err == nil || !strings.Contains(err.Error(), "cannot create an alias") { - t.Fatalf("error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryResolvesCompatibilityAliasLeaf(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy", "compat hidden") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - annotateTestCompatibilityPair(primary, alias) - hidden := exactSchemaCommand(root, "compat hidden") - hidden.Hidden = true - AttachRuntimeSchema(primary, "item", "get_item", "test") - AttachRuntimeSchema(alias, "item", "get_item", "test") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{ - { - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }, - { - CanonicalPath: "compat.hidden_helper", - PrimaryCLIPath: "compat hidden", - }, - }) - - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - item := bound.ByCanonical["item.get_item"] - if item.PrimaryCommand != primary || len(item.AliasCommands) != 1 || item.AliasCommands[0].Command != alias { - t.Fatalf("item binding = %#v", item) - } - if item.AliasCommands[0].Path != "item legacy" || item.AliasCommands[0].Kind != AliasKindCompatibilityLeaf { - t.Fatalf("compatibility alias binding = %#v", item.AliasCommands[0]) - } - if bound.ByCanonical["compat.hidden_helper"].PrimaryCommand != hidden { - t.Fatal("explicit reviewed hidden runnable leaf was not bound") - } -} - -func TestBindEffectiveCommandRegistryAcceptsEquivalentCompatibilityLeafContract(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - annotateTestCompatibilityPair(primary, alias) - primary.Use = "get " - alias.Use = "legacy " - argsValidator := cobra.ExactArgs(1) - primary.Args = argsValidator - alias.Args = argsValidator - primary.Flags().String("query", "all", "query expression (必填)") - alias.Flags().String("query", "all", "query expression (必填)") - if err := primary.MarkFlagRequired("query"); err != nil { - t.Fatalf("mark primary required: %v", err) - } - if err := alias.MarkFlagRequired("query"); err != nil { - t.Fatalf("mark alias required: %v", err) - } - AnnotateRuntimeFlagRequiredWhen(primary, "query", "item-id is present") - AnnotateRuntimeFlagRequiredWhen(alias, "query", "item-id is present") - positionals := []RuntimeSchemaPositional{{Name: "item-id", Type: "string", Required: true, Index: 0}} - AnnotateRuntimePositionals(primary, positionals...) - AnnotateRuntimePositionals(alias, positionals...) - constraints := RuntimeSchemaConstraints{RequireTogether: [][]string{{"item-id", "query"}}} - AnnotateRuntimeConstraints(primary, constraints) - AnnotateRuntimeConstraints(alias, constraints) - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - got := bound.ByCanonical["item.get_item"].AliasCommands[0] - if got.Kind != AliasKindCompatibilityLeaf || got.Command != alias { - t.Fatalf("compatibility alias = %#v", got) - } -} - -func TestBindEffectiveCommandRegistryRejectsDifferentCompatibilityHandlersWithoutTypedReview(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - primary.Run = nil - alias.Run = nil - primary.RunE = compatibilityPrimaryRunE - alias.RunE = compatibilityAliasRunE - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), "execution handler implementation differs for RunE") { - t.Fatalf("handler mismatch error = %v", err) - } - if !strings.Contains(err.Error(), "distinct canonical tools") { - t.Fatalf("handler mismatch error is not actionable: %v", err) - } -} - -func TestBindEffectiveCommandRegistryRejectsIndependentLeafWithoutTypedReviewEvenWithSameHandler(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), "independent compatibility leaves require the same reviewed typed compatibility equivalence") { - t.Fatalf("missing compatibility review error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryAcceptsDifferentHandlersWithMatchingTypedReview(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - primary.Run = nil - alias.Run = nil - primary.RunE = compatibilityPrimaryRunE - alias.RunE = compatibilityAliasRunE - AnnotateRuntimeCompatibilityEquivalence(primary, alias, RuntimeCompatibilityEquivalence{ - ID: "item-get-legacy-v1", - Reason: "The compatibility wrapper adds presentation-only behavior before invoking the exact primary operation.", - Reviewed: true, - }) - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - if _, err := BindEffectiveCommandRegistry(root, effective); err != nil { - t.Fatalf("reviewed handler equivalence was rejected: %v", err) - } -} - -func TestAnnotateRuntimeCompatibilityEquivalenceRejectsConflictingExistingReview(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy", "item older") - primary := exactSchemaCommand(root, "item get") - legacy := exactSchemaCommand(root, "item legacy") - older := exactSchemaCommand(root, "item older") - AnnotateRuntimeCompatibilityEquivalence(primary, legacy, RuntimeCompatibilityEquivalence{ - ID: "item-get-legacy-v1", Reason: "Reviewed first compatibility contract.", Reviewed: true, - }) - - defer func() { - if recovered := recover(); recovered == nil { - t.Fatal("conflicting compatibility review silently overwrote the existing marker") - } - }() - AnnotateRuntimeCompatibilityEquivalence(primary, older, RuntimeCompatibilityEquivalence{ - ID: "item-get-older-v1", Reason: "Conflicting compatibility contract.", Reviewed: true, - }) -} - -func compatibilityPrimaryRunE(*cobra.Command, []string) error { return nil } - -func compatibilityAliasRunE(*cobra.Command, []string) error { return nil } - -func TestBindEffectiveCommandRegistryIgnoresLazilyMaterializedHelpFlag(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - annotateTestCompatibilityPair(primary, alias) - // Cobra does this only for the command selected by Execute. Binding after - // parsing must remain independent of whether the primary or compatibility - // path was selected first. - primary.InitDefaultHelpFlag() - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - if _, err := BindEffectiveCommandRegistry(root, effective); err != nil { - t.Fatalf("lazy Cobra --help flag changed compatibility contract: %v", err) - } -} - -func TestBindEffectiveCommandRegistryCompatibilityGateIsOrderIndependentOfCanonicalMetadataMaterialization(t *testing.T) { - const canonical = "compat.get_item" - previousMetadata, hadMetadata := runtimeSchemaParameterMetadataByCanonical[canonical] - previousConstraints, hadConstraints := runtimeSchemaConstraintsByCanonical[canonical] - t.Cleanup(func() { - if hadMetadata { - runtimeSchemaParameterMetadataByCanonical[canonical] = previousMetadata - } else { - delete(runtimeSchemaParameterMetadataByCanonical, canonical) - } - if hadConstraints { - runtimeSchemaConstraintsByCanonical[canonical] = previousConstraints - } else { - delete(runtimeSchemaConstraintsByCanonical, canonical) - } - }) - runtimeSchemaParameterMetadataByCanonical[canonical] = RuntimeSchemaParameterMetadata{ - RequiredWhen: map[string]string{"query": "item-id is present"}, - Formats: map[string]string{"query": "typed-format"}, - Examples: map[string]string{"query": "open"}, - Enums: map[string][]string{"query": {"open", "closed"}}, - } - runtimeSchemaConstraintsByCanonical[canonical] = RuntimeSchemaConstraints{ - RequireTogether: [][]string{{"item-id", "query"}}, - } - - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - annotateTestCompatibilityPair(primary, alias) - primary.Flags().String("query", "", "query") - alias.Flags().String("query", "", "query") - AnnotateRuntimeFlagFormat(primary, "query", "native-format") - AnnotateRuntimeFlagFormat(alias, "query", "native-format") - AnnotateRuntimeFlagExample(primary, "query", "native-example") - AnnotateRuntimeFlagExample(alias, "query", "native-example") - AnnotateRuntimeFlagEnum(primary, "query", "native-a", "native-b") - AnnotateRuntimeFlagEnum(alias, "query", "native-a", "native-b") - // Simulate a previous Schema collection pass, which materializes canonical - // metadata only on the primary leaf. Binding must model the same canonical - // facts on both paths and remain deterministic on the next pass. - applyRuntimeSchemaParameterMetadata(primary, canonical) - AnnotateRuntimeConstraints(primary, runtimeSchemaConstraintsByCanonical[canonical]) - primaryFlag := primary.Flags().Lookup("query") - if got := firstFlagAnnotation(primaryFlag, "x-cli-format"); got != "native-format" { - t.Fatalf("typed metadata overwrote native format annotation: %q", got) - } - if got := firstFlagAnnotation(primaryFlag, runtimeSchemaFlagMetadataFormatAnnotation); got != "typed-format" { - t.Fatalf("typed format annotation = %q", got) - } - if got := firstFlagAnnotation(primaryFlag, runtimeSchemaFlagExampleAnnotation); got != "native-example" { - t.Fatalf("typed metadata overwrote native example annotation: %q", got) - } - if got := firstFlagAnnotation(primaryFlag, runtimeSchemaFlagMetadataExampleAnnotation); got != "open" { - t.Fatalf("typed example annotation = %q", got) - } - if got := runtimeFlagEnum(primaryFlag); !reflect.DeepEqual(got, []string{"native-a", "native-b"}) { - t.Fatalf("typed metadata overwrote native enum annotation: %#v", got) - } - if got := runtimeFlagEnumAnnotation(primaryFlag, runtimeSchemaFlagMetadataEnumAnnotation); !reflect.DeepEqual(got, []string{"open", "closed"}) { - t.Fatalf("typed enum annotation = %#v", got) - } - aliasAnnotations := effectiveCompatibilityFlagAnnotations(alias.Flags().Lookup("query"), nil, runtimeSchemaParameterMetadataByCanonical[canonical]) - if got := firstCompatibilityAnnotation(aliasAnnotations, "x-cli-format"); got != "native-format" { - t.Fatalf("compatibility overlay dropped native format annotation: %q", got) - } - if got := firstCompatibilityAnnotation(aliasAnnotations, runtimeSchemaFlagMetadataFormatAnnotation); got != "typed-format" { - t.Fatalf("compatibility overlay dropped typed format annotation: %q", got) - } - if got := aliasAnnotations["x-cli-enum"]; !reflect.DeepEqual(got, []string{"native-a", "native-b"}) { - t.Fatalf("compatibility overlay dropped native enum annotation: %#v", got) - } - if got := aliasAnnotations[runtimeSchemaFlagMetadataEnumAnnotation]; !reflect.DeepEqual(got, []string{"open", "closed"}) { - t.Fatalf("compatibility overlay dropped typed enum annotation: %#v", got) - } - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: canonical, - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - if _, err := BindEffectiveCommandRegistry(root, effective); err != nil { - t.Fatalf("BindEffectiveCommandRegistry() after canonical metadata materialization error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryTreatsManualParameterHintsAsCanonicalProjection(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - annotateTestCompatibilityPair(primary, alias) - primary.Flags().Bool("include-archived", false, "include archived items") - alias.Flags().Bool("include-archived", false, "include archived items") - - required := false - if err := annotateManualSchemaParameter( - primary, - "include-archived", - ManualSchemaParameterHint{Required: &required}, - "Reviewed canonical projection may lower the Agent-facing required value.", - ); err != nil { - t.Fatalf("annotateManualSchemaParameter() error = %v", err) - } - - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - if _, err := BindEffectiveCommandRegistry(root, effective); err != nil { - t.Fatalf("manual canonical projection must not create compatibility-leaf executable drift: %v", err) - } - if _, _, ok, err := runtimeManualSchemaParameter(alias, "include-archived"); err != nil || ok { - t.Fatalf("manual projection was copied to compatibility leaf: present=%t err=%v", ok, err) - } -} - -func TestBindEffectiveCommandRegistryRejectsCompatibilityLeafSemanticDrift(t *testing.T) { - tests := []struct { - name string - want string - mutate func(primary, alias *cobra.Command) - }{ - { - name: "flag surface", - want: "flag --query is missing", - mutate: func(primary, _ *cobra.Command) { - primary.Flags().String("query", "", "query") - }, - }, - { - name: "flag type", - want: "flag --query type differs", - mutate: func(primary, alias *cobra.Command) { - primary.Flags().String("query", "", "query") - alias.Flags().Int("query", 0, "query") - }, - }, - { - name: "flag default", - want: "flag --limit default differs", - mutate: func(primary, alias *cobra.Command) { - primary.Flags().Int("limit", 20, "limit") - alias.Flags().Int("limit", 50, "limit") - }, - }, - { - name: "flag hidden", - want: "flag --query hidden state differs", - mutate: func(primary, alias *cobra.Command) { - primary.Flags().String("query", "", "query") - alias.Flags().String("query", "", "query") - _ = alias.Flags().MarkHidden("query") - }, - }, - { - name: "flag required", - want: "required/required_when facts differ", - mutate: func(primary, alias *cobra.Command) { - primary.Flags().String("query", "", "query") - alias.Flags().String("query", "", "query") - _ = primary.MarkFlagRequired("query") - }, - }, - { - name: "flag required when", - want: "required/required_when facts differ", - mutate: func(primary, alias *cobra.Command) { - primary.Flags().String("query", "", "query") - alias.Flags().String("query", "", "query") - AnnotateRuntimeFlagRequiredWhen(primary, "query", "mode is search") - AnnotateRuntimeFlagRequiredWhen(alias, "query", "mode is list") - }, - }, - { - name: "persistent behavior", - want: "local/persistent/inherited behavior differs", - mutate: func(primary, alias *cobra.Command) { - primary.PersistentFlags().String("scope", "", "scope") - alias.Flags().String("scope", "", "scope") - }, - }, - { - name: "Args", - want: "Args validator implementation differs", - mutate: func(primary, alias *cobra.Command) { - primary.Args = cobra.NoArgs - alias.Args = cobra.MaximumNArgs(1) - }, - }, - { - name: "positionals", - want: "runtime positionals differ", - mutate: func(primary, alias *cobra.Command) { - AnnotateRuntimePositionals(primary, RuntimeSchemaPositional{Name: "item-id", Required: true, Index: 0}) - AnnotateRuntimePositionals(alias, RuntimeSchemaPositional{Name: "item-id", Required: false, Index: 0}) - }, - }, - { - name: "constraints", - want: "runtime constraints differ", - mutate: func(primary, alias *cobra.Command) { - AnnotateRuntimeConstraints(primary, RuntimeSchemaConstraints{RequireOneOf: [][]string{{"query", "item-id"}}}) - AnnotateRuntimeConstraints(alias, RuntimeSchemaConstraints{RequireTogether: [][]string{{"query", "item-id"}}}) - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - root := commandRegistryTestRoot("item get", "item legacy") - primary := exactSchemaCommand(root, "item get") - alias := exactSchemaCommand(root, "item legacy") - alias.Hidden = true - test.mutate(primary, alias) - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item legacy"}, - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), test.want) { - t.Fatalf("error = %v, want %q", err, test.want) - } - if !strings.Contains(err.Error(), "item.get_item") || !strings.Contains(err.Error(), `compatibility leaf alias "item legacy"`) { - t.Fatalf("error is not actionable: %v", err) - } - }) - } -} - -func TestBindEffectiveCommandRegistryResolvesAncestorCobraAlias(t *testing.T) { - root := commandRegistryTestRoot("item action get") - group := exactSchemaCommand(root, "item action") - group.Aliases = []string{"ops"} - primary := exactSchemaCommand(root, "item action get") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item action get", - Aliases: []string{"item ops get"}, - }}) - - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - alias := bound.ByCanonical["item.get_item"].AliasCommands[0] - if alias.Command != primary || alias.Kind != AliasKindCobraAlias { - t.Fatalf("ancestor Cobra alias binding = %#v", alias) - } -} - -func TestBindEffectiveCommandRegistryRejectsAmbiguousCobraAlias(t *testing.T) { - root := commandRegistryTestRoot("item get", "item list") - exactSchemaCommand(root, "item get").Aliases = []string{"fetch"} - exactSchemaCommand(root, "item list").Aliases = []string{"fetch"} - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item fetch"}, - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), `cobra alias segment "fetch" is ambiguous`) { - t.Fatalf("error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryPrefersExactNameOverCobraAlias(t *testing.T) { - root := commandRegistryTestRoot("item get", "item fetch") - primary := exactSchemaCommand(root, "item get") - compatibility := exactSchemaCommand(root, "item fetch") - primary.Aliases = []string{"fetch"} - compatibility.Hidden = true - annotateTestCompatibilityPair(primary, compatibility) - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item fetch"}, - }}) - - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - alias := bound.ByCanonical["item.get_item"].AliasCommands[0] - if alias.Command != compatibility || alias.Kind != AliasKindCompatibilityLeaf { - t.Fatalf("name-priority alias binding = %#v", alias) - } -} - -func TestBindEffectiveCommandRegistryResolvesCobraAliasToPrimaryCommand(t *testing.T) { - root := commandRegistryTestRoot("item get") - primary := exactSchemaCommand(root, "item get") - primary.Aliases = []string{"fetch"} - AttachRuntimeSchema(primary, "item", "get_item", "test") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item fetch"}, - }}) - - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - alias := bound.ByCanonical["item.get_item"].AliasCommands[0] - if alias.Path != "item fetch" || alias.Command != primary || alias.Kind != AliasKindCobraAlias { - t.Fatalf("Cobra alias binding = %#v", alias) - } - if got := bound.ByCLIPath["item fetch"].CanonicalPath; got != "item.get_item" { - t.Fatalf("alias canonical = %q", got) - } -} - -func TestBindEffectiveCommandRegistryRejectsCobraAliasToDifferentPrimary(t *testing.T) { - root := commandRegistryTestRoot("item get", "item list") - list := exactSchemaCommand(root, "item list") - list.Aliases = []string{"fetch"} - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item fetch"}, - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), "resolves to a different command") { - t.Fatalf("error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryRejectsAliasAsPrimaryPath(t *testing.T) { - root := commandRegistryTestRoot("item get") - exactSchemaCommand(root, "item get").Aliases = []string{"fetch"} - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item fetch", - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), "primary path") || !strings.Contains(err.Error(), "real Cobra command names") { - t.Fatalf("error = %v", err) - } -} - -func TestBuildEffectiveCommandRegistryResolvesManualAliasBeforeRegistryPolicy(t *testing.T) { - root := commandRegistryTestRoot("item action get") - exactSchemaCommand(root, "item action").Aliases = []string{"ops"} - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item action get", - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item ops get", - CanonicalPath: "item.get_item", - Reason: "Reviewed ancestor Cobra alias", - Reviewed: true, - }}, - } - - _, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err == nil || !strings.Contains(err.Error(), "cannot create an alias") { - t.Fatalf("error = %v", err) - } -} - -func TestBuildEffectiveCommandRegistryRejectsAmbiguousManualAlias(t *testing.T) { - root := commandRegistryTestRoot("item get", "item list") - exactSchemaCommand(root, "item get").Aliases = []string{"fetch"} - exactSchemaCommand(root, "item list").Aliases = []string{"fetch"} - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item fetch", - CanonicalPath: "item.get_item", - Reason: "Ambiguous alias fixture", - Reviewed: true, - }}, - } - - _, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err == nil || !strings.Contains(err.Error(), `cobra alias segment "fetch" is ambiguous`) { - t.Fatalf("error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryRejectsStaleRegistryPath(t *testing.T) { - root := commandRegistryTestRoot("item get") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.delete_item", - PrimaryCLIPath: "item delete", - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), "stale cli path") { - t.Fatalf("error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryDoesNotPrefixMatch(t *testing.T) { - root := commandRegistryTestRoot("item get") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item ge", - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), `stale cli path "item ge"`) { - t.Fatalf("error = %v", err) - } -} - -func TestBindEffectiveCommandRegistryRejectsNativeAnnotationConflict(t *testing.T) { - root := commandRegistryTestRoot("item get") - leaf := exactSchemaCommand(root, "item get") - AttachRuntimeSchema(leaf, "item", "delete_item", "test") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - - _, err := BindEffectiveCommandRegistry(root, effective) - if err == nil || !strings.Contains(err.Error(), "conflicts with native annotation item.delete_item") { - t.Fatalf("error = %v", err) - } -} - -func TestCommandRegistryRejectsAliasCollision(t *testing.T) { - _, err := newEffectiveCommandRegistry([]CommandSpec{ - { - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - Aliases: []string{"item shared"}, - }, - { - CanonicalPath: "item.delete_item", - PrimaryCLIPath: "item delete", - Aliases: []string{"item shared"}, - }, - }) - if err == nil || !strings.Contains(err.Error(), "belongs to both") { - t.Fatalf("error = %v", err) - } -} - -func TestCommandRegistryDefaultsAndValidatesReviewedVisibility(t *testing.T) { - registry := mustEffectiveCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - if got := registry.ByCanonical["item.get_item"].Visibility; got != SchemaVisibilityPublic { - t.Fatalf("default visibility = %q, want public", got) - } - - _, err := newEffectiveCommandRegistry([]CommandSpec{{ - CanonicalPath: "item.delete_item", - PrimaryCLIPath: "item delete", - Visibility: SchemaVisibility("unreviewed"), - }}) - if err == nil || !strings.Contains(err.Error(), "invalid visibility") { - t.Fatalf("invalid visibility error = %v", err) - } -} - -func TestAssemblerUsesReviewedCommandVisibility(t *testing.T) { - root := commandRegistryTestRoot("doc-comment run", "item internal") - effective := mustEffectiveCommandRegistry(t, []CommandSpec{ - { - CanonicalPath: "doc-comment.run", - PrimaryCLIPath: "doc-comment run", - Visibility: SchemaVisibilityPublic, - }, - { - CanonicalPath: "item.internal", - PrimaryCLIPath: "item internal", - Visibility: SchemaVisibilityInternal, - }, - }) - bound, err := BindEffectiveCommandRegistry(root, effective) - if err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - registry, err := AssembleSchemaRegistryFromBound(bound) - if err != nil { - t.Fatalf("AssembleSchemaRegistryFromBound() error = %v", err) - } - index, err := registry.Index() - if err != nil { - t.Fatalf("SchemaRegistry.Index() error = %v", err) - } - if _, ok := index.Resolve("doc-comment.run"); !ok { - t.Fatal("reviewed public command was filtered by legacy product visibility") - } - if _, ok := index.Resolve("item.internal"); ok { - t.Fatal("reviewed internal command was emitted by the assembler") - } -} - -func TestCommandRegistrySourceValidationIsFailClosed(t *testing.T) { - // After splitting registry into per-product shards, tests use the merged - // JSON (reassembled from shards) for byte-level operations. - merged, err := EmbeddedCommandRegistryMergedJSON() - if err != nil { - t.Fatalf("EmbeddedCommandRegistryMergedJSON() error = %v", err) - } - registry, err := ValidateCommandRegistrySource(merged) - if err != nil { - t.Fatalf("ValidateCommandRegistrySource(embedded) error = %v", err) - } - hash, err := EmbeddedCommandRegistrySourceHash() - if err != nil { - t.Fatalf("EmbeddedCommandRegistrySourceHash() error = %v", err) - } - if hash == "" || hash != registry.SourceHash() { - t.Fatalf("embedded hash = %q, decoded hash = %q", hash, registry.SourceHash()) - } - - // Match both pretty-printed ("cli_path": "...") and compact ("cli_path":"...") JSON. - drifted := strings.Replace(string(merged), `"aisearch person"`, `"aisearch people"`, 1) - if drifted == string(merged) { - t.Fatal("test fixture did not mutate embedded registry") - } - _, err = ValidateCommandRegistrySource([]byte(drifted)) - if err == nil || !strings.Contains(err.Error(), "disagrees with the embedded reviewed registry") { - t.Fatalf("drift error = %v", err) - } - - // Test unknown-field rejection: inject an unrecognized top-level key. - unknownField := strings.Replace(string(merged), `"version":1`, `"version":1,"unreviewed":true`, 1) - if unknownField == string(merged) { - // Try with space (pretty-printed format). - unknownField = strings.Replace(string(merged), `"version": 1`, `"version": 1, "unreviewed": true`, 1) - } - _, err = ValidateCommandRegistrySource([]byte(unknownField)) - if err == nil || !strings.Contains(err.Error(), "unknown field") { - t.Fatalf("unknown-field error = %v", err) - } -} - -func commandRegistryTestRoot(paths ...string) *cobra.Command { - root := &cobra.Command{Use: "dws"} - groups := map[string]*cobra.Command{} - for _, path := range paths { - parts := strings.Fields(path) - current := root - prefix := "" - for idx, part := range parts { - if prefix == "" { - prefix = part - } else { - prefix += " " + part - } - if idx == len(parts)-1 { - current.AddCommand(&cobra.Command{Use: part, Run: func(*cobra.Command, []string) {}}) - continue - } - next := groups[prefix] - if next == nil { - next = &cobra.Command{Use: part} - groups[prefix] = next - current.AddCommand(next) - } - current = next - } - } - return root -} - -func mustCommandRegistry(t *testing.T, commands []CommandSpec) CommandRegistry { - t.Helper() - registry, err := newCommandRegistry(commands) - if err != nil { - t.Fatalf("newCommandRegistry() error = %v", err) - } - return registry -} - -func mustEffectiveCommandRegistry(t *testing.T, commands []CommandSpec) EffectiveCommandRegistry { - t.Helper() - registry, err := newEffectiveCommandRegistry(commands) - if err != nil { - t.Fatalf("newEffectiveCommandRegistry() error = %v", err) - } - return registry -} - -func annotateTestCompatibilityPair(primary, alias *cobra.Command) { - AnnotateRuntimeCompatibilityEquivalence(primary, alias, RuntimeCompatibilityEquivalence{ - ID: "test-compatibility-pair-v1", - Reason: "The focused test explicitly reviews these independently registered leaves as semantically equivalent.", - Reviewed: true, - }) -} - -// TestCrossPlatformCoverageBindEffectiveCommandRegistryAttachesResidualHintDecl pins the bind-time -// migration path: an undeclared leaf whose canonical still has a compiled -// schema_hints declaration receives ContractFinal without replacing RunE. -func TestCrossPlatformCoverageBindEffectiveCommandRegistryAttachesResidualHintDecl(t *testing.T) { - root := commandRegistryTestRoot("item get") - leaf := exactSchemaCommand(root, "item get") - if leaf == nil { - t.Fatal("missing item get leaf") - } - t.Cleanup(func() { ClearRuntimeContractFinalForTest(leaf) }) - - schemaHintDeclsByCanonical["item.get_item"] = testSchemaHintDeclFixture() - t.Cleanup(func() { delete(schemaHintDeclsByCanonical, "item.get_item") }) - - reviewed := mustCommandRegistry(t, []CommandSpec{{ - CanonicalPath: "item.get_item", - PrimaryCLIPath: "item get", - }}) - manual := ManualSchemaHintSnapshot{ - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "item get", - CanonicalPath: "item.get_item", - Reason: "Confirms the reviewed primary identity", - Reviewed: true, - }}, - } - effective, err := buildEffectiveCommandRegistry(root, reviewed, manual) - if err != nil { - t.Fatalf("buildEffectiveCommandRegistry() error = %v", err) - } - if _, err := BindEffectiveCommandRegistry(root, effective); err != nil { - t.Fatalf("BindEffectiveCommandRegistry() error = %v", err) - } - if !HasRuntimeContractFinal(leaf) { - t.Fatal("residual hint decl must attach ContractFinal at bind time") - } - final, ok := RuntimeContractFinal(leaf) - if !ok || final.Description != testSchemaHintDeclFixture().Description { - t.Fatalf("attached ContractFinal = %#v ok=%v", final, ok) - } -} diff --git a/internal/cli/schema_command_registry.go b/internal/cli/schema_command_registry.go index 7419d76c..8bf570a1 100644 --- a/internal/cli/schema_command_registry.go +++ b/internal/cli/schema_command_registry.go @@ -95,7 +95,6 @@ var ( embeddedSchemaCommandRegistryErr error loadReviewedCommandRegistry = loadEmbeddedCommandRegistry validateReviewedParameterBindings = ValidateEmbeddedSchemaParameterBindings - loadReviewedManualSchemaHints = embeddedManualSchemaHints ) func loadEmbeddedCommandRegistry() (CommandRegistry, error) { @@ -496,11 +495,9 @@ func normalizeCommandAliases(aliases []string, primary string) []string { return sortedUniqueStrings(normalized) } -// BuildEffectiveCommandRegistry loads the reviewed registry and merges the -// embedded manual additions. Manual entries may only name an exact existing -// public runnable leaf. They may add a new command identity, but can never -// rewrite an existing registry identity or create an alias; aliases belong in -// the reviewed base CommandRegistry. +// BuildEffectiveCommandRegistry loads the reviewed CommandRegistry. Manual +// Schema hint overlays are retired; every public Schema identity must already +// exist in the reviewed registry. func BuildEffectiveCommandRegistry(root *cobra.Command) (EffectiveCommandRegistry, error) { if root == nil { return EffectiveCommandRegistry{}, fmt.Errorf("build effective Schema command registry: root is nil") @@ -512,94 +509,14 @@ func BuildEffectiveCommandRegistry(root *cobra.Command) (EffectiveCommandRegistr if err != nil { return EffectiveCommandRegistry{}, err } - manual, err := loadReviewedManualSchemaHints() - if err != nil { - return EffectiveCommandRegistry{}, err - } - return buildEffectiveCommandRegistry(root, reviewed, manual) + return buildEffectiveCommandRegistry(root, reviewed) } -func buildEffectiveCommandRegistry(root *cobra.Command, reviewed CommandRegistry, manual ManualSchemaHintSnapshot) (EffectiveCommandRegistry, error) { +func buildEffectiveCommandRegistry(root *cobra.Command, reviewed CommandRegistry) (EffectiveCommandRegistry, error) { if root == nil { return EffectiveCommandRegistry{}, fmt.Errorf("build effective Schema command registry: root is nil") } - if manual.Version != manualSchemaHintVersion { - return EffectiveCommandRegistry{}, fmt.Errorf("unsupported manual Schema hint version %d", manual.Version) - } - base, err := newCommandRegistry(reviewed.Commands) - if err != nil { - return EffectiveCommandRegistry{}, err - } - commands := append([]CommandSpec(nil), base.Commands...) - byCanonical := base.ByCanonical - byPath := base.ByCLIPath - seenManualPaths := map[string]bool{} - - for _, raw := range manual.Commands { - path := normalizeSchemaCLIPath(raw.CLIPath) - canonical := strings.TrimSpace(raw.CanonicalPath) - reason := strings.TrimSpace(raw.Reason) - if path == "" || strings.ContainsAny(path, "*?[]") { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint has invalid exact cli_path %q", raw.CLIPath) - } - if seenManualPaths[path] { - return EffectiveCommandRegistry{}, fmt.Errorf("duplicate manual Schema hint for %q", path) - } - seenManualPaths[path] = true - if !raw.Reviewed || reason == "" { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q is not reviewed or has no reason", path) - } - productID, _, ok := splitManualSchemaCanonicalPath(canonical) - if !ok { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q has invalid canonical_path %q", path, canonical) - } - match, resolveErr := resolveExactCobraPath(root, path) - if resolveErr != nil { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q cannot be resolved exactly: %w", path, resolveErr) - } - command := match.Command - if command == nil { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q does not resolve to an existing Cobra command", path) - } - if !publicRunnableSchemaLeaf(command) { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q must target a public runnable Cobra leaf", path) - } - namePath := normalizeSchemaCLIPath(strings.Join(commandPathParts(command), " ")) - if match.UsedAlias { - nameSpec, nameExists := byPath[namePath] - if !nameExists { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q uses a Cobra alias, but real command path %q is not present in reviewed CommandRegistry", path, namePath) - } - if nameSpec.CanonicalPath != canonical { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with real command path %q canonical path %q", path, canonical, namePath, nameSpec.CanonicalPath) - } - } - - pathSpec, pathExists := byPath[path] - canonicalSpec, canonicalExists := byCanonical[canonical] - if pathExists && pathSpec.CanonicalPath != canonical { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with command registry canonical path %q", path, canonical, pathSpec.CanonicalPath) - } - if pathExists { - continue - } - if canonicalExists { - return EffectiveCommandRegistry{}, fmt.Errorf("manual Schema hint %q cannot create an alias for %s; add the alias to the reviewed CommandRegistry", path, canonicalSpec.CanonicalPath) - } - commands = append(commands, CommandSpec{ - CanonicalPath: canonical, - SourceProductID: productID, - PrimaryCLIPath: path, - Visibility: SchemaVisibilityPublic, - Source: "reviewed_manual_hint", - ReviewReason: reason, - }) - // Update the working indexes so later manual entries cannot collide. - added := commands[len(commands)-1] - byCanonical[canonical] = added - byPath[path] = added - } - return newEffectiveCommandRegistry(commands) + return newEffectiveCommandRegistry(reviewed.Commands) } func cloneCommandRegistry(registry CommandRegistry) CommandRegistry { diff --git a/internal/cli/schema_command_registry_test_helpers_test.go b/internal/cli/schema_command_registry_test_helpers_test.go new file mode 100644 index 00000000..f86f8658 --- /dev/null +++ b/internal/cli/schema_command_registry_test_helpers_test.go @@ -0,0 +1,53 @@ +package cli + +import ( + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func commandRegistryTestRoot(paths ...string) *cobra.Command { + root := &cobra.Command{Use: "dws"} + groups := map[string]*cobra.Command{} + for _, path := range paths { + parts := strings.Fields(path) + current, prefix := root, "" + for index, part := range parts { + if prefix == "" { + prefix = part + } else { + prefix += " " + part + } + if index == len(parts)-1 { + current.AddCommand(&cobra.Command{Use: part, Run: func(*cobra.Command, []string) {}}) + continue + } + next := groups[prefix] + if next == nil { + next = &cobra.Command{Use: part} + groups[prefix] = next + current.AddCommand(next) + } + current = next + } + } + return root +} + +func mustEffectiveCommandRegistry(t *testing.T, commands []CommandSpec) EffectiveCommandRegistry { + t.Helper() + registry, err := newEffectiveCommandRegistry(commands) + if err != nil { + t.Fatalf("newEffectiveCommandRegistry() error = %v", err) + } + return registry +} + +func annotateTestCompatibilityPair(primary, alias *cobra.Command) { + AnnotateRuntimeCompatibilityEquivalence(primary, alias, RuntimeCompatibilityEquivalence{ + ID: "test-compatibility-pair-v1", + Reason: "Focused test review confirms equivalent compatibility leaves.", + Reviewed: true, + }) +} diff --git a/internal/cli/schema_completeness.go b/internal/cli/schema_completeness.go index d51fe492..6c535302 100644 --- a/internal/cli/schema_completeness.go +++ b/internal/cli/schema_completeness.go @@ -35,7 +35,6 @@ type runtimeSchemaExclusionGroup struct { var ( completenessLoadExclusions = EmbeddedRuntimeSchemaExclusions - completenessApplyManual = ApplyEmbeddedManualSchemaHints completenessBuildEffective = BuildEffectiveCommandRegistry completenessBindEffective = BindEffectiveCommandRegistry completenessRuntimeReport = runtimeSchemaCompletenessFromBound @@ -92,9 +91,6 @@ func EmbeddedRuntimeSchemaExclusions() ([]RuntimeSchemaExclusion, error) { // ValidateEmbeddedRuntimeSchemaCompleteness enforces the reviewed reverse // command-tree contract used by generation and CI. func ValidateEmbeddedRuntimeSchemaCompleteness(root *cobra.Command) error { - if _, err := completenessApplyManual(root); err != nil { - return err - } effective, err := completenessBuildEffective(root) if err != nil { return err diff --git a/internal/cli/schema_completeness_coverage_more_test.go b/internal/cli/schema_completeness_coverage_more_test.go deleted file mode 100644 index 34be50aa..00000000 --- a/internal/cli/schema_completeness_coverage_more_test.go +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -package cli - -import ( - "errors" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestCrossPlatformCoverageEmbeddedRuntimeSchemaExclusionDecodeEdges(t *testing.T) { - previous := embeddedRuntimeSchemaExclusionsJSON - t.Cleanup(func() { embeddedRuntimeSchemaExclusionsJSON = previous }) - cases := []struct { - data string - want string - }{ - {data: "{", want: "decode"}, - {data: `{"version":2,"groups":[]}`, want: "unsupported"}, - {data: `{"version":1,"groups":[{"id":"","reason":"r","reviewed":true}]}`, want: "not reviewed"}, - {data: `{"version":1,"groups":[{"id":"g","reason":"r","reviewed":true,"commands":[" "]}]}`, want: "empty command"}, - {data: `{"version":1,"groups":[{"id":"g","reason":"r","reviewed":true,"commands":["a b","a b"]}]}`, want: "duplicate"}, - } - for _, tc := range cases { - embeddedRuntimeSchemaExclusionsJSON = []byte(tc.data) - if _, err := EmbeddedRuntimeSchemaExclusions(); err == nil || !strings.Contains(err.Error(), tc.want) { - t.Errorf("EmbeddedRuntimeSchemaExclusions(%s) error = %v, want %q", tc.data, err, tc.want) - } - } - embeddedRuntimeSchemaExclusionsJSON = previous -} - -func TestCrossPlatformCoverageCompletenessWrapperErrorBoundaries(t *testing.T) { - oldLoad := completenessLoadExclusions - oldApply := completenessApplyManual - oldBuild := completenessBuildEffective - oldBind := completenessBindEffective - oldRuntime := completenessRuntimeReport - oldDelivery := completenessDeliveryReport - oldCollect := completenessCollectEntries - t.Cleanup(func() { - completenessLoadExclusions = oldLoad - completenessApplyManual = oldApply - completenessBuildEffective = oldBuild - completenessBindEffective = oldBind - completenessRuntimeReport = oldRuntime - completenessDeliveryReport = oldDelivery - completenessCollectEntries = oldCollect - }) - root := &cobra.Command{Use: "dws"} - completenessApplyManual = func(*cobra.Command) (ManualSchemaHintReport, error) { - return ManualSchemaHintReport{}, errors.New("apply failed") - } - if err := ValidateEmbeddedRuntimeSchemaCompleteness(root); err == nil || !strings.Contains(err.Error(), "apply failed") { - t.Fatalf("apply error = %v", err) - } - completenessApplyManual = func(*cobra.Command) (ManualSchemaHintReport, error) { return ManualSchemaHintReport{}, nil } - completenessBuildEffective = func(*cobra.Command) (EffectiveCommandRegistry, error) { - return EffectiveCommandRegistry{}, errors.New("build failed") - } - if err := ValidateEmbeddedRuntimeSchemaCompleteness(root); err == nil || !strings.Contains(err.Error(), "build failed") { - t.Fatalf("build error = %v", err) - } - completenessBuildEffective = func(*cobra.Command) (EffectiveCommandRegistry, error) { return EffectiveCommandRegistry{}, nil } - completenessBindEffective = func(*cobra.Command, EffectiveCommandRegistry) (BoundCommandRegistry, error) { - return BoundCommandRegistry{}, errors.New("bind failed") - } - if err := ValidateEmbeddedRuntimeSchemaCompleteness(root); err == nil || !strings.Contains(err.Error(), "bind failed") { - t.Fatalf("bind error = %v", err) - } - - completenessLoadExclusions = func() ([]RuntimeSchemaExclusion, error) { return nil, errors.New("exclusions failed") } - if err := validateResolvedRuntimeSchemaCompleteness(root, BoundCommandRegistry{}); err == nil || !strings.Contains(err.Error(), "exclusions failed") { - t.Fatalf("resolved exclusions error = %v", err) - } - if err := validateResolvedSchemaCatalogDeliveryCompleteness(root, BoundCommandRegistry{}, SchemaCatalogSnapshot{}); err == nil || !strings.Contains(err.Error(), "exclusions failed") { - t.Fatalf("delivery exclusions error = %v", err) - } - completenessLoadExclusions = func() ([]RuntimeSchemaExclusion, error) { return nil, nil } - - reports := []struct { - report RuntimeSchemaCompletenessReport - want string - }{ - {report: RuntimeSchemaCompletenessReport{DeliveryErrors: []string{"delivery"}}, want: "invalid effective"}, - {report: RuntimeSchemaCompletenessReport{Missing: []string{"missing"}}, want: "missing from Schema"}, - {report: RuntimeSchemaCompletenessReport{InvalidExclusions: []string{"invalid"}}, want: "invalid runtime"}, - {report: RuntimeSchemaCompletenessReport{StaleExclusions: []string{"stale"}}, want: "stale runtime"}, - } - for _, tc := range reports { - completenessRuntimeReport = func(*cobra.Command, []RuntimeSchemaExclusion, BoundCommandRegistry) RuntimeSchemaCompletenessReport { - return tc.report - } - if err := validateResolvedRuntimeSchemaCompleteness(root, BoundCommandRegistry{}); err == nil || !strings.Contains(err.Error(), tc.want) { - t.Errorf("runtime report %#v error = %v, want %q", tc.report, err, tc.want) - } - } - - badSnapshot := SchemaCatalogSnapshot{Catalog: map[string]any{"bad": func() {}}} - if err := validateSchemaCatalogDeliveryCompletenessFromBound(root, BoundCommandRegistry{}, badSnapshot, nil); err == nil || !strings.Contains(err.Error(), "encode final") { - t.Fatalf("delivery marshal error = %v", err) - } - invalidSnapshot := SchemaCatalogSnapshot{Version: 999, Catalog: map[string]any{}, Tools: map[string]map[string]any{}} - if err := validateSchemaCatalogDeliveryCompletenessFromBound(root, BoundCommandRegistry{}, invalidSnapshot, nil); err == nil || !strings.Contains(err.Error(), "production loader") { - t.Fatalf("delivery loader error = %v", err) - } - validLoaded := deliveryCoverageLoaded(t) - for _, tc := range reports { - completenessDeliveryReport = func(*cobra.Command, loadedSchemaCatalog, []RuntimeSchemaExclusion, BoundCommandRegistry) RuntimeSchemaCompletenessReport { - return tc.report - } - err := validateSchemaCatalogDeliveryCompletenessFromBound(root, BoundCommandRegistry{}, validLoaded.Snapshot, nil) - if err == nil { - t.Errorf("delivery report %#v unexpectedly succeeded", tc.report) - } - } - - completenessCollectEntries = func(*cobra.Command) ([]runtimeSchemaEntry, error) { return nil, errors.New("collect failed") } - report := RuntimeSchemaCompleteness(root, nil) - if len(report.DeliveryErrors) != 1 || !strings.Contains(report.DeliveryErrors[0], "collect failed") { - t.Fatalf("collect report = %#v", report) - } -} - -func TestCrossPlatformCoverageCatalogDeliveryCompletenessLoadedIdentityEdges(t *testing.T) { - oldQuery := deliverySchemaPayload - oldResolve := deliveryIndexResolve - oldTool := deliveryToolPayload - t.Cleanup(func() { - deliverySchemaPayload = oldQuery - deliveryIndexResolve = oldResolve - deliveryToolPayload = oldTool - }) - reset := func() { - deliverySchemaPayload = oldQuery - deliveryIndexResolve = oldResolve - deliveryToolPayload = oldTool - } - root := commandRegistryTestRoot("sample category run", "extra leaf") - loaded := deliveryCoverageLoaded(t) - identities := map[string]runtimeSchemaResolvedIdentity{ - "sample category run": {CanonicalPath: "sample.run", Source: "expected-source"}, - "sample legacy execute": {CanonicalPath: "sample.run", Source: "expected-source"}, - } - - // An unreviewed public leaf has no expected identity and is left to the - // reverse-completeness report. - schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample category run" { - return nil, errors.New("query failed") - } - return oldQuery(loaded, args) - } - schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample category run" { - return map[string]any{"canonical_path": "other.run"}, nil - } - return oldQuery(loaded, args) - } - report := schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "public CLI path") { - t.Fatalf("wrong public canonical report = %#v", report) - } - reset() - deliveryIndexResolve = func(SchemaIndex, string) (ToolSpec, bool) { return ToolSpec{}, false } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "lost canonical") { - t.Fatalf("lost canonical report = %#v", report) - } - reset() - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "identity source") { - t.Fatalf("identity source report = %#v", report) - } - - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample.run" { - return nil, errors.New("canonical failed") - } - return oldQuery(loaded, args) - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "canonical \"sample.run\" is not queryable") { - t.Fatalf("canonical query report = %#v", report) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample.run" { - return map[string]any{"canonical_path": "other.run"}, nil - } - return oldQuery(loaded, args) - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "resolves to \"other.run\"") { - t.Fatalf("canonical mismatch report = %#v", report) - } - reset() - deliveryToolPayload = func(ToolSpec) (map[string]any, error) { return nil, errors.New("render failed") } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "cannot render typed payload") { - t.Fatalf("canonical render report = %#v", report) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - payload, err := oldQuery(loaded, args) - if len(args) > 0 && args[0] == "sample.run" && err == nil { - payload["title"] = "changed" - } - return payload, err - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "query differs from final ToolSpec") { - t.Fatalf("canonical drift report = %#v", report) - } - reset() - - missing := map[string]runtimeSchemaResolvedIdentity{} - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, missing, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "non-executable Schema path") { - t.Fatalf("missing path report = %#v", report) - } - wrongOwner := map[string]runtimeSchemaResolvedIdentity{ - "sample category run": {CanonicalPath: "sample.other"}, - "sample legacy execute": {CanonicalPath: "sample.other"}, - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, wrongOwner, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "owned by sample.other") { - t.Fatalf("wrong owner report = %#v", report) - } - - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample legacy execute" { - return nil, errors.New("path failed") - } - return oldQuery(loaded, args) - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "path \"sample legacy execute\" is not queryable") { - t.Fatalf("path query report = %#v", report) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample legacy execute" { - return map[string]any{"canonical_path": "other.run"}, nil - } - return oldQuery(loaded, args) - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "path \"sample legacy execute\" resolves") { - t.Fatalf("path canonical report = %#v", report) - } - reset() - deliveryToolPayload = func(tool ToolSpec) (map[string]any, error) { - if tool.Identity.IsAlias { - return nil, errors.New("alias render failed") - } - return oldTool(tool) - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "path \"sample legacy execute\" cannot render") { - t.Fatalf("path render report = %#v", report) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - payload, err := oldQuery(loaded, args) - if len(args) > 0 && args[0] == "sample legacy execute" && err == nil { - payload["title"] = "changed" - } - return payload, err - } - report = schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) - if !strings.Contains(strings.Join(report.DeliveryErrors, ";"), "path \"sample legacy execute\" query differs") { - t.Fatalf("path drift report = %#v", report) - } - reset() - - loaded.Index.registry.Products[0].Tools[0].Identity.Aliases = append(loaded.Index.registry.Products[0].Tools[0].Identity.Aliases, " ", "sample category run") - schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, nil, identities, nil) -} - -func TestCrossPlatformCoverageSchemaRegistryProjectionRemainingEdges(t *testing.T) { - oldRegistry := deliveryRegistryPayload - oldProduct := renderRegistryProductSummary - oldQuery := deliverySchemaPayload - oldTool := deliveryToolPayload - oldSummary := deliveryToolSummary - t.Cleanup(func() { - deliveryRegistryPayload = oldRegistry - renderRegistryProductSummary = oldProduct - deliverySchemaPayload = oldQuery - deliveryToolPayload = oldTool - deliveryToolSummary = oldSummary - }) - reset := func() { - deliveryRegistryPayload = oldRegistry - renderRegistryProductSummary = oldProduct - deliverySchemaPayload = oldQuery - deliveryToolPayload = oldTool - deliveryToolSummary = oldSummary - } - loaded := deliveryCoverageLoaded(t) - deliveryRegistryPayload = func(SchemaRegistry) (map[string]any, error) { return nil, errors.New("all failed") } - if got := schemaRegistryProjectionErrors(loaded); len(got) == 0 || !strings.Contains(got[0], "render final Schema --all") { - t.Fatalf("all render problems = %v", got) - } - reset() - deliveryRegistryPayload = func(SchemaRegistry) (map[string]any, error) { - return map[string]any{"products": []map[string]any{{"tools": []map[string]any{{}, {"canonical_path": "sample.run"}, {"canonical_path": "sample.run"}}}}}, nil - } - problems := schemaRegistryProjectionErrors(loaded) - if joined := strings.Join(problems, ";"); !strings.Contains(joined, "without canonical_path") || !strings.Contains(joined, "duplicate tool") { - t.Fatalf("all identity problems = %v", problems) - } - reset() - renderRegistryProductSummary = func(ProductSpec) (map[string]any, error) { return nil, errors.New("product failed") } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "render product") { - t.Fatalf("product render problems = %s", joined) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample" { - return nil, errors.New("product query failed") - } - return oldQuery(loaded, args) - } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "product sample is not queryable") { - t.Fatalf("product query problems = %s", joined) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample" { - return map[string]any{"product": map[string]any{}}, nil - } - return oldQuery(loaded, args) - } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "query differs from final ProductSpec") { - t.Fatalf("product drift problems = %s", joined) - } - reset() - deliveryToolPayload = func(ToolSpec) (map[string]any, error) { return nil, errors.New("tool failed") } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "render final ToolSpec") { - t.Fatalf("tool render problems = %s", joined) - } - reset() - deliveryRegistryPayload = func(SchemaRegistry) (map[string]any, error) { - return map[string]any{"products": []map[string]any{}}, nil - } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "Schema --all is missing") { - t.Fatalf("missing all tool problems = %s", joined) - } - reset() - deliveryRegistryPayload = func(registry SchemaRegistry) (map[string]any, error) { - payload, err := oldRegistry(registry) - products := schemaMapSlice(payload["products"]) - tools := schemaMapSlice(products[0]["tools"]) - tools[0]["title"] = "changed" - products[0]["tools"] = tools - payload["products"] = products - return payload, err - } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "differs from final ToolSpec") { - t.Fatalf("all tool drift problems = %s", joined) - } - reset() - - loaded.Registry.Products[0].Tools[0].Identity.CLIPath = "other group run" - loaded.Registry.Products[0].Tools[0].Identity.PrimaryCLIPath = "other group run" - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "has no typed product") { - t.Fatalf("missing group product problems = %s", joined) - } - loaded = deliveryCoverageLoaded(t) - deliveryToolSummary = func(ToolSpec) (map[string]any, error) { return nil, errors.New("summary failed") } - if joined := strings.Join(schemaRegistryProjectionErrors(loaded), ";"); !strings.Contains(joined, "render group") { - t.Fatalf("group summary problems = %s", joined) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample category" { - return nil, errors.New("group failed") - } - return oldQuery(loaded, args) - } - if joined := strings.Join(schemaRegistryProjectionErrors(deliveryCoverageLoaded(t)), ";"); !strings.Contains(joined, "group sample category is not queryable") { - t.Fatalf("group query problems = %s", joined) - } - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample category" { - return map[string]any{"tools": []map[string]any{}}, nil - } - return oldQuery(loaded, args) - } - if joined := strings.Join(schemaRegistryProjectionErrors(deliveryCoverageLoaded(t)), ";"); !strings.Contains(joined, "query differs from final ToolSpec summaries") { - t.Fatalf("group drift problems = %s", joined) - } -} - -func TestCrossPlatformCoverageCompletenessIdentityAndPathHelpersRemainingEdges(t *testing.T) { - bound := BoundCommandRegistry{Commands: []BoundCommandSpec{ - {CommandSpec: CommandSpec{CanonicalPath: "sample.one", PrimaryCLIPath: "sample run", Visibility: SchemaVisibilityPublic}}, - {CommandSpec: CommandSpec{CanonicalPath: "sample.two", PrimaryCLIPath: "sample run", Visibility: SchemaVisibilityPublic}}, - {CommandSpec: CommandSpec{CanonicalPath: "hidden.tool", PrimaryCLIPath: "hidden run", Visibility: SchemaVisibilityInternal}}, - {CommandSpec: CommandSpec{CanonicalPath: "", PrimaryCLIPath: "empty run", Visibility: SchemaVisibilityPublic}}, - }} - identities, conflicts := runtimeSchemaIdentityByBound(bound) - if len(identities) != 1 || len(conflicts) != 1 || !strings.Contains(conflicts[0], "belongs to both") { - t.Fatalf("identities=%#v conflicts=%v", identities, conflicts) - } - if got := sortedUniqueSchemaStrings([]string{" b ", "", "a", "b"}); strings.Join(got, ",") != "a,b" { - t.Fatalf("sorted unique = %v", got) - } - covered := map[string]bool{} - addSchemaCoveredPath(covered, " dws sample run ") - addSchemaCoveredPath(covered, " ") - if !covered["sample run"] || len(covered) != 1 { - t.Fatalf("covered paths = %#v", covered) - } - runtimeSchemaCompletenessFromBound(&cobra.Command{Use: "dws"}, nil, bound) - emptyRoot := &cobra.Command{Use: "dws", Run: func(*cobra.Command, []string) {}} - runtimeSchemaCompletenessAgainstPaths(emptyRoot, nil, nil) - hiddenRoot := &cobra.Command{Use: "dws"} - hiddenRoot.AddCommand(&cobra.Command{Use: "hidden", Hidden: true, Run: func(*cobra.Command, []string) {}}) - walkPublicRunnableLeaves(hiddenRoot, func(*cobra.Command) { t.Fatal("hidden leaf visited") }) - walkPublicRunnableLeaves(nil, func(*cobra.Command) { t.Fatal("nil root visited") }) -} diff --git a/internal/cli/schema_completeness_test.go b/internal/cli/schema_completeness_test.go index eac41762..ad9cb2a2 100644 --- a/internal/cli/schema_completeness_test.go +++ b/internal/cli/schema_completeness_test.go @@ -164,28 +164,6 @@ func TestSchemaCatalogDeliveryCompletenessAcceptsDeliveredAlias(t *testing.T) { } } -func TestSchemaRegistryRejectsIdentityConflictOnAliasLeaf(t *testing.T) { - root := &cobra.Command{Use: "dws"} - product := &cobra.Command{Use: "sample"} - current := &cobra.Command{Use: "current", Run: func(*cobra.Command, []string) {}} - legacy := &cobra.Command{Use: "legacy", Run: func(*cobra.Command, []string) {}} - AttachRuntimeSchema(current, "sample", "current", "test") - AttachRuntimeSchema(legacy, "sample", "current", "test") - annotateManualSchemaIdentity(legacy, "sample.wrong", "reviewed conflict fixture") - product.AddCommand(current, legacy) - root.AddCommand(product) - - if _, err := schemaRegistryForTest(root); err == nil || !strings.Contains(err.Error(), "sample legacy") || !strings.Contains(err.Error(), "conflicts") { - t.Fatalf("schemaRegistryForTest() error = %v, want alias identity conflict", err) - } - report := schemaCatalogDeliveryCompletenessForTest(root, schemaDeliveryTestSnapshot(schemaDeliveryTestTool{ - Canonical: "sample.current", CLIPath: "sample current", Aliases: []string{"sample legacy"}, - }), nil) - if len(report.DeliveryErrors) == 0 || !strings.Contains(strings.Join(report.DeliveryErrors, " "), "sample legacy") { - t.Fatalf("delivery errors = %v, want alias identity conflict", report.DeliveryErrors) - } -} - func TestSchemaCatalogDeliveryCompletenessRejectsWrongCanonical(t *testing.T) { root := schemaDeliveryTestRoot( schemaDeliveryTestTool{Canonical: "sample.expected", CLIPath: "sample run"}, diff --git a/internal/cli/schema_delivery_invariants_coverage_more_test.go b/internal/cli/schema_delivery_invariants_coverage_more_test.go deleted file mode 100644 index f9260d88..00000000 --- a/internal/cli/schema_delivery_invariants_coverage_more_test.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -package cli - -import ( - "errors" - "strings" - "testing" -) - -func deliveryCoverageLoaded(t *testing.T) loadedSchemaCatalog { - t.Helper() - snapshot := schemaDeliveryTestSnapshot(schemaDeliveryTestTool{ - Canonical: "sample.run", - CLIPath: "sample category run", - Aliases: []string{"sample legacy execute"}, - }) - loaded, err := loadSchemaCatalogSnapshot(snapshot) - if err != nil { - t.Fatal(err) - } - return loaded -} - -func requireDeliveryProblem(t *testing.T, loaded loadedSchemaCatalog, want string) { - t.Helper() - problems := schemaDeliveryInvariantErrors(loaded) - if !strings.Contains(strings.Join(problems, "; "), want) { - t.Fatalf("problems = %v, want containing %q", problems, want) - } -} - -func TestCrossPlatformCoverageSchemaDeliveryInvariantBoundaryErrors(t *testing.T) { - badSnapshot := SchemaCatalogSnapshot{Catalog: map[string]any{"bad": func() {}}} - if err := ValidateSchemaDeliveryInvariants(SchemaRegistry{}, badSnapshot); err == nil || !strings.Contains(err.Error(), "encode final") { - t.Fatalf("ValidateSchemaDeliveryInvariants() marshal error = %v", err) - } - if err := validateSchemaSnapshotDeliveryInvariants(badSnapshot); err == nil || !strings.Contains(err.Error(), "encode final") { - t.Fatalf("snapshot invariant marshal error = %v", err) - } - invalidSnapshot := SchemaCatalogSnapshot{Version: 999, Catalog: map[string]any{}, Tools: map[string]map[string]any{}} - if err := ValidateSchemaDeliveryInvariants(SchemaRegistry{}, invalidSnapshot); err == nil || !strings.Contains(err.Error(), "production loader") { - t.Fatalf("ValidateSchemaDeliveryInvariants() loader error = %v", err) - } - if err := validateSchemaSnapshotDeliveryInvariants(invalidSnapshot); err == nil || !strings.Contains(err.Error(), "production loader") { - t.Fatalf("snapshot invariant loader error = %v", err) - } - oldRegistryPayload := deliveryRegistryPayload - t.Cleanup(func() { deliveryRegistryPayload = oldRegistryPayload }) - deliveryRegistryPayload = func(SchemaRegistry) (map[string]any, error) { return nil, errors.New("projection failed") } - if err := validateSchemaSnapshotDeliveryInvariants(deliveryCoverageLoaded(t).Snapshot); err == nil || !strings.Contains(err.Error(), "invalid final Schema delivery invariants") { - t.Fatalf("snapshot projection error = %v", err) - } - deliveryRegistryPayload = oldRegistryPayload - if problems := schemaSourceSnapshotInvariantErrors(SchemaRegistry{Products: []ProductSpec{{ID: " "}}}, SchemaCatalogSnapshot{}); len(problems) == 0 || !strings.Contains(problems[0], "render source") { - t.Fatalf("source snapshot problems = %v", problems) - } - if problems := schemaSourceDeliveryInvariantErrors(SchemaRegistry{Products: []ProductSpec{{ID: " "}}}, SchemaRegistry{}); len(problems) == 0 || !strings.Contains(problems[0], "normalize source") { - t.Fatalf("source normalization problems = %v", problems) - } - if problems := schemaSourceDeliveryInvariantErrors(SchemaRegistry{}, SchemaRegistry{Products: []ProductSpec{{ID: " "}}}); len(problems) == 0 || !strings.Contains(problems[0], "production-decoded") { - t.Fatalf("delivered normalization problems = %v", problems) - } - left, _ := validSnapshotAdapterFixture(t) - right := left - right.Source = "other" - if problems := schemaSourceDeliveryInvariantErrors(left, right); len(problems) == 0 || !strings.Contains(problems[0], "differs from complete source") { - t.Fatalf("typed difference problems = %v", problems) - } -} - -func TestCrossPlatformCoverageSchemaJSONDifferenceRemainingShapes(t *testing.T) { - path, left, _ := firstSchemaJSONDifference(func() {}, map[string]any{}) - if path != "$" || !strings.Contains(left, "encode error") { - t.Fatalf("encode difference = %q %q", path, left) - } - cases := []struct { - left any - right any - path string - diff bool - }{ - {left: map[string]any{"a": 1}, right: []any{1}, path: "$", diff: true}, - {left: map[string]any{"a": 1}, right: map[string]any{"b": 1}, path: "$.a", diff: true}, - {left: map[string]any{"a": map[string]any{"b": 1}}, right: map[string]any{"a": map[string]any{"b": 2}}, path: "$.a.b", diff: true}, - {left: map[string]any{"a": 1}, right: map[string]any{"a": 1}, path: "$", diff: false}, - {left: []any{1}, right: map[string]any{}, path: "$", diff: true}, - {left: []any{1}, right: []any{1, 2}, path: "$", diff: true}, - {left: []any{map[string]any{"a": 1}}, right: []any{map[string]any{"a": 2}}, path: "$[0].a", diff: true}, - {left: []any{1}, right: []any{1}, path: "$", diff: false}, - {left: 1, right: 2, path: "$", diff: true}, - } - for _, tc := range cases { - path, _, _, different := firstSchemaJSONDifferenceAt("$", tc.left, tc.right) - if path != tc.path || different != tc.diff { - t.Errorf("difference(%#v, %#v) = %q, %v; want %q, %v", tc.left, tc.right, path, different, tc.path, tc.diff) - } - } - if got := compactSchemaDiagnosticValue(func() {}); !strings.Contains(got, "0x") { - t.Fatalf("compact unsupported value = %q", got) - } - if got := compactSchemaDiagnosticValue(strings.Repeat("x", 300)); len(got) != 243 || !strings.HasSuffix(got, "...") { - t.Fatalf("compact long value length = %d, value = %q", len(got), got) - } -} - -func TestCrossPlatformCoverageSchemaDeliveryInvariantProjectionEdges(t *testing.T) { - oldRegistry := deliveryRegistryPayload - oldQuery := deliverySchemaPayload - oldOverview := deliveryOverviewPayload - oldResolve := deliveryIndexResolve - oldTool := deliveryToolPayload - oldSummary := deliveryToolSummary - t.Cleanup(func() { - deliveryRegistryPayload = oldRegistry - deliverySchemaPayload = oldQuery - deliveryOverviewPayload = oldOverview - deliveryIndexResolve = oldResolve - deliveryToolPayload = oldTool - deliveryToolSummary = oldSummary - }) - reset := func() { - deliveryRegistryPayload = oldRegistry - deliverySchemaPayload = oldQuery - deliveryOverviewPayload = oldOverview - deliveryIndexResolve = oldResolve - deliveryToolPayload = oldTool - deliveryToolSummary = oldSummary - } - - deliveryRegistryPayload = func(SchemaRegistry) (map[string]any, error) { return nil, errors.New("all failed") } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "render final Schema --all") - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) == 0 { - return nil, errors.New("list failed") - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "Schema list is not queryable") - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) == 0 { - return map[string]any{"catalog_hash": "wrong", "surface_hash": "unexpected"}, nil - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "Schema list differs from complete Catalog") - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "catalog_hash") - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "has surface_hash") - reset() - loaded := deliveryCoverageLoaded(t) - loaded.Snapshot.SurfaceHash = "expected" - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) == 0 { - payload, err := oldQuery(loaded, args) - delete(payload, "surface_hash") - return payload, err - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, loaded, "differs from snapshot surface_hash") - reset() - deliveryOverviewPayload = func(SchemaRegistry) (map[string]any, error) { return nil, errors.New("overview failed") } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "render final Schema product overview") - reset() - deliveryOverviewPayload = func(SchemaRegistry) (map[string]any, error) { return map[string]any{}, nil } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "overview differs") - reset() - deliveryIndexResolve = func(SchemaIndex, string) (ToolSpec, bool) { return ToolSpec{}, false } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "typed Schema index lost canonical") - reset() - deliveryToolPayload = func(ToolSpec) (map[string]any, error) { return nil, errors.New("tool failed") } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "render final ToolSpec sample.run") - reset() - - loaded = deliveryCoverageLoaded(t) - delete(loaded.Snapshot.Tools, "sample.run") - requireDeliveryProblem(t, loaded, "Catalog full tools are missing") - loaded = deliveryCoverageLoaded(t) - loaded.Snapshot.Tools["sample.run"]["title"] = "changed" - requireDeliveryProblem(t, loaded, "Catalog full tool sample.run differs") - - deliveryRegistryPayload = func(registry SchemaRegistry) (map[string]any, error) { - payload, err := oldRegistry(registry) - products := schemaMapSlice(payload["products"]) - products[0]["tools"] = []map[string]any{} - payload["products"] = products - return payload, err - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "Schema --all is missing") - reset() - deliveryRegistryPayload = func(registry SchemaRegistry) (map[string]any, error) { - payload, err := oldRegistry(registry) - products := schemaMapSlice(payload["products"]) - tools := schemaMapSlice(products[0]["tools"]) - tools[0]["title"] = "changed" - products[0]["tools"] = tools - payload["products"] = products - return payload, err - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "Schema --all tool sample.run differs") - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample.run" { - return nil, errors.New("leaf failed") - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "canonical leaf sample.run is not queryable") - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample.run" { - return map[string]any{"canonical_path": "sample.run"}, nil - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "canonical leaf sample.run differs") - reset() - deliveryToolSummary = func(ToolSpec) (map[string]any, error) { return nil, errors.New("summary failed") } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "render final ToolSpec summary") - reset() - loaded = deliveryCoverageLoaded(t) - products := schemaMapSlice(loaded.Snapshot.Catalog["products"]) - products[0]["tools"] = []map[string]any{} - loaded.Snapshot.Catalog["products"] = products - requireDeliveryProblem(t, loaded, "Catalog summary is missing") - loaded = deliveryCoverageLoaded(t) - products = schemaMapSlice(loaded.Snapshot.Catalog["products"]) - tools := schemaMapSlice(products[0]["tools"]) - tools[0]["title"] = "changed" - products[0]["tools"] = tools - loaded.Snapshot.Catalog["products"] = products - requireDeliveryProblem(t, loaded, "Catalog summary sample.run differs") - - loaded = deliveryCoverageLoaded(t) - loaded.Index.registry.Products[0].Tools[0].Identity.Aliases = append(loaded.Index.registry.Products[0].Tools[0].Identity.Aliases, " ") - schemaDeliveryInvariantErrors(loaded) - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample legacy execute" { - return nil, errors.New("alias failed") - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "alias \"sample legacy execute\" for sample.run is not queryable") - reset() - deliverySchemaPayload = func(loaded loadedSchemaCatalog, args []string) (map[string]any, error) { - if len(args) > 0 && args[0] == "sample legacy execute" { - return map[string]any{"cli_path": "wrong", "is_alias": true}, nil - } - return oldQuery(loaded, args) - } - requireDeliveryProblem(t, deliveryCoverageLoaded(t), "has cli_path") - reset() - - loaded = deliveryCoverageLoaded(t) - loaded.Snapshot.Tools["extra"] = map[string]any{} - requireDeliveryProblem(t, loaded, "Catalog full tools contain 2 tools") -} - -func TestCrossPlatformCoverageSchemaDeliveryToolExtractionAndAliasPathEdge(t *testing.T) { - payload := map[string]any{"products": []map[string]any{{"tools": []map[string]any{{"title": "missing"}}}}} - _, problems := schemaDeliveryToolsByCanonical(payload, "view") - if len(problems) != 1 || !strings.Contains(problems[0], "without canonical_path") { - t.Fatalf("problems = %v", problems) - } - canonical := map[string]any{"cli_path": "sample run", "is_alias": false, "title": "Run"} - alias := map[string]any{"cli_path": "wrong", "is_alias": true, "title": "Run"} - if problem := schemaAliasViewProblem(canonical, alias, "sample execute"); !strings.Contains(problem, "has cli_path") { - t.Fatalf("alias path problem = %q", problem) - } -} diff --git a/internal/cli/schema_full_pipeline_external_test.go b/internal/cli/schema_full_pipeline_external_test.go index 31b46cf7..0ed8d2cf 100644 --- a/internal/cli/schema_full_pipeline_external_test.go +++ b/internal/cli/schema_full_pipeline_external_test.go @@ -43,9 +43,6 @@ func TestCrossPlatformCoverageProductionSchemaSourcePipeline(t *testing.T) { t.Fatalf("ValidateEmbeddedRuntimeSchemaCompleteness() error = %v", err) } root = app.NewSchemaSourceRootCommand() - if _, err := cli.ApplyEmbeddedManualSchemaHints(root); err != nil { - t.Fatal(err) - } effective, err := cli.BuildEffectiveCommandRegistry(root) if err != nil { t.Fatal(err) @@ -54,15 +51,11 @@ func TestCrossPlatformCoverageProductionSchemaSourcePipeline(t *testing.T) { if err != nil { t.Fatal(err) } - hints, err := cli.LoadAgentHintsFromSelectionForValidation(nil) - if err != nil { - t.Fatal(err) + if _, err := cli.ValidateAgentSelectionContract(bound); err != nil { + t.Fatalf("ValidateAgentSelectionContract() error = %v", err) } - if _, err := cli.ValidateManualAgentSelectionContract(bound, hints); err != nil { - t.Fatalf("ValidateManualAgentSelectionContract() error = %v", err) - } - if _, _, err := cli.BuildManualAgentSelectionEvalFixture(bound, hints); err != nil { - t.Fatalf("BuildManualAgentSelectionEvalFixture() error = %v", err) + if _, _, err := cli.BuildAgentSelectionEvalFixture(bound); err != nil { + t.Fatalf("BuildAgentSelectionEvalFixture() error = %v", err) } if _, err := cli.ValidateEmbeddedManualAgentExampleDelivery(bound, registry); err != nil { t.Fatalf("ValidateEmbeddedManualAgentExampleDelivery() error = %v", err) diff --git a/internal/cli/schema_hint_decls.go b/internal/cli/schema_hint_decls.go deleted file mode 100644 index 9fe2cb95..00000000 --- a/internal/cli/schema_hint_decls.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. - -package cli - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" -) - -// schemaHintDecl is one command's Safety + Schema migrated from reviewed -// schema_hints selection/metadata into compiled Go. -type schemaHintDecl struct { - Safety SafetySpec - Description string - DryRun *DryRunSpec - Interface *InterfaceSpec - Selection SelectionSpec -} - -// lookupSchemaHintDecl returns the migrated declaration for a canonical path. -func lookupSchemaHintDecl(canonical string) (schemaHintDecl, bool) { - canonical = strings.TrimSpace(canonical) - if canonical == "" { - return schemaHintDecl{}, false - } - d, ok := schemaHintDeclsByCanonical[canonical] - return d, ok -} - -// attachSchemaHintDecl registers ContractFinal from a migrated hint declaration -// without replacing RunE/Execute. Completeness is enforced here so bind-time -// attaches cannot ship a partial overlay. -func attachSchemaHintDecl(cmd *cobra.Command, decl schemaHintDecl) { - if cmd == nil { - return - } - if strings.TrimSpace(decl.Description) == "" { - panic(fmt.Sprintf("schema hint decl for %s missing Description", cmd.CommandPath())) - } - if strings.TrimSpace(decl.Selection.AgentSummary) == "" || - len(decl.Selection.UseWhen) == 0 || len(decl.Selection.AvoidWhen) == 0 || - len(decl.Selection.Examples) == 0 { - panic(fmt.Sprintf("schema hint decl for %s missing Selection prose", cmd.CommandPath())) - } - if decl.Interface == nil || strings.TrimSpace(decl.Interface.Mode) == "" || - strings.TrimSpace(decl.Interface.Availability) == "" { - panic(fmt.Sprintf("schema hint decl for %s missing Interface", cmd.CommandPath())) - } - if (decl.Interface.Mode == InterfaceModeComposite || - decl.Interface.Availability == InterfaceUnavailable) && - strings.TrimSpace(decl.Interface.Reason) == "" { - panic(fmt.Sprintf("schema hint decl for %s missing Interface.Reason", cmd.CommandPath())) - } - - safety := decl.Safety - if strings.TrimSpace(safety.Effect) == "" && strings.TrimSpace(safety.Risk) == "" && - strings.TrimSpace(safety.Confirmation) == "" && strings.TrimSpace(safety.Idempotency) == "" { - safety = SafetySpec{ - Effect: "read", Risk: "low", - Confirmation: "not_required", Idempotency: "idempotent", - } - } - safety.EffectSource = "corecmd.contract" - - payload := ContractFinalPayload{ - Title: firstNonEmptyTrim(cmd.Short), - Description: firstNonEmptyTrim(decl.Description, cmd.Long), - Safety: &safety, - DryRun: decl.DryRun, - Interface: decl.Interface, - Selection: &SelectionSpec{ - AgentSummary: strings.TrimSpace(decl.Selection.AgentSummary), - UseWhen: decl.Selection.UseWhen, - AvoidWhen: decl.Selection.AvoidWhen, - Prerequisites: decl.Selection.Prerequisites, - Tips: decl.Selection.Tips, - WorkflowRefs: decl.Selection.WorkflowRefs, - Examples: decl.Selection.Examples, - }, - } - RegisterRuntimeContractFinal(cmd, payload) -} - -func firstNonEmptyTrim(values ...string) string { - for _, v := range values { - if s := strings.TrimSpace(v); s != "" { - return s - } - } - return "" -} diff --git a/internal/cli/schema_hint_decls_generated.go b/internal/cli/schema_hint_decls_generated.go deleted file mode 100644 index fd2ee4ce..00000000 --- a/internal/cli/schema_hint_decls_generated.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2026 Alibaba Group -// -// Intentionally empty after the ContractFinal migration: leaf and shortcut -// declarations now live in helpers.DeclareLeafMetadata / Shortcut.Schema, so the -// lookup in schema_hint_decls.go always misses and callers fall through to the -// declaration path. No generator maintains this table. - -package cli - -var schemaHintDeclsByCanonical = map[string]schemaHintDecl{} diff --git a/internal/cli/schema_hint_decls_test.go b/internal/cli/schema_hint_decls_test.go deleted file mode 100644 index 7a3e8ddc..00000000 --- a/internal/cli/schema_hint_decls_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. - -package cli - -import ( - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func testSchemaHintDeclFixture() schemaHintDecl { - return schemaHintDecl{ - Safety: SafetySpec{ - Effect: "read", Risk: "low", - Confirmation: "not_required", Idempotency: "idempotent", - }, - Description: "fixture command for attachSchemaHintDecl freeze test", - Interface: &InterfaceSpec{ - Mode: InterfaceModeMCP, Availability: InterfaceAvailable, - Ref: &InterfaceRefSpec{ProductID: "fixture", RPCName: "fixture_probe"}, - }, - Selection: SelectionSpec{ - AgentSummary: "fixture probe", - UseWhen: []string{"unit testing attachSchemaHintDecl"}, - AvoidWhen: []string{"production use"}, - Examples: []string{"dws fixture probe"}, - }, - } -} - -func TestSchemaHintDeclAttachDoesNotReplaceRunE(t *testing.T) { - ran := false - cmd := &cobra.Command{ - Use: "probe", - Short: "probe", - RunE: func(*cobra.Command, []string) error { - ran = true - return nil - }, - } - attachSchemaHintDecl(cmd, testSchemaHintDeclFixture()) - if !HasRuntimeContractFinal(cmd) { - t.Fatal("expected ContractFinal after attach") - } - if err := cmd.Execute(); err != nil { - t.Fatalf("Execute() error = %v", err) - } - if !ran { - t.Fatal("RunE body was not invoked — execution must stay frozen") - } - ClearRuntimeContractFinalForTest(cmd) -} - -func TestSchemaHintDeclLookupCoverage(t *testing.T) { - // All reviewed tools now author ContractFinal beside the command - // (DeclareLeafMetadata / Shortcut.Schema). The compiled residual map is empty. - if got := len(schemaHintDeclsByCanonical); got != 0 { - t.Fatalf("compiled residual decls = %d, want 0 after full colocation", got) - } - if _, ok := lookupSchemaHintDecl("aitable.view_update_aggregate"); ok { - t.Fatal("aitable.view_update_aggregate must not remain in compiled hint decls") - } - if _, ok := lookupSchemaHintDecl("aitable.shortcut_base_list"); ok { - t.Fatal("aitable.shortcut_base_list must not remain in compiled hint decls") - } - if _, ok := lookupSchemaHintDecl("doc.create_document"); ok { - t.Fatal("doc.create_document must not remain in compiled hint decls") - } -} - -func TestCrossPlatformCoverageLookupSchemaHintDeclBlankCanonical(t *testing.T) { - if _, ok := lookupSchemaHintDecl(""); ok { - t.Fatal("empty canonical must not resolve a hint decl") - } - if _, ok := lookupSchemaHintDecl(" "); ok { - t.Fatal("blank canonical must not resolve a hint decl") - } -} - -func TestCrossPlatformCoverageAttachSchemaHintDeclNilCommandIsNoOp(t *testing.T) { - // Must not panic and must not register anything. - attachSchemaHintDecl(nil, testSchemaHintDeclFixture()) -} - -func TestCrossPlatformCoverageAttachSchemaHintDeclCompletenessPanics(t *testing.T) { - mustPanic := func(t *testing.T, wantFragment string, decl schemaHintDecl) { - t.Helper() - cmd := &cobra.Command{Use: "probe", Short: "probe"} - defer func() { - recovered := recover() - if recovered == nil { - t.Fatalf("attachSchemaHintDecl must panic for %q", wantFragment) - } - message, ok := recovered.(string) - if !ok || !strings.Contains(message, wantFragment) { - t.Fatalf("panic = %v, want fragment %q", recovered, wantFragment) - } - if HasRuntimeContractFinal(cmd) { - t.Fatal("panicking attach must not register ContractFinal") - } - }() - attachSchemaHintDecl(cmd, decl) - } - - t.Run("missing description", func(t *testing.T) { - decl := testSchemaHintDeclFixture() - decl.Description = " " - mustPanic(t, "missing Description", decl) - }) - t.Run("missing selection prose", func(t *testing.T) { - decl := testSchemaHintDeclFixture() - decl.Selection.UseWhen = nil - mustPanic(t, "missing Selection prose", decl) - }) - t.Run("missing interface", func(t *testing.T) { - decl := testSchemaHintDeclFixture() - decl.Interface = nil - mustPanic(t, "missing Interface", decl) - }) - t.Run("composite without reason", func(t *testing.T) { - decl := testSchemaHintDeclFixture() - decl.Interface = &InterfaceSpec{Mode: InterfaceModeComposite, Availability: InterfaceAvailable} - mustPanic(t, "missing Interface.Reason", decl) - }) -} - -func TestCrossPlatformCoverageAttachSchemaHintDeclDefaultsEmptySafetyToRead(t *testing.T) { - decl := testSchemaHintDeclFixture() - decl.Safety = SafetySpec{} - cmd := &cobra.Command{Use: "probe", Short: "probe"} - t.Cleanup(func() { ClearRuntimeContractFinalForTest(cmd) }) - attachSchemaHintDecl(cmd, decl) - final, ok := RuntimeContractFinal(cmd) - if !ok || final.Safety == nil { - t.Fatalf("ContractFinal after attach = %#v ok=%v", final, ok) - } - if final.Safety.Effect != "read" || final.Safety.Risk != "low" || - final.Safety.Confirmation != "not_required" || final.Safety.Idempotency != "idempotent" { - t.Fatalf("defaulted safety = %#v", final.Safety) - } - if final.Safety.EffectSource != "corecmd.contract" { - t.Fatalf("effect source = %q, want corecmd.contract", final.Safety.EffectSource) - } -} - -func TestFirstNonEmptyTrim(t *testing.T) { - if got := firstNonEmptyTrim(" ", "", "\t"); got != "" { - t.Fatalf("all-blank firstNonEmptyTrim = %q, want empty", got) - } - if got := firstNonEmptyTrim(); got != "" { - t.Fatalf("no-arg firstNonEmptyTrim = %q, want empty", got) - } - if got := firstNonEmptyTrim("", " value "); got != "value" { - t.Fatalf("firstNonEmptyTrim = %q, want value", got) - } -} diff --git a/internal/cli/schema_hint_dirs.go b/internal/cli/schema_hint_dirs.go deleted file mode 100644 index 09e69b15..00000000 --- a/internal/cli/schema_hint_dirs.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "encoding/json" - "fmt" - "io/fs" - "path/filepath" - "sort" - "strings" -) - -const selectionRevisionID = "selection" - -type hintDirFile struct { - Version int `json:"version"` - Source hintDirSource `json:"source"` - Products map[string]hintDirProduct `json:"products,omitempty"` - Tools map[string]hintDirTool `json:"tools,omitempty"` -} - -type hintDirSource struct { - Kind string `json:"kind"` - Name string `json:"name"` - Reviewed bool `json:"reviewed,omitempty"` -} - -type hintDirProduct struct { - AgentSummary string `json:"agent_summary"` - UseWhen []string `json:"use_when"` - AvoidWhen []string `json:"avoid_when"` - Reviewed bool `json:"reviewed"` - ReviewReason string `json:"review_reason"` - SourceRefs []string `json:"source_refs"` -} - -type hintDirTool struct { - AgentSummary string `json:"agent_summary,omitempty"` - UseWhen []string `json:"use_when,omitempty"` - AvoidWhen []string `json:"avoid_when,omitempty"` - Examples []string `json:"examples,omitempty"` - Reviewed bool `json:"reviewed,omitempty"` - ReviewReason string `json:"review_reason,omitempty"` - SourceRefs []string `json:"source_refs,omitempty"` - CLIPath string `json:"cli_path,omitempty"` - Parameters map[string]ManualSchemaParameterHint `json:"parameters,omitempty"` -} - -func loadManualSchemaHintsFromHintDirs(metadataFS fs.FS, metadataGlob string, selectionFS fs.FS, selectionGlob string) (ManualSchemaHintSnapshot, error) { - // Metadata and selection HintFile dirs are both optional/retired. - // Production passes nil/"" for both; ProductDecl / ContractFinal own - // Agent routing and leaf facts. Nil FS or empty glob = no overlays and - // an empty Agent hint set (synthetic revision only). - var commands []ManualSchemaCommandHint - if metadataFS != nil && strings.TrimSpace(metadataGlob) != "" { - var err error - commands, err = loadParameterCommandsFromMetadata(metadataFS, metadataGlob) - if err != nil { - return ManualSchemaHintSnapshot{}, err - } - } - agentHints, err := loadAgentHintsFromSelection(selectionFS, selectionGlob) - if err != nil { - return ManualSchemaHintSnapshot{}, err - } - snapshot := ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: commands, - AgentHints: agentHints, - } - if err := ValidateManualAgentHintSet(snapshot.AgentHints, nil, nil); err != nil { - return ManualSchemaHintSnapshot{}, fmt.Errorf("validate selection Agent hints: %w", err) - } - return snapshot, nil -} - -func loadParameterCommandsFromMetadata(metadataFS fs.FS, globPattern string) ([]ManualSchemaCommandHint, error) { - // Nil FS, empty glob, or zero matches are all OK (no overlays). The - // on-disk schema_hints/metadata/ directory may be absent. - if metadataFS == nil || strings.TrimSpace(globPattern) == "" { - return nil, nil - } - files, err := fs.Glob(metadataFS, globPattern) - if err != nil { - return nil, fmt.Errorf("list metadata hints: %w", err) - } - sort.Strings(files) - commands := make([]ManualSchemaCommandHint, 0) - for _, name := range files { - data, err := fs.ReadFile(metadataFS, name) - if err != nil { - return nil, fmt.Errorf("read metadata hint %s: %w", name, err) - } - var file hintDirFile - if err := json.Unmarshal(data, &file); err != nil { - return nil, fmt.Errorf("decode metadata hint %s: %w", name, err) - } - canonicals := make([]string, 0, len(file.Tools)) - for canonical := range file.Tools { - canonicals = append(canonicals, canonical) - } - sort.Strings(canonicals) - for _, canonical := range canonicals { - tool := file.Tools[canonical] - if len(tool.Parameters) == 0 { - continue - } - cliPath := strings.TrimSpace(tool.CLIPath) - if cliPath == "" { - return nil, fmt.Errorf("metadata tool %s has parameters but missing cli_path", canonical) - } - reason := strings.TrimSpace(tool.ReviewReason) - if reason == "" { - reason = "reviewed parameter overrides from schema_hints/metadata" - } - commands = append(commands, ManualSchemaCommandHint{ - CLIPath: cliPath, - CanonicalPath: canonical, - Reason: reason, - Reviewed: true, - Parameters: tool.Parameters, - }) - } - } - return commands, nil -} - -// LoadAgentHintsFromSelectionForValidation loads selection HintFiles from an -// FS rooted at schema_hints/selection (so glob *.json). A nil FS is treated -// as an absent optional/retired directory and returns an empty hint set; -// callers that rely on ProductDecl/ContractFinal should pass nil. -func LoadAgentHintsFromSelectionForValidation(selectionFS fs.FS) (ManualAgentHintSet, error) { - return loadAgentHintsFromSelection(selectionFS, "*.json") -} - -func emptySelectionAgentHints() ManualAgentHintSet { - return ManualAgentHintSet{ - Revisions: map[string]ManualAgentHintRevision{ - selectionRevisionID: { - GeneratedBy: "human", - Reason: "schema_hints/selection retired; Agent routing comes from ProductDecl/ContractFinal", - }, - }, - Products: map[string]ManualAgentProductHint{}, - Tools: map[string]ManualAgentToolHint{}, - } -} - -func loadAgentHintsFromSelection(selectionFS fs.FS, globPattern string) (ManualAgentHintSet, error) { - // Nil FS or empty glob = optional/absent selection directory. Zero matches - // (empty shells already deleted, or empty dir) also yield an empty set. - if selectionFS == nil || strings.TrimSpace(globPattern) == "" { - return emptySelectionAgentHints(), nil - } - files, err := fs.Glob(selectionFS, globPattern) - if err != nil { - return ManualAgentHintSet{}, fmt.Errorf("list selection hints: %w", err) - } - sort.Strings(files) - hints := emptySelectionAgentHints() - for _, name := range files { - data, err := fs.ReadFile(selectionFS, name) - if err != nil { - return ManualAgentHintSet{}, fmt.Errorf("read selection hint %s: %w", name, err) - } - var file hintDirFile - if err := json.Unmarshal(data, &file); err != nil { - return ManualAgentHintSet{}, fmt.Errorf("decode selection hint %s: %w", name, err) - } - productIDs := make([]string, 0, len(file.Products)) - for productID := range file.Products { - productIDs = append(productIDs, productID) - } - sort.Strings(productIDs) - for _, productID := range productIDs { - product := file.Products[productID] - reason := strings.TrimSpace(product.ReviewReason) - if reason == "" { - reason = "reviewed product selection" - } - evidence := append([]string{}, product.SourceRefs...) - if len(evidence) == 0 { - evidence = []string{"schema_hints/selection/" + filepath.Base(name)} - } - hints.Products[productID] = ManualAgentProductHint{ - AgentSummary: product.AgentSummary, - UseWhen: product.UseWhen, - AvoidWhen: product.AvoidWhen, - Reviewed: true, - Revision: selectionRevisionID, - Reason: reason, - Evidence: evidence, - } - } - canonicals := make([]string, 0, len(file.Tools)) - for canonical := range file.Tools { - canonicals = append(canonicals, canonical) - } - sort.Strings(canonicals) - for _, canonical := range canonicals { - tool := file.Tools[canonical] - reason := strings.TrimSpace(tool.ReviewReason) - if reason == "" { - reason = "reviewed tool selection" - } - evidence := append([]string{}, tool.SourceRefs...) - if len(evidence) == 0 { - evidence = []string{"schema_hints/selection/" + filepath.Base(name)} - } - hints.Tools[canonical] = ManualAgentToolHint{ - AgentSummary: tool.AgentSummary, - UseWhen: tool.UseWhen, - AvoidWhen: tool.AvoidWhen, - Examples: tool.Examples, - Reviewed: true, - Revision: selectionRevisionID, - Reason: reason, - Evidence: evidence, - } - } - } - return hints, nil -} diff --git a/internal/cli/schema_hint_emptiness_contract_test.go b/internal/cli/schema_hint_emptiness_contract_test.go index e9f3b2d8..712cad84 100644 --- a/internal/cli/schema_hint_emptiness_contract_test.go +++ b/internal/cli/schema_hint_emptiness_contract_test.go @@ -22,9 +22,7 @@ import ( ) // TestDeletedProductionSchemaHintFilesStayGone locks stage-3 cleanup: the -// former production RegisterSchemaHints init files must not return. Their -// pins live on leaf ParamDecl / ContractFinal; RegisterSchemaHints remains -// only for test-fixture injection. +// former production RegisterSchemaHints init files must not return. func TestDeletedProductionSchemaHintFilesStayGone(t *testing.T) { deleted := []string{ "schema_hints_aitable.go", @@ -32,11 +30,17 @@ func TestDeletedProductionSchemaHintFilesStayGone(t *testing.T) { "schema_hints_ding.go", "schema_hints_helper_roots.go", "schema_hints_runtime.go", + "schema_manual_hints.go", + "schema_hint_dirs.go", + "schema_hint_decls.go", + "schema_hint_decls_generated.go", + "schema_hints.go", + "schema_wukong_agent_hints_audit.json", } for _, name := range deleted { path := filepath.Join(".", name) if _, err := os.Stat(path); err == nil { - t.Fatalf("%s must stay deleted after RegisterSchemaHints→ParamDecl migration", name) + t.Fatalf("%s must stay deleted after Manual Hint / Schema Hint retirement", name) } else if !os.IsNotExist(err) { t.Fatalf("stat %s: %v", name, err) } @@ -51,33 +55,6 @@ func TestSchemaHintsDirectoryRetired(t *testing.T) { } } -// TestEmbeddedMetadataHintLoadingStaysEmpty locks production embed loading: -// metadata HintFiles are retired; nil/empty FS must yield zero overlays. -func TestEmbeddedMetadataHintLoadingStaysEmpty(t *testing.T) { - commands, err := loadParameterCommandsFromMetadata(nil, "") - if err != nil { - t.Fatalf("loadParameterCommandsFromMetadata(nil, \"\"): %v", err) - } - if got := len(commands); got != 0 { - t.Fatalf("nil metadata parameter overlays = %d, want 0", got) - } -} - -// TestEmbeddedSelectionHintLoadingStaysEmpty locks production embed loading: -// selection HintFiles are retired; nil/empty FS must yield empty products/tools. -func TestEmbeddedSelectionHintLoadingStaysEmpty(t *testing.T) { - hints, err := loadAgentHintsFromSelection(nil, "") - if err != nil { - t.Fatalf("loadAgentHintsFromSelection(nil, \"\"): %v", err) - } - if len(hints.Products) != 0 || len(hints.Tools) != 0 { - t.Fatalf("nil selection hints products=%d tools=%d, want 0/0", len(hints.Products), len(hints.Tools)) - } - if len(hints.Revisions) == 0 { - t.Fatal("nil selection hints must keep a synthetic revision") - } -} - // TestEmbeddedCatalogHasNoToolSchemaHintProvenance asserts the published // Catalog no longer selects tool_schema_hint as a provenance winner or // retained candidate after RegisterSchemaHints → ParamDecl migration. diff --git a/internal/cli/schema_hints.go b/internal/cli/schema_hints.go deleted file mode 100644 index 39e60fb1..00000000 --- a/internal/cli/schema_hints.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "fmt" - "strings" -) - -// ToolSchemaHint is reviewed field metadata for an existing registry-owned -// tool. It intentionally has no command path, alias, visibility, or canonical -// identity fields: all identity/navigation comes from CommandRegistry. -type ToolSchemaHint struct { - Title string - Description string - Parameters map[string]ParameterSchemaHint -} - -// ParameterSchemaHint overrides the projected flat schema for one parameter. -// It can be keyed either by the original MCP parameter name (preferred) or by -// the final CLI flag name when a hint is purely CLI-facing. -type ParameterSchemaHint struct { - FlagName string - Type string - Description string - Required *bool - RequiredWhen string -} - -type SchemaVisibility string - -const ( - SchemaVisibilityPublic SchemaVisibility = "public" - SchemaVisibilityCompat SchemaVisibility = "compat" - SchemaVisibilityInternal SchemaVisibility = "internal" -) - -type SchemaHintRegistry struct { - tools map[string]ToolSchemaHint -} - -var defaultSchemaHintRegistry = newSchemaHintRegistry() - -func newSchemaHintRegistry() *SchemaHintRegistry { - return &SchemaHintRegistry{ - tools: map[string]ToolSchemaHint{}, - } -} - -// RegisterSchemaHints registers curated tool_schema_hint overlays for one -// product. Production init no longer registers any product; ParamDecl / -// LeafSchema owns those pins. The registry and this helper remain so runtime -// lookup and test fixtures can still inject temporary hints for precedence -// coverage. Keys may be RPC names ("send_ding_message") or canonical paths -// ("ding.send_ding_message"). Duplicate canonical paths panic. -func RegisterSchemaHints(productID string, tools map[string]ToolSchemaHint) { - defaultSchemaHintRegistry.RegisterProduct(productID, tools) -} - -func (r *SchemaHintRegistry) RegisterProduct(productID string, tools map[string]ToolSchemaHint) { - productID = strings.TrimSpace(productID) - if productID == "" { - panic("schema hints: product id is required") - } - for name, hint := range tools { - path := canonicalHintPath(productID, name) - if path == "" { - panic(fmt.Sprintf("schema hints: empty tool name for product %q", productID)) - } - if _, exists := r.tools[path]; exists { - panic(fmt.Sprintf("schema hints: duplicate hint for %s", path)) - } - r.tools[path] = hint - } -} - -func (r *SchemaHintRegistry) Lookup(canonicalPath string) (ToolSchemaHint, bool) { - canonicalPath = strings.TrimSpace(canonicalPath) - if canonicalPath == "" { - return ToolSchemaHint{}, false - } - hint, ok := r.tools[canonicalPath] - return hint, ok -} - -func canonicalHintPath(productID, name string) string { - productID = strings.TrimSpace(productID) - name = strings.TrimSpace(name) - if name == "" { - return "" - } - if strings.Contains(name, ".") { - return name - } - return productID + "." + name -} - -func schemaHintForCanonicalPath(canonicalPath string) ToolSchemaHint { - if hint, ok := defaultSchemaHintRegistry.Lookup(canonicalPath); ok { - return hint - } - return ToolSchemaHint{} -} - -func lookupParameterSchemaHint(hints map[string]ParameterSchemaHint, paramName, flagName string) (ParameterSchemaHint, string, bool) { - if len(hints) == 0 { - return ParameterSchemaHint{}, "", false - } - if hint, ok := hints[paramName]; ok { - return hint, paramName, true - } - if hint, ok := hints[flagName]; ok { - return hint, flagName, true - } - return ParameterSchemaHint{}, "", false -} diff --git a/internal/cli/schema_identity_resolver.go b/internal/cli/schema_identity_resolver.go index f66817a6..3125fc91 100644 --- a/internal/cli/schema_identity_resolver.go +++ b/internal/cli/schema_identity_resolver.go @@ -45,23 +45,6 @@ func commandRegistryIdentityProvenance(command BoundCommandSpec) FieldProvenance Selected: &unselected, }) } - if productID, toolName, reason, ok := runtimeManualSchemaIdentity(leaf); ok { - manualValue, _ := json.Marshal(productID + "." + toolName) - // A manual-only CommandSpec is already represented by the selected - // candidate above. Keep a second candidate only when manual review is - // corroborating a registry-owned identity. - if command.Source != "reviewed_manual_hint" { - unselected := false - candidates = append(candidates, FieldCandidateProvenance{ - Value: manualValue, - Source: "reviewed_manual_hint", - SourceRef: path, - Precedence: "reviewed_manual", - ReviewReason: reason, - Selected: &unselected, - }) - } - } } appendEvidence(command.PrimaryCLIPath, command.PrimaryCommand) diff --git a/internal/cli/schema_manual_hints.go b/internal/cli/schema_manual_hints.go deleted file mode 100644 index 9ab4bada..00000000 --- a/internal/cli/schema_manual_hints.go +++ /dev/null @@ -1,1266 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "sort" - "strings" - "sync" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -const manualSchemaHintVersion = 1 -const manualSchemaHintSchemaRef = "./schema_manual_hints.schema.json" - -const ( - runtimeSchemaManualIdentityAnnotation = "dws.schema.manual.identity" - runtimeSchemaManualIdentityReason = "dws.schema.manual.identity_reason" - runtimeSchemaManualParameterAnnotation = "dws.schema.manual.parameter" - runtimeSchemaManualReasonAnnotation = "dws.schema.manual.reason" -) - -// ManualSchemaHintSnapshot is the human-owned bridge from an existing -// public Cobra leaf to Schema. It cannot create commands, flags, exclusions, or -// interfaces: every referenced command and flag is checked against the live -// tree before any annotation is applied. -type ManualSchemaHintSnapshot struct { - Schema string `json:"$schema"` - Version int `json:"version"` - Commands []ManualSchemaCommandHint `json:"commands"` - AgentHints ManualAgentHintSet `json:"agent_hints"` -} - -// ManualAgentHintSet keeps reviewed Agent-selection prose in the same manual -// source as command and parameter overrides without making that prose an -// identity annotation. Generators may read this section; they must never write -// it back or use it to change executable, parameter, safety, or interface -// facts. -type ManualAgentHintSet struct { - Revisions map[string]ManualAgentHintRevision `json:"revisions,omitempty"` - Products map[string]ManualAgentProductHint `json:"products,omitempty"` - Tools map[string]ManualAgentToolHint `json:"tools,omitempty"` -} - -// ManualAgentHintRevision records how one reviewed authoring batch was -// produced. Product and tool entries reference it by the enclosing map key so -// later focused revisions do not relabel unchanged prose. -type ManualAgentHintRevision struct { - GeneratedBy string `json:"generated_by"` - Model string `json:"model,omitempty"` - PromptVersion string `json:"prompt_version,omitempty"` - Reason string `json:"reason"` -} - -// ManualAgentProductHint defines product-level routing prose. -type ManualAgentProductHint struct { - AgentSummary string `json:"agent_summary"` - UseWhen []string `json:"use_when"` - AvoidWhen []string `json:"avoid_when"` - Reviewed bool `json:"reviewed"` - Revision string `json:"revision"` - Reason string `json:"reason"` - Evidence []string `json:"evidence"` -} - -// ManualAgentToolHint defines the reviewed prose used to select and -// demonstrate one existing effective command. ExampleDispositions contains -// only test eligibility exceptions; it cannot change identity, parameters, -// safety, or interface disposition. -type ManualAgentToolHint struct { - AgentSummary string `json:"agent_summary"` - UseWhen []string `json:"use_when"` - AvoidWhen []string `json:"avoid_when"` - Examples []string `json:"examples"` - ExampleDispositions []ManualAgentExampleDisposition `json:"example_dispositions,omitempty"` - Reviewed bool `json:"reviewed"` - Revision string `json:"revision"` - Reason string `json:"reason"` - Evidence []string `json:"evidence"` -} - -// ManualAgentExampleMode controls only how an already contract-validated -// example is exercised. Contract validation is the default. dry_run is used -// only when the final ToolSpec publishes an explicit reviewed capability; -// contract_only remains a precise reviewed exception for such a capability -// whose runtime preconditions cannot be exercised safely and deterministically -// in the isolated test process. -type ManualAgentExampleMode string - -const ( - ManualAgentExampleModeContract ManualAgentExampleMode = "contract" - ManualAgentExampleModeDryRun ManualAgentExampleMode = "dry_run" - ManualAgentExampleModeContractOnly ManualAgentExampleMode = "contract_only" -) - -// ManualAgentExampleReasonCode is a closed taxonomy for reviewed -// contract-only exceptions to an explicit dry-run capability. Safety and -// confirmation remain independently enforced and cannot be weakened through -// an example disposition. There is deliberately no generic "skip" value: a -// reviewer must identify the concrete precondition. -type ManualAgentExampleReasonCode string - -const ( - ManualAgentExampleReasonLocalState ManualAgentExampleReasonCode = "local_state" - ManualAgentExampleReasonStatefulPreflight ManualAgentExampleReasonCode = "stateful_preflight" -) - -// ManualAgentExampleDisposition narrows one exact example with an explicit -// typed dry-run capability to contract-only. Index is a pointer so a missing -// JSON field cannot silently select example zero. -type ManualAgentExampleDisposition struct { - Index *int `json:"index"` - Mode ManualAgentExampleMode `json:"mode"` - ReasonCode ManualAgentExampleReasonCode `json:"reason_code"` - Reason string `json:"reason"` - Reviewed bool `json:"reviewed"` -} - -// ManualAgentExampleExecution is one resolved example and its effective test -// mode. Contract validation has already succeeded when this value is returned. -type ManualAgentExampleExecution struct { - CanonicalPath string - Index int - Example string - Mode ManualAgentExampleMode - DryRun *DryRunSpec - ReasonCode ManualAgentExampleReasonCode - Reason string - Source ManualAgentExampleDispositionSource -} - -// ManualAgentExampleDispositionSource distinguishes the normal typed-contract -// classification from narrow reviewed manual exceptions. -type ManualAgentExampleDispositionSource string - -const ( - ManualAgentExampleDispositionDefault ManualAgentExampleDispositionSource = "default" - ManualAgentExampleDispositionReviewedManual ManualAgentExampleDispositionSource = "reviewed_manual" -) - -// ManualAgentExampleExecutionPlan is a stable, typed report used by the -// exhaustive real-Cobra dry-run test. Counts are derived only from the final -// typed dry-run capability and committed reviewed dispositions; runtime -// failures can never add implicit skips. -type ManualAgentExampleExecutionPlan struct { - Examples []ManualAgentExampleExecution - Total int - Contract int - DryRun int - ContractOnly int - ReviewedContractOnly int - ContractOnlyByReason map[ManualAgentExampleReasonCode]int -} - -// ManualSchemaCommandHint opts one exact existing Cobra leaf into -// Schema and optionally reviews its CLI-facing parameter projection. -type ManualSchemaCommandHint struct { - CLIPath string `json:"cli_path"` - CanonicalPath string `json:"canonical_path"` - Reason string `json:"reason"` - Reviewed bool `json:"reviewed"` - Parameters map[string]ManualSchemaParameterHint `json:"parameters,omitempty"` -} - -// ManualSchemaParameterHint changes only Schema annotations on a real -// flag. Pointer fields distinguish an omitted override from an explicit false. -type ManualSchemaParameterHint struct { - Description *string `json:"description,omitempty"` - Property *string `json:"property,omitempty"` - InterfaceType *string `json:"interface_type,omitempty"` - Required *bool `json:"required,omitempty"` - RequiredWhen *string `json:"required_when,omitempty"` -} - -// ManualSchemaHintReport records the exact reviewed inputs applied to -// a command tree. It is useful to generators and tests; no runtime discovery is -// performed. -type ManualSchemaHintReport struct { - Commands []string - Parameters []string -} - -var ( - manualSchemaHintsOnce sync.Once - manualSchemaHintsSnapshot ManualSchemaHintSnapshot - manualSchemaHintsErr error - loadManualSchemaHints = embeddedManualSchemaHints - loadManualCommandRegistry = loadEmbeddedCommandRegistry - marshalManualParameter = json.Marshal - applyManualParameter = annotateManualSchemaParameter -) - -// ApplyEmbeddedManualSchemaHints loads committed hint dirs and applies any -// residual metadata parameter overlays to an already-built Cobra tree. -// Selection HintFiles are retired in production (nil/empty load); Agent routing -// comes from ProductDecl/ContractFinal. This function only annotates Commands. -// The operation is deterministic and idempotent. -func ApplyEmbeddedManualSchemaHints(root *cobra.Command) (ManualSchemaHintReport, error) { - snapshot, err := loadManualSchemaHints() - if err != nil { - return ManualSchemaHintReport{}, err - } - return applyManualSchemaHints(root, snapshot) -} - -func embeddedManualSchemaHints() (ManualSchemaHintSnapshot, error) { - manualSchemaHintsOnce.Do(func() { - // Metadata and selection HintFile dirs are both retired (nil/empty). - // ProductDecl / ContractFinal own Agent routing prose; residual - // schema_hints/selection/ must not reappear as an embed source. - manualSchemaHintsSnapshot, manualSchemaHintsErr = loadManualSchemaHintsFromHintDirs( - nil, "", - nil, "", - ) - }) - return manualSchemaHintsSnapshot, manualSchemaHintsErr -} - -func decodeManualSchemaHints(data []byte) (ManualSchemaHintSnapshot, error) { - var snapshot ManualSchemaHintSnapshot - decoder := json.NewDecoder(bytes.NewReader(data)) - decoder.DisallowUnknownFields() - if err := decoder.Decode(&snapshot); err != nil { - return snapshot, fmt.Errorf("decode manual Schema hints: %w", err) - } - if err := decoder.Decode(&struct{}{}); err != io.EOF { - if err == nil { - err = fmt.Errorf("multiple JSON values") - } - return snapshot, fmt.Errorf("decode manual Schema hints: %w", err) - } - if snapshot.Version != manualSchemaHintVersion { - return snapshot, fmt.Errorf("unsupported manual Schema hint version %d", snapshot.Version) - } - if strings.TrimSpace(snapshot.Schema) != manualSchemaHintSchemaRef { - return snapshot, fmt.Errorf("manual Schema hints must declare $schema=%q", manualSchemaHintSchemaRef) - } - if err := ValidateManualAgentHintSet(snapshot.AgentHints, nil, nil); err != nil { - return snapshot, fmt.Errorf("validate manual Agent hints: %w", err) - } - return snapshot, nil -} - -// DecodeManualSchemaHintSource strictly decodes the reviewed manual input for -// build-time generators and policy checks. Callers receive data only; this -// function never writes or normalizes the source file in place. -func DecodeManualSchemaHintSource(data []byte) (ManualSchemaHintSnapshot, error) { - return decodeManualSchemaHints(data) -} - -// ValidateManualAgentHintSet validates the reviewed Agent prose and, when -// expected sets are supplied, requires exact product and canonical-tool -// coverage. It is intentionally independent from generated Catalog data. -func ValidateManualAgentHintSet(hints ManualAgentHintSet, expectedProducts, expectedTools map[string]bool) error { - if len(hints.Revisions) == 0 { - return fmt.Errorf("agent_hints.revisions must not be empty") - } - for rawRevision, provenance := range hints.Revisions { - revision := strings.TrimSpace(rawRevision) - if revision == "" || revision != rawRevision { - return fmt.Errorf("agent_hints has invalid revision key %q", rawRevision) - } - generatedBy := strings.TrimSpace(provenance.GeneratedBy) - if generatedBy == "" || strings.TrimSpace(provenance.Reason) == "" { - return fmt.Errorf("agent_hints revision %q requires generated_by and reason", revision) - } - if strings.EqualFold(generatedBy, "ai") && (strings.TrimSpace(provenance.Model) == "" || strings.TrimSpace(provenance.PromptVersion) == "") { - return fmt.Errorf("AI agent_hints revision %q requires model and prompt_version", revision) - } - } - - for rawProductID, hint := range hints.Products { - productID := strings.TrimSpace(rawProductID) - if productID == "" || productID != rawProductID || strings.ContainsAny(productID, ". \t\r\n") { - return fmt.Errorf("agent_hints has invalid product key %q", rawProductID) - } - if err := validateManualAgentHintFields("product "+productID, hint.AgentSummary, hint.UseWhen, hint.AvoidWhen, nil, hint.Reviewed, hint.Revision, hint.Reason, hint.Evidence, hints.Revisions); err != nil { - return err - } - } - for rawCanonical, hint := range hints.Tools { - canonical := strings.TrimSpace(rawCanonical) - if canonical != rawCanonical { - return fmt.Errorf("agent_hints has invalid canonical tool key %q", rawCanonical) - } - if _, _, ok := splitManualSchemaCanonicalPath(canonical); !ok { - return fmt.Errorf("agent_hints has invalid canonical tool key %q", rawCanonical) - } - if err := validateManualAgentHintFields("tool "+canonical, hint.AgentSummary, hint.UseWhen, hint.AvoidWhen, hint.Examples, hint.Reviewed, hint.Revision, hint.Reason, hint.Evidence, hints.Revisions); err != nil { - return err - } - if len(hint.Examples) == 0 { - return fmt.Errorf("agent_hints tool %s requires non-empty examples", canonical) - } - if len(hint.Examples) > 2 { - return fmt.Errorf("agent_hints tool %s has %d examples; maximum is 2", canonical, len(hint.Examples)) - } - for _, example := range hint.Examples { - argv, err := tokenizeManualAgentExample(example) - if err != nil { - return fmt.Errorf("agent_hints tool %s example has invalid argv syntax: %w", canonical, err) - } - if len(argv) < 2 || argv[0] != "dws" { - return fmt.Errorf("agent_hints tool %s example must start with dws: %q", canonical, example) - } - for _, argument := range argv[1:] { - if argument == "--yes" || strings.HasPrefix(argument, "--yes=") { - return fmt.Errorf("agent_hints tool %s example must not bypass confirmation with --yes", canonical) - } - if argument == "--help" || strings.HasPrefix(argument, "--help=") || argument == "-h" || strings.HasPrefix(argument, "-h=") { - return fmt.Errorf("agent_hints tool %s example must demonstrate execution, not only --help", canonical) - } - } - } - if err := validateManualAgentExampleDispositions(canonical, hint.Examples, hint.ExampleDispositions); err != nil { - return err - } - } - - actualProducts := mapKeysManualAgentProducts(hints.Products) - if expectedProducts != nil { - // Declared products may keep a selection/ products{} row during - // migration; count those leftovers as covered so exact-set passes. - expectedProducts = cloneManualAgentHintBoolSet(expectedProducts) - for productID := range actualProducts { - if HasProductDecl(productID) { - expectedProducts[productID] = true - } - } - } - if err := validateManualAgentHintExactSet("products", expectedProducts, actualProducts); err != nil { - return err - } - if err := validateManualAgentHintExactSet("tools", expectedTools, mapKeysManualAgentTools(hints.Tools)); err != nil { - return err - } - return nil -} - -func cloneManualAgentHintBoolSet(values map[string]bool) map[string]bool { - if values == nil { - return nil - } - out := make(map[string]bool, len(values)) - for key, include := range values { - out[key] = include - } - return out -} - -func validateManualAgentExampleDispositions(canonical string, examples []string, dispositions []ManualAgentExampleDisposition) error { - seen := make(map[int]bool, len(dispositions)) - for _, disposition := range dispositions { - if disposition.Index == nil { - return fmt.Errorf("agent_hints tool %s example disposition requires index", canonical) - } - index := *disposition.Index - if index < 0 || index >= len(examples) { - return fmt.Errorf("agent_hints tool %s example disposition index %d is out of range for %d examples", canonical, index, len(examples)) - } - if seen[index] { - return fmt.Errorf("agent_hints tool %s has duplicate example disposition index %d", canonical, index) - } - seen[index] = true - if !disposition.Reviewed { - return fmt.Errorf("agent_hints tool %s example disposition index %d must be reviewed", canonical, index) - } - if disposition.Mode != ManualAgentExampleModeContractOnly { - return fmt.Errorf("agent_hints tool %s example disposition index %d has invalid mode %q; only %q may narrow an explicit dry_run capability", canonical, index, disposition.Mode, ManualAgentExampleModeContractOnly) - } - if !validManualAgentExampleReasonCode(disposition.ReasonCode) { - return fmt.Errorf("agent_hints tool %s example disposition index %d has invalid reason_code %q", canonical, index, disposition.ReasonCode) - } - if strings.TrimSpace(disposition.Reason) == "" { - return fmt.Errorf("agent_hints tool %s example disposition index %d requires a non-empty reason", canonical, index) - } - } - return nil -} - -func validManualAgentExampleReasonCode(code ManualAgentExampleReasonCode) bool { - switch code { - case ManualAgentExampleReasonLocalState, - ManualAgentExampleReasonStatefulPreflight: - return true - default: - return false - } -} - -func validateManualAgentHintFields(scope, summary string, useWhen, avoidWhen, examples []string, reviewed bool, revision, reason string, evidence []string, revisions map[string]ManualAgentHintRevision) error { - if !reviewed { - return fmt.Errorf("agent_hints %s must be reviewed", scope) - } - revision = strings.TrimSpace(revision) - if _, ok := revisions[revision]; revision == "" || !ok { - return fmt.Errorf("agent_hints %s references unknown revision %q", scope, revision) - } - if strings.TrimSpace(summary) == "" || strings.TrimSpace(reason) == "" { - return fmt.Errorf("agent_hints %s requires agent_summary and reason", scope) - } - for name, values := range map[string][]string{ - "use_when": useWhen, - "avoid_when": avoidWhen, - "evidence": evidence, - } { - if err := validateNonEmptyManualAgentStrings(scope, name, values); err != nil { - return err - } - } - if examples != nil { - if err := validateNonEmptyManualAgentStrings(scope, "examples", examples); err != nil { - return err - } - } - return nil -} - -func validateNonEmptyManualAgentStrings(scope, field string, values []string) error { - if len(values) == 0 { - return fmt.Errorf("agent_hints %s requires non-empty %s", scope, field) - } - seen := map[string]bool{} - for _, value := range values { - value = strings.TrimSpace(value) - if value == "" { - return fmt.Errorf("agent_hints %s has an empty %s entry", scope, field) - } - if seen[value] { - return fmt.Errorf("agent_hints %s has duplicate %s entry %q", scope, field, value) - } - seen[value] = true - } - return nil -} - -func validateManualAgentHintExactSet(scope string, expected, actual map[string]bool) error { - if expected == nil { - return nil - } - missing := make([]string, 0) - unexpected := make([]string, 0) - for key, include := range expected { - if include && !actual[key] { - missing = append(missing, key) - } - } - for key := range actual { - if !expected[key] { - unexpected = append(unexpected, key) - } - } - sort.Strings(missing) - sort.Strings(unexpected) - if len(missing) != 0 || len(unexpected) != 0 { - return fmt.Errorf("agent_hints %s do not exactly match EffectiveCommandRegistry: missing=%v unexpected=%v", scope, missing, unexpected) - } - return nil -} - -func mapKeysManualAgentProducts(values map[string]ManualAgentProductHint) map[string]bool { - keys := make(map[string]bool, len(values)) - for key := range values { - keys[key] = true - } - return keys -} - -func mapKeysManualAgentTools(values map[string]ManualAgentToolHint) map[string]bool { - keys := make(map[string]bool, len(values)) - for key := range values { - keys[key] = true - } - return keys -} - -// ValidateManualAgentHintExamples is the pre-generation validation layer. It -// binds every authored example to the exact reviewed primary/alias path and -// rejects flags and required arguments not accepted by the live Cobra command. -// Final typed constraints, dry-run capability, and exact delivery coverage are -// validated later by BuildManualAgentExampleExecutionPlan after generated -// Agent metadata exists. It never executes an example. -func ValidateManualAgentHintExamples(bound BoundCommandRegistry, hints ManualAgentHintSet) error { - _, err := buildManualAgentExampleExecutionPlan(bound, nil, hints) - return err -} - -// ValidateEmbeddedManualAgentExampleDelivery is the final generation gate. It -// validates the committed Manual Agent hints against the fully assembled typed -// SchemaRegistry, after generated Agent metadata has been compiled in. This is -// deliberately stronger than the pre-generation path/flag validation above. -func ValidateEmbeddedManualAgentExampleDelivery(bound BoundCommandRegistry, registry SchemaRegistry) (ManualAgentExampleExecutionPlan, error) { - snapshot, err := loadManualSchemaHints() - if err != nil { - return ManualAgentExampleExecutionPlan{}, err - } - return BuildManualAgentExampleExecutionPlan(bound, registry, snapshot.AgentHints) -} - -// BuildManualAgentExampleExecutionPlan validates every example against its -// real BoundCommand/Cobra contract. Runtime dry-run execution is opt-in and -// comes only from the final typed ToolSpec; a globally accepted --dry-run flag -// is not capability evidence. Only unavoidable local-state or stateful- -// preflight requirements may narrow an explicit capability through an exact -// reviewed manual disposition. Callers must never turn a runtime failure into -// an implicit skip. -func BuildManualAgentExampleExecutionPlan(bound BoundCommandRegistry, registry SchemaRegistry, hints ManualAgentHintSet) (ManualAgentExampleExecutionPlan, error) { - tools := make(map[string]ToolSpec, len(bound.Commands)) - for _, product := range registry.Products { - for _, tool := range product.Tools { - canonical := strings.TrimSpace(tool.Identity.CanonicalPath) - if canonical == "" { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("typed SchemaRegistry contains a tool with empty canonical path") - } - if _, duplicate := tools[canonical]; duplicate { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("typed SchemaRegistry contains duplicate tool %q", canonical) - } - tools[canonical] = tool - } - } - return buildManualAgentExampleExecutionPlan(bound, tools, hints) -} - -func buildManualAgentExampleExecutionPlan(bound BoundCommandRegistry, typedTools map[string]ToolSpec, hints ManualAgentHintSet) (ManualAgentExampleExecutionPlan, error) { - plan := ManualAgentExampleExecutionPlan{ - ContractOnlyByReason: map[ManualAgentExampleReasonCode]int{}, - } - // Declared tools (Contract final overlay) take part without a hint-file - // row: their examples come from the in-code declaration. - declaredTools := map[string]bool{} - for _, command := range bound.Commands { - if HasRuntimeContractFinal(command.PrimaryCommand) { - declaredTools[strings.TrimSpace(command.CanonicalPath)] = true - } - } - canonicalPaths := make([]string, 0, len(hints.Tools)+len(declaredTools)) - seenCanonical := map[string]bool{} - for canonical := range hints.Tools { - canonicalPaths = append(canonicalPaths, canonical) - seenCanonical[canonical] = true - } - for canonical := range declaredTools { - if !seenCanonical[canonical] { - canonicalPaths = append(canonicalPaths, canonical) - } - } - sort.Strings(canonicalPaths) - for _, canonical := range canonicalPaths { - spec, ok := bound.ByCanonical[canonical] - if !ok { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints example references unknown canonical tool %q", canonical) - } - // ContractFinal authored beside the command is the source of truth for - // examples/selection. Residual schema_hints rows must not override it - // after full colocation (otherwise shared factories / stale hint-file - // examples fail primary/alias path validation). - var hint ManualAgentToolHint - if HasRuntimeContractFinal(spec.PrimaryCommand) { - hint = contractFinalSelectionHint(spec.PrimaryCommand) - } else if fileHint, hintOK := hints.Tools[canonical]; hintOK { - hint = fileHint - } else { - hint = ManualAgentToolHint{} - } - var typedTool ToolSpec - if typedTools != nil { - var found bool - typedTool, found = typedTools[canonical] - if !found { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints example tool %q is missing from final typed SchemaRegistry", canonical) - } - } - if err := validateManualAgentExampleDispositions(canonical, hint.Examples, hint.ExampleDispositions); err != nil { - return ManualAgentExampleExecutionPlan{}, err - } - dispositions := make(map[int]ManualAgentExampleDisposition, len(hint.ExampleDispositions)) - for _, disposition := range hint.ExampleDispositions { - dispositions[*disposition.Index] = disposition - } - paths := []manualAgentExamplePath{{Path: spec.PrimaryCLIPath, Argv: strings.Fields(spec.PrimaryCLIPath), Command: spec.PrimaryCommand}} - for _, alias := range spec.AliasCommands { - paths = append(paths, manualAgentExamplePath{Path: alias.Path, Argv: strings.Fields(alias.Path), Command: alias.Command}) - } - sort.Slice(paths, func(i, j int) bool { - if len(paths[i].Argv) != len(paths[j].Argv) { - return len(paths[i].Argv) > len(paths[j].Argv) - } - return paths[i].Path < paths[j].Path - }) - for index, example := range hint.Examples { - argv, err := tokenizeManualAgentExample(example) - if err != nil { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s example has invalid argv syntax: %w", canonical, err) - } - remainder, matched, ok := matchManualAgentExamplePath(argv, paths) - if !ok { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s example does not use its reviewed primary/alias path: %q", canonical, example) - } - if matched.Command == nil { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s reviewed path %q has no bound Cobra command", canonical, matched.Path) - } - constraints, err := strictCompatibilityConstraints(matched.Command, canonical) - if err != nil { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s example for %q has invalid executable constraints: %w", canonical, matched.Path, err) - } - if typedTools != nil { - constraints.MutuallyExclusive = append(constraints.MutuallyExclusive, typedTool.Constraints.MutuallyExclusive...) - constraints.RequireOneOf = append(constraints.RequireOneOf, typedTool.Constraints.RequireOneOf...) - constraints.RequireTogether = append(constraints.RequireTogether, typedTool.Constraints.RequireTogether...) - constraints = normalizeRuntimeSchemaConstraints(constraints) - } - positionals, err := strictCompatibilityPositionals(matched.Command) - if err != nil { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s example for %q has invalid executable positionals: %w", canonical, matched.Path, err) - } - if typedTools != nil { - positionals = mergeManualAgentExamplePositionals(positionals, typedTool.Positionals) - } - if err := validateManualAgentExampleCobraContract(matched.Command, remainder, constraints, positionals); err != nil { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s example for %q: %w", canonical, matched.Path, err) - } - - execution := ManualAgentExampleExecution{ - CanonicalPath: canonical, - Index: index, - Example: example, - Mode: ManualAgentExampleModeContract, - Source: ManualAgentExampleDispositionDefault, - } - if typedTool.DryRun != nil { - dryRun := *typedTool.DryRun - execution.DryRun = &dryRun - execution.Mode = ManualAgentExampleModeDryRun - } - disposition, hasManualDisposition := dispositions[index] - if hasManualDisposition { - if typedTools != nil && execution.DryRun == nil { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("agent_hints tool %s example disposition index %d narrows no explicit dry_run capability", canonical, index) - } - execution.Mode = disposition.Mode - execution.ReasonCode = disposition.ReasonCode - execution.Reason = strings.TrimSpace(disposition.Reason) - execution.Source = ManualAgentExampleDispositionReviewedManual - plan.ContractOnly++ - plan.ReviewedContractOnly++ - plan.ContractOnlyByReason[disposition.ReasonCode]++ - } else if execution.DryRun != nil { - plan.DryRun++ - } else { - plan.Contract++ - } - plan.Examples = append(plan.Examples, execution) - plan.Total++ - } - } - for canonical := range typedTools { - if _, ok := hints.Tools[canonical]; !ok && !declaredTools[canonical] { - return ManualAgentExampleExecutionPlan{}, fmt.Errorf("final typed SchemaRegistry tool %q has no Manual Agent hint examples", canonical) - } - } - return plan, nil -} - -type manualAgentExamplePath struct { - Path string - Argv []string - Command *cobra.Command -} - -func matchManualAgentExamplePath(argv []string, paths []manualAgentExamplePath) ([]string, manualAgentExamplePath, bool) { - if len(argv) == 0 || argv[0] != "dws" { - return nil, manualAgentExamplePath{}, false - } - for index := range paths { - if paths[index].Argv == nil { - paths[index].Argv = strings.Fields(strings.TrimSpace(paths[index].Path)) - } - pathArgv := paths[index].Argv - if len(pathArgv) == 0 || len(argv) < len(pathArgv)+1 { - continue - } - matches := true - for offset := range pathArgv { - if argv[offset+1] != pathArgv[offset] { - matches = false - break - } - } - if matches { - return argv[len(pathArgv)+1:], paths[index], true - } - } - return nil, manualAgentExamplePath{}, false -} - -// tokenizeManualAgentExample parses one deliberately small, shell-free argv -// grammar. Quotes and backslash escaping are supported for readable values, -// while shell control operators, expansion, redirection, and the "--" -// terminator fail closed. Angle-bracket placeholders are data tokens, not -// redirection operators. -func tokenizeManualAgentExample(input string) ([]string, error) { - var ( - argv []string - current strings.Builder - quote byte - tokenStarted bool - ) - flush := func() { - if tokenStarted { - argv = append(argv, current.String()) - current.Reset() - tokenStarted = false - } - } - for index := 0; index < len(input); index++ { - character := input[index] - if quote != 0 { - if character == quote { - quote = 0 - continue - } - if quote == '"' && character == '\\' { - if index+1 >= len(input) { - return nil, fmt.Errorf("trailing escape in double-quoted value") - } - index++ - current.WriteByte(input[index]) - continue - } - if quote == '"' && (character == '`' || character == '$') { - return nil, fmt.Errorf("shell expansion is not allowed") - } - current.WriteByte(character) - continue - } - - switch character { - case ' ', '\t': - flush() - case '\r', '\n': - return nil, fmt.Errorf("unquoted newline shell operator is not allowed") - case '\'', '"': - quote = character - tokenStarted = true - case '\\': - if index+1 >= len(input) { - return nil, fmt.Errorf("trailing escape") - } - index++ - current.WriteByte(input[index]) - tokenStarted = true - case '<': - placeholder, next, ok := manualAgentPlaceholderAt(input, index) - if !ok { - return nil, fmt.Errorf("shell redirection operator %q is not allowed", string(character)) - } - current.WriteString(placeholder) - tokenStarted = true - index = next - case '>', ';', '|', '&', '(', ')': - return nil, fmt.Errorf("shell operator %q is not allowed", string(character)) - case '`', '$': - return nil, fmt.Errorf("shell expansion is not allowed") - case '#': - return nil, fmt.Errorf("shell comments are not allowed") - default: - current.WriteByte(character) - tokenStarted = true - } - } - if quote != 0 { - return nil, fmt.Errorf("unterminated quoted value") - } - flush() - for _, argument := range argv { - if argument == "--" { - return nil, fmt.Errorf("the -- argument terminator is not allowed") - } - } - return argv, nil -} - -// ParseManualAgentExampleArgv exposes the same deliberately shell-free argv -// parser used by Manual Agent hint validation. Contract tests use it to run -// reviewed examples through Cobra without invoking a shell or maintaining a -// second parser with different escaping rules. -func ParseManualAgentExampleArgv(input string) ([]string, error) { - return tokenizeManualAgentExample(input) -} - -func manualAgentPlaceholderAt(input string, start int) (string, int, bool) { - endOffset := strings.IndexByte(input[start+1:], '>') - if endOffset < 1 { - return "", start, false - } - end := start + 1 + endOffset - body := input[start+1 : end] - for index := 0; index < len(body); index++ { - character := body[index] - if strings.ContainsRune(" \t\r\n<>;&|`$()#'\"\\", rune(character)) { - return "", start, false - } - } - return input[start : end+1], end, true -} - -func validateManualAgentExampleCobraContract(command *cobra.Command, arguments []string, constraints RuntimeSchemaConstraints, positionalSpecs []RuntimeSchemaPositional) error { - if command == nil { - return fmt.Errorf("bound Cobra command is nil") - } - providedFacts := map[string]bool{} - positionals := make([]string, 0) - for index := 0; index < len(arguments); index++ { - argument := arguments[index] - if argument == "--" { - return fmt.Errorf("the -- argument terminator is not allowed") - } - if strings.HasPrefix(argument, "--") { - nameAndValue := strings.TrimPrefix(argument, "--") - name, _, hasValue := strings.Cut(nameAndValue, "=") - if name == "" { - return fmt.Errorf("invalid empty long flag") - } - if name == "help" { - return fmt.Errorf("must demonstrate execution, not only --help") - } - flag := runtimeCommandFlag(command, name) - if flag == nil { - return fmt.Errorf("uses unknown flag --%s", name) - } - providedFacts[flag.Name] = true - if !hasValue && flag.NoOptDefVal == "" { - if index+1 >= len(arguments) { - return fmt.Errorf("flag --%s requires a value", name) - } - index++ - } - continue - } - if !strings.HasPrefix(argument, "-") || argument == "-" { - positionals = append(positionals, argument) - continue - } - - shorthandsAndValue := strings.TrimPrefix(argument, "-") - shorthands, _, hasExplicitValue := strings.Cut(shorthandsAndValue, "=") - for offset := 0; offset < len(shorthands); offset++ { - shorthand := shorthands[offset : offset+1] - if shorthand[0] >= 0x80 { - return fmt.Errorf("uses invalid non-ASCII shorthand flag") - } - if shorthand == "h" { - return fmt.Errorf("must demonstrate execution, not only -h") - } - flag := runtimeCommandFlagByShorthand(command, shorthand) - if flag == nil { - return fmt.Errorf("uses unknown shorthand flag -%s", shorthand) - } - providedFacts[flag.Name] = true - if flag.NoOptDefVal == "" { - if offset+1 < len(shorthands) || hasExplicitValue { - break - } - if index+1 >= len(arguments) { - return fmt.Errorf("shorthand flag -%s requires a value", shorthand) - } - index++ - break - } - } - } - missingRequired := make([]string, 0) - visitManualAgentCommandFlags(command, func(flag *pflag.Flag) { - if flag != nil && len(flag.Annotations[cobra.BashCompOneRequiredFlag]) > 0 && !providedFacts[flag.Name] { - missingRequired = append(missingRequired, "--"+flag.Name) - } - }) - if len(missingRequired) != 0 { - sort.Strings(missingRequired) - return fmt.Errorf("missing required flag(s): %s", strings.Join(missingRequired, ", ")) - } - missingPositionals := make([]string, 0) - for _, positional := range positionalSpecs { - provided := positional.Index >= 0 && positional.Index < len(positionals) - if positional.Variadic { - provided = positional.Index >= 0 && len(positionals) > positional.Index - } - if provided && strings.TrimSpace(positional.Name) != "" { - providedFacts[strings.TrimLeft(strings.TrimSpace(positional.Name), "-")] = true - } - if positional.Required && !provided { - missingPositionals = append(missingPositionals, strings.TrimSpace(positional.Name)) - } - } - if len(missingPositionals) != 0 { - sort.Strings(missingPositionals) - return fmt.Errorf("missing required positional argument(s): %s", strings.Join(missingPositionals, ", ")) - } - if err := validateManualAgentExampleConstraints(providedFacts, constraints); err != nil { - return err - } - // Runtime positional annotations are the stable Cobra contract and can - // express flag/positional alternatives. Do not call a closure-backed Args - // validator in that case: invoking it without mutating the live FlagSet - // would observe default captured flag values (for example pat chmod's - // --products alternative) and report a false missing positional. Commands - // without typed positional facts still use their real Cobra Args validator. - if command.Args != nil && len(positionalSpecs) == 0 { - if err := command.Args(command, positionals); err != nil { - return fmt.Errorf("invalid positional arguments: %w", err) - } - } - return nil -} - -func mergeManualAgentExamplePositionals(groups ...[]RuntimeSchemaPositional) []RuntimeSchemaPositional { - byIdentity := map[string]RuntimeSchemaPositional{} - for _, group := range groups { - for _, positional := range group { - key := fmt.Sprintf("%d\x00%s", positional.Index, strings.TrimSpace(positional.Name)) - if _, exists := byIdentity[key]; !exists { - byIdentity[key] = positional - } - } - } - result := make([]RuntimeSchemaPositional, 0, len(byIdentity)) - for _, positional := range byIdentity { - result = append(result, positional) - } - sort.Slice(result, func(i, j int) bool { - if result[i].Index != result[j].Index { - return result[i].Index < result[j].Index - } - return result[i].Name < result[j].Name - }) - return result -} - -func validateManualAgentExampleConstraints(provided map[string]bool, constraints RuntimeSchemaConstraints) error { - for _, group := range constraints.RequireOneOf { - if !manualAgentExampleAnyFlagProvided(provided, group) { - return fmt.Errorf("missing require_one_of flags: %s", manualAgentExampleFlagGroup(group)) - } - } - for _, group := range constraints.RequireTogether { - providedCount := manualAgentExampleProvidedFlagCount(provided, group) - if providedCount != 0 && providedCount != len(group) { - return fmt.Errorf("incomplete require_together flags: %s", manualAgentExampleFlagGroup(group)) - } - } - for _, group := range constraints.MutuallyExclusive { - if manualAgentExampleProvidedFlagCount(provided, group) > 1 { - return fmt.Errorf("mutually_exclusive flags used together: %s", manualAgentExampleFlagGroup(group)) - } - } - return nil -} - -func manualAgentExampleAnyFlagProvided(provided map[string]bool, group []string) bool { - return manualAgentExampleProvidedFlagCount(provided, group) > 0 -} - -func manualAgentExampleProvidedFlagCount(provided map[string]bool, group []string) int { - count := 0 - seen := map[string]bool{} - for _, raw := range group { - name := strings.TrimLeft(strings.TrimSpace(raw), "-") - if name != "" && !seen[name] && provided[name] { - seen[name] = true - count++ - } - } - return count -} - -func manualAgentExampleFlagGroup(group []string) string { - flags := make([]string, 0, len(group)) - for _, raw := range group { - if name := strings.TrimLeft(strings.TrimSpace(raw), "-"); name != "" { - flags = append(flags, "--"+name) - } - } - sort.Strings(flags) - return strings.Join(flags, ", ") -} - -func visitManualAgentCommandFlags(command *cobra.Command, visit func(*pflag.Flag)) { - seen := map[string]bool{} - visitSet := func(flags *pflag.FlagSet) { - flags.VisitAll(func(flag *pflag.Flag) { - if seen[flag.Name] { - return - } - seen[flag.Name] = true - visit(flag) - }) - } - visitSet(command.Flags()) - for current := command; current != nil; current = current.Parent() { - visitSet(current.PersistentFlags()) - } -} - -func runtimeCommandFlagByShorthand(command *cobra.Command, shorthand string) *pflag.Flag { - if command == nil || len(shorthand) != 1 { - return nil - } - if flag := command.Flags().ShorthandLookup(shorthand); flag != nil { - return flag - } - for current := command; current != nil; current = current.Parent() { - if flag := current.PersistentFlags().ShorthandLookup(shorthand); flag != nil { - return flag - } - } - return nil -} - -type validatedManualSchemaHint struct { - hint ManualSchemaCommandHint - command *cobra.Command -} - -func applyManualSchemaHints(root *cobra.Command, snapshot ManualSchemaHintSnapshot) (ManualSchemaHintReport, error) { - if root == nil { - return ManualSchemaHintReport{}, fmt.Errorf("apply manual Schema hints: root is nil") - } - if snapshot.Version != manualSchemaHintVersion { - return ManualSchemaHintReport{}, fmt.Errorf("unsupported manual Schema hint version %d", snapshot.Version) - } - reviewedRegistry, err := loadManualCommandRegistry() - if err != nil { - return ManualSchemaHintReport{}, fmt.Errorf("load reviewed Schema command registry: %w", err) - } - - validated := make([]validatedManualSchemaHint, 0, len(snapshot.Commands)) - seenPaths := map[string]bool{} - for _, raw := range snapshot.Commands { - hint := raw - hint.CLIPath = normalizeSchemaCLIPath(hint.CLIPath) - hint.CanonicalPath = strings.TrimSpace(hint.CanonicalPath) - hint.Reason = strings.TrimSpace(hint.Reason) - if hint.CLIPath == "" || strings.ContainsAny(hint.CLIPath, "*?[]") { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint has invalid exact cli_path %q", raw.CLIPath) - } - if seenPaths[hint.CLIPath] { - return ManualSchemaHintReport{}, fmt.Errorf("duplicate manual Schema hint for %q", hint.CLIPath) - } - seenPaths[hint.CLIPath] = true - if !hint.Reviewed || hint.Reason == "" { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q is not reviewed or has no reason", hint.CLIPath) - } - _, _, ok := splitManualSchemaCanonicalPath(hint.CanonicalPath) - if !ok { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q has invalid canonical_path %q", hint.CLIPath, hint.CanonicalPath) - } - match, resolveErr := resolveExactCobraPath(root, hint.CLIPath) - if resolveErr != nil { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q cannot be resolved exactly: %w", hint.CLIPath, resolveErr) - } - command := match.Command - if command == nil { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q does not resolve to an existing Cobra command", hint.CLIPath) - } - if !publicRunnableSchemaLeaf(command) { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q must target a public runnable Cobra leaf", hint.CLIPath) - } - if productID, toolName, _ := runtimeSchemaAnnotations(command); productID != "" || toolName != "" { - existing := strings.Trim(strings.TrimSpace(productID)+"."+strings.TrimSpace(toolName), ".") - if existing != hint.CanonicalPath { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with existing canonical path %q", hint.CLIPath, hint.CanonicalPath, existing) - } - } - if identity, ok := reviewedRegistry.ByCLIPath[hint.CLIPath]; ok && identity.CanonicalPath != hint.CanonicalPath { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with reviewed CommandRegistry canonical path %q", hint.CLIPath, hint.CanonicalPath, identity.CanonicalPath) - } - if match.UsedAlias { - namePath := normalizeSchemaCLIPath(strings.Join(commandPathParts(command), " ")) - identity, ok := reviewedRegistry.ByCLIPath[namePath] - if !ok { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q uses a Cobra alias, but real command path %q is not present in reviewed CommandRegistry", hint.CLIPath, namePath) - } - if identity.CanonicalPath != hint.CanonicalPath { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q canonical path %q conflicts with real command path %q canonical path %q", hint.CLIPath, hint.CanonicalPath, namePath, identity.CanonicalPath) - } - } - for flagName, parameter := range hint.Parameters { - flagName = strings.TrimSpace(flagName) - if flagName == "" { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q contains an empty flag name", hint.CLIPath) - } - if runtimeCommandFlag(command, flagName) == nil { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q references missing flag --%s", hint.CLIPath, flagName) - } - if parameter.Description == nil && parameter.Property == nil && parameter.InterfaceType == nil && parameter.Required == nil && parameter.RequiredWhen == nil { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q flag --%s has no Schema overrides", hint.CLIPath, flagName) - } - if parameter.Description != nil && strings.TrimSpace(*parameter.Description) == "" { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q flag --%s has an empty description override", hint.CLIPath, flagName) - } - if parameter.Property != nil && strings.TrimSpace(*parameter.Property) == "" { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q flag --%s has an empty property override", hint.CLIPath, flagName) - } - if parameter.InterfaceType != nil && !supportedManualSchemaInterfaceType(*parameter.InterfaceType) { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q flag --%s has unsupported interface_type %q", hint.CLIPath, flagName, *parameter.InterfaceType) - } - if parameter.RequiredWhen != nil && strings.TrimSpace(*parameter.RequiredWhen) == "" { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q flag --%s has an empty required_when override", hint.CLIPath, flagName) - } - } - validated = append(validated, validatedManualSchemaHint{hint: hint, command: command}) - } - - report := ManualSchemaHintReport{} - for _, item := range validated { - annotateManualSchemaIdentity(item.command, item.hint.CanonicalPath, item.hint.Reason) - report.Commands = append(report.Commands, item.hint.CLIPath) - flagNames := make([]string, 0, len(item.hint.Parameters)) - for flagName := range item.hint.Parameters { - flagNames = append(flagNames, flagName) - } - sort.Strings(flagNames) - for _, flagName := range flagNames { - parameter := item.hint.Parameters[flagName] - if err := applyManualParameter(item.command, flagName, parameter, item.hint.Reason); err != nil { - return ManualSchemaHintReport{}, fmt.Errorf("manual Schema hint %q flag --%s: %w", item.hint.CLIPath, flagName, err) - } - report.Parameters = append(report.Parameters, item.hint.CLIPath+" --"+flagName) - } - } - sort.Strings(report.Commands) - return report, nil -} - -// annotateManualSchemaIdentity keeps reviewed implementation evidence in a -// source-owned annotation. It deliberately does not rewrite code-owned runtime -// annotations: EffectiveCommandRegistry owns the selected identity, and the -// Cobra binder requires every manual/native annotation to agree with it before -// retaining the evidence in provenance. -func annotateManualSchemaIdentity(cmd *cobra.Command, canonicalPath, reason string) { - if cmd == nil { - return - } - setRuntimeCommandAnnotation(cmd, runtimeSchemaManualIdentityAnnotation, strings.TrimSpace(canonicalPath)) - setRuntimeCommandAnnotation(cmd, runtimeSchemaManualIdentityReason, strings.TrimSpace(reason)) -} - -func runtimeManualSchemaIdentity(cmd *cobra.Command) (productID, toolName, reason string, ok bool) { - if cmd == nil || cmd.Annotations == nil { - return "", "", "", false - } - canonicalPath := strings.TrimSpace(cmd.Annotations[runtimeSchemaManualIdentityAnnotation]) - productID, toolName, ok = splitManualSchemaCanonicalPath(canonicalPath) - if !ok { - return "", "", "", false - } - reason = strings.TrimSpace(cmd.Annotations[runtimeSchemaManualIdentityReason]) - return productID, toolName, reason, true -} - -// annotateManualSchemaParameter stores the reviewed value in a source-owned -// annotation instead of overwriting the annotations used by bindings, Cobra -// adapters, and code-owned runtime metadata. The renderer resolves these -// independent candidates field by field and can therefore retain provenance. -func annotateManualSchemaParameter(command *cobra.Command, flagName string, hint ManualSchemaParameterHint, reason string) error { - flagName = strings.TrimSpace(flagName) - if command == nil { - return fmt.Errorf("command is nil") - } - if flagName == "" || runtimeCommandFlag(command, flagName) == nil { - return fmt.Errorf("flag --%s is missing", flagName) - } - hint = normalizeManualSchemaParameterHint(hint) - encoded, err := marshalManualParameter(hint) - if err != nil { - return fmt.Errorf("encode reviewed parameter: %w", err) - } - setRuntimeCommandAnnotation(command, runtimeManualSchemaParameterKey(runtimeSchemaManualParameterAnnotation, flagName), string(encoded)) - setRuntimeCommandAnnotation(command, runtimeManualSchemaParameterKey(runtimeSchemaManualReasonAnnotation, flagName), strings.TrimSpace(reason)) - return nil -} - -func runtimeManualSchemaParameter(command *cobra.Command, flagName string) (ManualSchemaParameterHint, string, bool, error) { - if command == nil || command.Annotations == nil { - return ManualSchemaParameterHint{}, "", false, nil - } - flagName = strings.TrimSpace(flagName) - raw := strings.TrimSpace(command.Annotations[runtimeManualSchemaParameterKey(runtimeSchemaManualParameterAnnotation, flagName)]) - if raw == "" { - return ManualSchemaParameterHint{}, "", false, nil - } - var hint ManualSchemaParameterHint - if err := json.Unmarshal([]byte(raw), &hint); err != nil { - return ManualSchemaParameterHint{}, "", false, fmt.Errorf("decode reviewed manual parameter hint: %w", err) - } - reason := strings.TrimSpace(command.Annotations[runtimeManualSchemaParameterKey(runtimeSchemaManualReasonAnnotation, flagName)]) - return normalizeManualSchemaParameterHint(hint), reason, true, nil -} - -func runtimeManualSchemaParameterKey(prefix, flagName string) string { - return strings.TrimSpace(prefix) + "." + strings.TrimSpace(flagName) -} - -func normalizeManualSchemaParameterHint(hint ManualSchemaParameterHint) ManualSchemaParameterHint { - hint.Description = trimmedManualSchemaString(hint.Description) - hint.Property = trimmedManualSchemaString(hint.Property) - hint.InterfaceType = trimmedManualSchemaString(hint.InterfaceType) - hint.RequiredWhen = trimmedManualSchemaString(hint.RequiredWhen) - return hint -} - -func trimmedManualSchemaString(value *string) *string { - if value == nil { - return nil - } - trimmed := strings.TrimSpace(*value) - return &trimmed -} - -func supportedManualSchemaInterfaceType(value string) bool { - switch strings.TrimSpace(value) { - case "string", "array", "object", "integer", "number", "boolean": - return true - default: - return false - } -} - -func splitManualSchemaCanonicalPath(path string) (string, string, bool) { - path = strings.TrimSpace(path) - productID, toolName, ok := strings.Cut(path, ".") - productID = strings.TrimSpace(productID) - toolName = strings.TrimSpace(toolName) - if !ok || productID == "" || toolName == "" || strings.ContainsAny(productID+toolName, " \t\r\n") { - return "", "", false - } - return productID, toolName, true -} - -func publicRunnableSchemaLeaf(command *cobra.Command) bool { - if command == nil || !command.Runnable() || command.HasSubCommands() { - return false - } - for current := command; current != nil; current = current.Parent() { - if current.Hidden { - return false - } - } - return true -} diff --git a/internal/cli/schema_manual_hints_coverage_more_test.go b/internal/cli/schema_manual_hints_coverage_more_test.go deleted file mode 100644 index 0c0fb39d..00000000 --- a/internal/cli/schema_manual_hints_coverage_more_test.go +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -package cli - -import ( - "encoding/json" - "errors" - "reflect" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestCrossPlatformCoverageManualSchemaHintDecodeAndValidationEdges(t *testing.T) { - valid := ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - AgentHints: manualAgentHintSetFixture(), - } - encoded, err := json.Marshal(valid) - if err != nil { - t.Fatal(err) - } - if snapshot, err := DecodeManualSchemaHintSource(encoded); err != nil || snapshot.Version != manualSchemaHintVersion { - t.Fatalf("DecodeManualSchemaHintSource() = %#v, %v", snapshot, err) - } - - decodeCases := []struct { - name string - data []byte - want string - }{ - {name: "malformed", data: []byte("{"), want: "decode manual Schema hints"}, - {name: "second malformed value", data: append(append([]byte{}, encoded...), []byte(" {")...), want: "decode manual Schema hints"}, - {name: "multiple values", data: append(append([]byte{}, encoded...), []byte(" {}")...), want: "multiple JSON values"}, - {name: "version", data: []byte(`{"$schema":"./schema_manual_hints.schema.json","version":2}`), want: "unsupported"}, - } - for _, tc := range decodeCases { - t.Run(tc.name, func(t *testing.T) { - _, err := DecodeManualSchemaHintSource(tc.data) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - validationCases := []struct { - name string - mutate func(*ManualAgentHintSet) - want string - }{ - {name: "blank revision key", mutate: func(h *ManualAgentHintSet) { - h.Revisions[" "] = h.Revisions["ai-agent-contract-v1"] - delete(h.Revisions, "ai-agent-contract-v1") - }, want: "invalid revision key"}, - {name: "revision provenance", mutate: func(h *ManualAgentHintSet) { - h.Revisions["ai-agent-contract-v1"] = ManualAgentHintRevision{} - }, want: "requires generated_by and reason"}, - {name: "ai provenance", mutate: func(h *ManualAgentHintSet) { - h.Revisions["ai-agent-contract-v1"] = ManualAgentHintRevision{GeneratedBy: "AI", Reason: "r"} - }, want: "requires model and prompt_version"}, - {name: "product key", mutate: func(h *ManualAgentHintSet) { - h.Products["bad.product"] = h.Products["sample"] - delete(h.Products, "sample") - }, want: "invalid product key"}, - {name: "product fields", mutate: func(h *ManualAgentHintSet) { - item := h.Products["sample"] - item.UseWhen = nil - h.Products["sample"] = item - }, want: "requires non-empty use_when"}, - {name: "trimmed tool key", mutate: func(h *ManualAgentHintSet) { - h.Tools[" sample.search_items"] = h.Tools["sample.search_items"] - delete(h.Tools, "sample.search_items") - }, want: "invalid canonical tool key"}, - {name: "invalid tool key", mutate: func(h *ManualAgentHintSet) { - h.Tools["sample"] = h.Tools["sample.search_items"] - delete(h.Tools, "sample.search_items") - }, want: "invalid canonical tool key"}, - {name: "tool fields", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.AgentSummary = "" - h.Tools["sample.search_items"] = item - }, want: "requires agent_summary and reason"}, - {name: "too many examples", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.Examples = []string{"dws a", "dws b", "dws c"} - h.Tools["sample.search_items"] = item - }, want: "maximum is 2"}, - {name: "invalid example syntax", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.Examples = []string{"dws sample | bad"} - h.Tools["sample.search_items"] = item - }, want: "invalid argv syntax"}, - {name: "example prefix", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.Examples = []string{"other command"} - h.Tools["sample.search_items"] = item - }, want: "must start with dws"}, - {name: "help example", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.Examples = []string{"dws sample --help=true"} - h.Tools["sample.search_items"] = item - }, want: "must demonstrate execution"}, - {name: "empty example", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.Examples = []string{" "} - h.Tools["sample.search_items"] = item - }, want: "empty examples entry"}, - {name: "invalid disposition", mutate: func(h *ManualAgentHintSet) { - item := h.Tools["sample.search_items"] - item.ExampleDispositions = []ManualAgentExampleDisposition{{}} - h.Tools["sample.search_items"] = item - }, want: "requires index"}, - {name: "unexpected product", mutate: func(h *ManualAgentHintSet) {}, want: "unexpected=[sample]"}, - } - for _, tc := range validationCases { - t.Run(tc.name, func(t *testing.T) { - hints := manualAgentHintSetFixture() - tc.mutate(&hints) - expectedProducts := map[string]bool{"sample": true} - if tc.name == "unexpected product" { - expectedProducts = map[string]bool{} - } - err := ValidateManualAgentHintSet(hints, expectedProducts, map[string]bool{"sample.search_items": true}) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - if err := validateNonEmptyManualAgentStrings("scope", "field", []string{"x", " x "}); err == nil || !strings.Contains(err.Error(), "duplicate") { - t.Fatalf("duplicate error = %v", err) - } - if err := validateNonEmptyManualAgentStrings("scope", "field", []string{" "}); err == nil || !strings.Contains(err.Error(), "empty") { - t.Fatalf("empty error = %v", err) - } - if !validManualAgentExampleReasonCode(ManualAgentExampleReasonLocalState) || validManualAgentExampleReasonCode("other") { - t.Fatal("reason code validation disagrees with closed taxonomy") - } -} - -func TestCrossPlatformCoverageManualAgentExamplePlanAndParserEdges(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - spec := BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{Commands: []BoundCommandSpec{spec}, ByCanonical: map[string]BoundCommandSpec{"sample.search_items": spec}} - hints := manualAgentHintSetFixture() - - registries := []struct { - name string - registry SchemaRegistry - want string - }{ - {name: "empty canonical", registry: manualAgentExampleSchemaRegistry("", SafetySpec{}), want: "empty canonical path"}, - {name: "duplicate canonical", registry: SchemaRegistry{Products: []ProductSpec{{Tools: []ToolSpec{{Identity: ToolIdentitySpec{CanonicalPath: "sample.search_items"}}, {Identity: ToolIdentitySpec{CanonicalPath: "sample.search_items"}}}}}}, want: "duplicate tool"}, - {name: "missing typed tool", registry: SchemaRegistry{}, want: "missing from final typed"}, - } - for _, tc := range registries { - t.Run(tc.name, func(t *testing.T) { - _, err := BuildManualAgentExampleExecutionPlan(bound, tc.registry, hints) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - unknownHints := manualAgentHintSetFixture() - unknownHints.Tools["other.tool"] = unknownHints.Tools["sample.search_items"] - delete(unknownHints.Tools, "sample.search_items") - if _, err := buildManualAgentExampleExecutionPlan(bound, nil, unknownHints); err == nil || !strings.Contains(err.Error(), "unknown canonical") { - t.Fatalf("unknown canonical error = %v", err) - } - - nilSpec := spec - nilSpec.PrimaryCommand = nil - nilBound := BoundCommandRegistry{ByCanonical: map[string]BoundCommandSpec{"sample.search_items": nilSpec}} - if _, err := buildManualAgentExampleExecutionPlan(nilBound, nil, hints); err == nil || !strings.Contains(err.Error(), "no bound Cobra command") { - t.Fatalf("nil command error = %v", err) - } - - leaf.Annotations = map[string]string{runtimeSchemaRulesAnnotation: "{"} - if _, err := buildManualAgentExampleExecutionPlan(bound, nil, hints); err == nil || !strings.Contains(err.Error(), "invalid executable constraints") { - t.Fatalf("constraints error = %v", err) - } - leaf.Annotations = map[string]string{runtimeSchemaArgsAnnotation: "{"} - if _, err := buildManualAgentExampleExecutionPlan(bound, nil, hints); err == nil || !strings.Contains(err.Error(), "invalid executable positionals") { - t.Fatalf("positionals error = %v", err) - } - leaf.Annotations = nil - - index := 0 - tool := hints.Tools["sample.search_items"] - tool.ExampleDispositions = []ManualAgentExampleDisposition{{Index: &index, Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "local", Reviewed: true}} - hints.Tools["sample.search_items"] = tool - if _, err := BuildManualAgentExampleExecutionPlan(bound, manualAgentExampleSchemaRegistry("sample.search_items", SafetySpec{}), hints); err == nil || !strings.Contains(err.Error(), "narrows no explicit") { - t.Fatalf("disposition error = %v", err) - } - - hints = manualAgentHintSetFixture() - extraRegistry := manualAgentExampleSchemaRegistry("sample.search_items", SafetySpec{}) - extraRegistry.Products[0].Tools = append(extraRegistry.Products[0].Tools, ToolSpec{Identity: ToolIdentitySpec{CanonicalPath: "sample.extra"}}) - if _, err := BuildManualAgentExampleExecutionPlan(bound, extraRegistry, hints); err == nil || !strings.Contains(err.Error(), "has no Manual Agent hint examples") { - t.Fatalf("extra typed tool error = %v", err) - } - - parserCases := []struct { - input string - want string - }{ - {input: `dws x "value\`, want: "trailing escape in double-quoted"}, - {input: `dws x "${value}"`, want: "shell expansion"}, - {input: `dws x value\`, want: "trailing escape"}, - {input: `dws x `, want: "redirection"}, - {input: `dws x # comment`, want: "comments"}, - } - for _, tc := range parserCases { - if _, err := ParseManualAgentExampleArgv(tc.input); err == nil || !strings.Contains(err.Error(), tc.want) { - t.Errorf("ParseManualAgentExampleArgv(%q) error = %v, want %q", tc.input, err, tc.want) - } - } - argv, err := ParseManualAgentExampleArgv(`dws x "a b" 'c d' escaped\ value `) - if err != nil || !reflect.DeepEqual(argv, []string{"dws", "x", "a b", "c d", "escaped value", ""}) { - t.Fatalf("parsed argv = %#v, %v", argv, err) - } - - paths := []manualAgentExamplePath{{Path: "sample item search"}, {Path: "sample item deeply search"}} - if remainder, matched, ok := matchManualAgentExamplePath([]string{"dws", "sample", "item", "deeply", "search", "x"}, paths); !ok || matched.Path != "sample item deeply search" || !reflect.DeepEqual(remainder, []string{"x"}) { - t.Fatalf("match = %#v %#v %v", remainder, matched, ok) - } - if _, _, ok := matchManualAgentExamplePath(nil, paths); ok { - t.Fatal("empty argv matched") - } - if _, _, ok := matchManualAgentExamplePath([]string{"other", "x"}, paths); ok { - t.Fatal("non-dws argv matched") - } - if _, _, ok := matchManualAgentExamplePath([]string{"dws", "x"}, []manualAgentExamplePath{{Path: " "}, {Path: "x y"}}); ok { - t.Fatal("empty or overlong path matched") - } -} - -func TestCrossPlatformCoverageManualAgentExampleCobraContractEdges(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - root.PersistentFlags().StringP("format", "f", "", "format") - leaf.Flags().BoolP("verbose", "v", false, "verbose") - - cases := []struct { - name string - cmd *cobra.Command - args []string - pos []RuntimeSchemaPositional - want string - }{ - {name: "nil command", want: "command is nil"}, - {name: "terminator", cmd: leaf, args: []string{"--"}, want: "terminator"}, - {name: "empty long", cmd: leaf, args: []string{"--=x"}, want: "empty long"}, - {name: "long help", cmd: leaf, args: []string{"--help=true"}, want: "not only --help"}, - {name: "unknown long", cmd: leaf, args: []string{"--missing=x"}, want: "unknown flag"}, - {name: "missing long value", cmd: leaf, args: []string{"--query"}, want: "requires a value"}, - {name: "non ascii shorthand", cmd: leaf, args: []string{"-é"}, want: "non-ASCII"}, - {name: "short help", cmd: leaf, args: []string{"-h"}, want: "not only -h"}, - {name: "unknown shorthand", cmd: leaf, args: []string{"-x"}, want: "unknown shorthand"}, - {name: "missing shorthand value", cmd: leaf, args: []string{"-q"}, want: "requires a value"}, - {name: "required positional", cmd: leaf, pos: []RuntimeSchemaPositional{{Index: 0, Name: "id", Required: true}}, want: "missing required positional"}, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - err := validateManualAgentExampleCobraContract(tc.cmd, tc.args, RuntimeSchemaConstraints{}, tc.pos) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - query := leaf.Flags().Lookup("query") - query.Annotations = map[string][]string{cobra.BashCompOneRequiredFlag: {"true"}} - if err := validateManualAgentExampleCobraContract(leaf, nil, RuntimeSchemaConstraints{}, nil); err == nil || !strings.Contains(err.Error(), "missing required flag") { - t.Fatalf("required flag error = %v", err) - } - query.Annotations = nil - - constraints := RuntimeSchemaConstraints{RequireOneOf: [][]string{{"id"}}} - positionals := []RuntimeSchemaPositional{{Index: 0, Name: "id", Required: true, Variadic: true}} - if err := validateManualAgentExampleCobraContract(leaf, []string{"value"}, constraints, positionals); err != nil { - t.Fatalf("variadic positional contract = %v", err) - } - leaf.Args = cobra.NoArgs - if err := validateManualAgentExampleCobraContract(leaf, []string{"value"}, RuntimeSchemaConstraints{}, nil); err == nil || !strings.Contains(err.Error(), "invalid positional") { - t.Fatalf("Args validator error = %v", err) - } - leaf.Args = nil - if err := validateManualAgentExampleCobraContract(leaf, []string{"-vf=json", "-qvalue"}, RuntimeSchemaConstraints{}, nil); err != nil { - t.Fatalf("combined and persistent shorthand contract = %v", err) - } - - merged := mergeManualAgentExamplePositionals( - []RuntimeSchemaPositional{{Index: 1, Name: "b"}, {Index: 0, Name: "z"}}, - []RuntimeSchemaPositional{{Index: 0, Name: "a"}, {Index: 1, Name: "b"}}, - ) - if got := []string{merged[0].Name, merged[1].Name, merged[2].Name}; !reflect.DeepEqual(got, []string{"a", "z", "b"}) { - t.Fatalf("merged positionals = %#v", merged) - } - if err := validateManualAgentExampleConstraints(map[string]bool{}, RuntimeSchemaConstraints{RequireOneOf: [][]string{{"a", ""}}}); err == nil { - t.Fatal("missing require_one_of accepted") - } - if err := validateManualAgentExampleConstraints(map[string]bool{"a": true}, RuntimeSchemaConstraints{RequireTogether: [][]string{{"a", "b"}}}); err == nil { - t.Fatal("incomplete require_together accepted") - } - if err := validateManualAgentExampleConstraints(map[string]bool{"a": true, "b": true}, RuntimeSchemaConstraints{MutuallyExclusive: [][]string{{"a", "b"}}}); err == nil { - t.Fatal("mutually exclusive flags accepted") - } - if got := manualAgentExampleProvidedFlagCount(map[string]bool{"a": true}, []string{"--a", "a", " "}); got != 1 { - t.Fatalf("provided count = %d", got) - } - if got := manualAgentExampleFlagGroup([]string{" --b ", "", "a"}); got != "--a, --b" { - t.Fatalf("flag group = %q", got) - } - if runtimeCommandFlagByShorthand(nil, "q") != nil || runtimeCommandFlagByShorthand(leaf, "qq") != nil { - t.Fatal("invalid shorthand lookup succeeded") - } - if runtimeCommandFlagByShorthand(leaf, "q") != query || runtimeCommandFlagByShorthand(leaf, "f") == nil { - t.Fatal("local or persistent shorthand lookup failed") - } -} - -func TestCrossPlatformCoverageApplyManualSchemaHintsAndAnnotationEdges(t *testing.T) { - if _, err := applyManualSchemaHints(nil, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion}); err == nil || !strings.Contains(err.Error(), "root is nil") { - t.Fatalf("nil root error = %v", err) - } - root, leaf := manualSchemaHintTestTree() - if _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{Version: 99}); err == nil || !strings.Contains(err.Error(), "unsupported") { - t.Fatalf("version error = %v", err) - } - - base := ManualSchemaCommandHint{CLIPath: "sample item search", CanonicalPath: "sample.search_items", Reason: "reviewed", Reviewed: true} - applyCases := []struct { - name string - setup func(*cobra.Command, *ManualSchemaCommandHint) - params map[string]ManualSchemaParameterHint - want string - }{ - {name: "duplicate", setup: func(*cobra.Command, *ManualSchemaCommandHint) {}, want: "duplicate"}, - {name: "non runnable", setup: func(cmd *cobra.Command, _ *ManualSchemaCommandHint) { cmd.RunE = nil }, want: "public runnable"}, - {name: "hidden", setup: func(cmd *cobra.Command, _ *ManualSchemaCommandHint) { cmd.Parent().Hidden = true }, want: "public runnable"}, - {name: "empty flag", params: map[string]ManualSchemaParameterHint{" ": {Required: boolPointer(true)}}, want: "empty flag name"}, - {name: "no overrides", params: map[string]ManualSchemaParameterHint{"query": {}}, want: "no Schema overrides"}, - {name: "empty description", params: map[string]ManualSchemaParameterHint{"query": {Description: stringPointer(" ")}}, want: "empty description"}, - {name: "empty property", params: map[string]ManualSchemaParameterHint{"query": {Property: stringPointer(" ")}}, want: "empty property"}, - {name: "empty required when", params: map[string]ManualSchemaParameterHint{"query": {RequiredWhen: stringPointer(" ")}}, want: "empty required_when"}, - } - for _, tc := range applyCases { - t.Run(tc.name, func(t *testing.T) { - candidateRoot, candidateLeaf := manualSchemaHintTestTree() - hint := base - hint.Parameters = tc.params - if tc.setup != nil { - tc.setup(candidateLeaf, &hint) - } - commands := []ManualSchemaCommandHint{hint} - if tc.name == "duplicate" { - commands = append(commands, hint) - } - _, err := applyManualSchemaHints(candidateRoot, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: commands}) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - previousRegistryLoader := loadManualCommandRegistry - loadManualCommandRegistry = func() (CommandRegistry, error) { return CommandRegistry{}, errors.New("registry failed") } - if _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion}); err == nil || !strings.Contains(err.Error(), "registry failed") { - t.Fatalf("registry loader error = %v", err) - } - loadManualCommandRegistry = previousRegistryLoader - - loadManualCommandRegistry = func() (CommandRegistry, error) { - return CommandRegistry{ByCLIPath: map[string]CommandSpec{"sample item search": {CanonicalPath: "sample.other"}}}, nil - } - if _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{base}}); err == nil || !strings.Contains(err.Error(), "reviewed CommandRegistry canonical path") { - t.Fatalf("reviewed registry conflict = %v", err) - } - loadManualCommandRegistry = previousRegistryLoader - - aliasRoot, aliasLeaf := manualSchemaHintTestTree() - aliasLeaf.Aliases = []string{"find"} - aliasHint := base - aliasHint.CLIPath = "sample item find" - if _, err := applyManualSchemaHints(aliasRoot, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{aliasHint}}); err == nil || !strings.Contains(err.Error(), "not present in reviewed CommandRegistry") { - t.Fatalf("missing alias registry path = %v", err) - } - loadManualCommandRegistry = func() (CommandRegistry, error) { - return CommandRegistry{ByCLIPath: map[string]CommandSpec{"sample item search": {CanonicalPath: "sample.other"}}}, nil - } - if _, err := applyManualSchemaHints(aliasRoot, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{aliasHint}}); err == nil || !strings.Contains(err.Error(), "conflicts with real command path") { - t.Fatalf("alias registry conflict = %v", err) - } - loadManualCommandRegistry = previousRegistryLoader - - previousApply := applyManualParameter - applyManualParameter = func(*cobra.Command, string, ManualSchemaParameterHint, string) error { - return errors.New("annotation failed") - } - hint := base - hint.Parameters = map[string]ManualSchemaParameterHint{"query": {Required: boolPointer(true)}} - if _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{hint}}); err == nil || !strings.Contains(err.Error(), "annotation failed") { - t.Fatalf("annotation error = %v", err) - } - applyManualParameter = previousApply - - annotateManualSchemaIdentity(nil, "x.y", "reason") - if _, _, _, ok := runtimeManualSchemaIdentity(nil); ok { - t.Fatal("nil command has identity") - } - if _, _, _, ok := runtimeManualSchemaIdentity(&cobra.Command{Annotations: map[string]string{runtimeSchemaManualIdentityAnnotation: "invalid"}}); ok { - t.Fatal("invalid identity accepted") - } - if err := annotateManualSchemaParameter(nil, "x", ManualSchemaParameterHint{}, ""); err == nil { - t.Fatal("nil annotation command accepted") - } - if err := annotateManualSchemaParameter(leaf, "missing", ManualSchemaParameterHint{}, ""); err == nil { - t.Fatal("missing annotation flag accepted") - } - previousMarshal := marshalManualParameter - marshalManualParameter = func(any) ([]byte, error) { return nil, errors.New("marshal failed") } - if err := annotateManualSchemaParameter(leaf, "query", ManualSchemaParameterHint{}, ""); err == nil || !strings.Contains(err.Error(), "marshal failed") { - t.Fatalf("marshal error = %v", err) - } - marshalManualParameter = previousMarshal - - bad := &cobra.Command{Annotations: map[string]string{runtimeManualSchemaParameterKey(runtimeSchemaManualParameterAnnotation, "query"): "{"}} - if _, _, _, err := runtimeManualSchemaParameter(bad, "query"); err == nil || !strings.Contains(err.Error(), "decode") { - t.Fatalf("decode parameter error = %v", err) - } - if hint, reason, ok, err := runtimeManualSchemaParameter(nil, "query"); err != nil || ok || reason != "" || !reflect.DeepEqual(hint, ManualSchemaParameterHint{}) { - t.Fatalf("nil parameter = %#v %q %v %v", hint, reason, ok, err) - } - if hint, _, ok, err := runtimeManualSchemaParameter(&cobra.Command{Annotations: map[string]string{}}, "query"); err != nil || ok || !reflect.DeepEqual(hint, ManualSchemaParameterHint{}) { - t.Fatalf("missing parameter = %#v %v %v", hint, ok, err) - } - - for _, value := range []string{"string", "array", "object", "integer", "number", "boolean"} { - if !supportedManualSchemaInterfaceType(" " + value + " ") { - t.Fatalf("interface type %q rejected", value) - } - } - if supportedManualSchemaInterfaceType("unknown") { - t.Fatal("unknown interface type accepted") - } - if value := trimmedManualSchemaString(nil); value != nil { - t.Fatalf("trimmed nil = %#v", value) - } - if product, tool, ok := splitManualSchemaCanonicalPath(" product.tool "); !ok || product != "product" || tool != "tool" { - t.Fatalf("canonical split = %q %q %v", product, tool, ok) - } - for _, path := range []string{"", "product", ".tool", "product.", "bad product.tool"} { - if _, _, ok := splitManualSchemaCanonicalPath(path); ok { - t.Fatalf("invalid canonical %q accepted", path) - } - } - if publicRunnableSchemaLeaf(nil) || publicRunnableSchemaLeaf(&cobra.Command{Use: "x"}) { - t.Fatal("non-runnable command accepted") - } - parent := &cobra.Command{Use: "parent"} - parent.AddCommand(&cobra.Command{Use: "child", Run: func(*cobra.Command, []string) {}}) - if publicRunnableSchemaLeaf(parent) { - t.Fatal("command with children accepted as leaf") - } -} - -func TestCrossPlatformCoverageEmbeddedManualSchemaHintWrapperErrors(t *testing.T) { - previous := loadManualSchemaHints - loadManualSchemaHints = func() (ManualSchemaHintSnapshot, error) { - return ManualSchemaHintSnapshot{}, errors.New("hints failed") - } - t.Cleanup(func() { loadManualSchemaHints = previous }) - if _, err := ApplyEmbeddedManualSchemaHints(&cobra.Command{}); err == nil || !strings.Contains(err.Error(), "hints failed") { - t.Fatalf("ApplyEmbeddedManualSchemaHints() error = %v", err) - } - if _, err := ValidateEmbeddedManualAgentExampleDelivery(BoundCommandRegistry{}, SchemaRegistry{}); err == nil || !strings.Contains(err.Error(), "hints failed") { - t.Fatalf("ValidateEmbeddedManualAgentExampleDelivery() error = %v", err) - } -} - -// TestCrossPlatformCoverageBuildManualAgentExampleExecutionPlanDefaultsMissingHintToEmpty pins the -// defensive default: a canonical collected from the declared set whose bound -// entry resolves without a ContractFinal payload and without a hint-file row -// gets an empty hint instead of failing or reusing stale prose. -func TestCrossPlatformCoverageBuildManualAgentExampleExecutionPlanDefaultsMissingHintToEmpty(t *testing.T) { - declaredCmd := &cobra.Command{Use: "get", Run: func(*cobra.Command, []string) {}} - t.Cleanup(func() { ClearRuntimeContractFinalForTest(declaredCmd) }) - RegisterRuntimeContractFinal(declaredCmd, ContractFinalPayload{Title: "T"}) - - bareCmd := &cobra.Command{Use: "get", Run: func(*cobra.Command, []string) {}} - commandSpec := CommandSpec{CanonicalPath: "sample.get", PrimaryCLIPath: "sample get"} - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{{CommandSpec: commandSpec, PrimaryCommand: declaredCmd}}, - ByCanonical: map[string]BoundCommandSpec{ - "sample.get": {CommandSpec: commandSpec, PrimaryCommand: bareCmd}, - }, - } - plan, err := buildManualAgentExampleExecutionPlan(bound, nil, ManualAgentHintSet{}) - if err != nil { - t.Fatalf("buildManualAgentExampleExecutionPlan() error = %v", err) - } - if plan.Total != 0 || len(plan.Examples) != 0 { - t.Fatalf("empty defensive hint must plan no examples, got %#v", plan) - } -} diff --git a/internal/cli/schema_manual_hints_test.go b/internal/cli/schema_manual_hints_test.go deleted file mode 100644 index 253d6202..00000000 --- a/internal/cli/schema_manual_hints_test.go +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestManualSchemaHintsIncludeExistingLeafAndOverrideParameters(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - description := "Reviewed query text" - property := "queryText" - interfaceType := "object" - required := true - requiredWhen := "mode is advanced" - snapshot := ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Reviewed public helper", - Reviewed: true, - Parameters: map[string]ManualSchemaParameterHint{ - "query": { - Description: &description, - Property: &property, - InterfaceType: &interfaceType, - Required: &required, - RequiredWhen: &requiredWhen, - }, - }, - }}, - } - - report, err := applyManualSchemaHints(root, snapshot) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - if len(report.Commands) != 1 || report.Commands[0] != "sample item search" { - t.Fatalf("report.Commands = %#v", report.Commands) - } - productID, toolName, reason, ok := runtimeManualSchemaIdentity(leaf) - if !ok || productID != "sample" || toolName != "search_items" || reason != "Reviewed public helper" { - t.Fatalf("Schema identity = %s.%s (%s)", productID, toolName, reason) - } - - payload, err := runtimeSchemaPayloadForTest(root, []string{"sample.search_items"}) - if err != nil { - t.Fatalf("runtimeSchemaPayloadForTest() error = %v", err) - } - parameters := schemaMap(payload["parameters"]) - query := parameters["query"] - if query["description"] != description || query["property"] != property || query["interface_type"] != interfaceType || query["required"] != true || query["required_when"] != requiredWhen { - t.Fatalf("query parameter = %#v", query) - } - if leaf.Flags().Lookup("query").Usage != "Original query text" { - t.Fatalf("human Schema hint changed Cobra help: %q", leaf.Flags().Lookup("query").Usage) - } - if len(leaf.Flags().Lookup("query").Annotations[cobra.BashCompOneRequiredFlag]) != 0 { - t.Fatal("manual Schema hint changed Cobra execution validation") - } -} - -func TestManualSchemaParameterHintsDoNotLeakAcrossSharedFlags(t *testing.T) { - root, query := manualSchemaHintTestTree() - get := &cobra.Command{Use: "get", RunE: func(*cobra.Command, []string) error { return nil }} - get.Flags().AddFlag(query.Flags().Lookup("query")) - query.Parent().AddCommand(get) - - required := false - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Only the broad search command allows an omitted query.", - Reviewed: true, - Parameters: map[string]ManualSchemaParameterHint{ - "query": {Required: &required}, - }, - }}, - }) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - - canonical := "sample.get_item" - previous, existed := runtimeSchemaParameterMetadataByCanonical[canonical] - runtimeSchemaParameterMetadataByCanonical[canonical] = RuntimeSchemaParameterMetadata{Required: []string{"query"}} - t.Cleanup(func() { - if existed { - runtimeSchemaParameterMetadataByCanonical[canonical] = previous - } else { - delete(runtimeSchemaParameterMetadataByCanonical, canonical) - } - }) - - queryParameters, err := runtimeCommandParameters(query, "sample.search_items", nil, nil, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("query parameters: %v", err) - } - if got := schemaParameterEntry(t, queryParameters, "query")["required"]; got != false { - t.Fatalf("query required = %#v, want false", got) - } - - getParameters, err := runtimeCommandParameters(get, canonical, nil, nil, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("get parameters: %v", err) - } - getQuery := schemaParameterEntry(t, getParameters, "query") - if getQuery["required"] != true { - t.Fatalf("get required = %#v, want true", getQuery["required"]) - } - if source := schemaParameterFieldSource(t, getQuery, "required"); source != "typed_parameter_metadata" { - t.Fatalf("get required source = %q, want typed_parameter_metadata", source) - } - if _, _, present, err := runtimeManualSchemaParameter(get, "query"); err != nil || present { - t.Fatalf("manual parameter leaked to sibling command: present=%t err=%v", present, err) - } -} - -func TestManualSchemaHintsRejectInvalidOrStaleInputs(t *testing.T) { - tests := []struct { - name string - mutate func(*ManualSchemaCommandHint) - wantErr string - }{ - {name: "missing command", mutate: func(h *ManualSchemaCommandHint) { h.CLIPath = "sample item missing" }, wantErr: "does not resolve"}, - {name: "wildcard command", mutate: func(h *ManualSchemaCommandHint) { h.CLIPath = "sample item *" }, wantErr: "invalid exact cli_path"}, - {name: "not reviewed", mutate: func(h *ManualSchemaCommandHint) { h.Reviewed = false }, wantErr: "not reviewed"}, - {name: "missing reason", mutate: func(h *ManualSchemaCommandHint) { h.Reason = "" }, wantErr: "has no reason"}, - {name: "bad canonical", mutate: func(h *ManualSchemaCommandHint) { h.CanonicalPath = "sample" }, wantErr: "invalid canonical_path"}, - {name: "missing flag", mutate: func(h *ManualSchemaCommandHint) { - h.Parameters = map[string]ManualSchemaParameterHint{"missing": {Required: boolPointer(true)}} - }, wantErr: "missing flag --missing"}, - {name: "invalid interface type", mutate: func(h *ManualSchemaCommandHint) { - h.Parameters = map[string]ManualSchemaParameterHint{"query": {InterfaceType: stringPointer("made-up")}} - }, wantErr: "unsupported interface_type"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - root, _ := manualSchemaHintTestTree() - hint := ManualSchemaCommandHint{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Reviewed public helper", - Reviewed: true, - } - test.mutate(&hint) - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{hint}, - }) - if err == nil || !strings.Contains(err.Error(), test.wantErr) { - t.Fatalf("error = %v, want containing %q", err, test.wantErr) - } - }) - } -} - -func TestManualSchemaHintsRejectCanonicalConflict(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - AttachRuntimeSchema(leaf, "sample", "existing", "test") - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.replacement", - Reason: "Reviewed public helper", - Reviewed: true, - }}, - }) - if err == nil || !strings.Contains(err.Error(), "conflicts with existing canonical path") { - t.Fatalf("error = %v", err) - } -} - -func TestManualSchemaHintsRejectAmbiguousCobraAlias(t *testing.T) { - root, search := manualSchemaHintTestTree() - search.Aliases = []string{"find"} - list := &cobra.Command{Use: "list", Aliases: []string{"find"}, RunE: func(*cobra.Command, []string) error { return nil }} - search.Parent().AddCommand(list) - - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item find", - CanonicalPath: "sample.search_items", - Reason: "Ambiguous alias fixture", - Reviewed: true, - }}, - }) - if err == nil || !strings.Contains(err.Error(), `cobra alias segment "find" is ambiguous`) { - t.Fatalf("error = %v", err) - } -} - -func TestManualSchemaHintsResolveReviewedCobraAliasExactly(t *testing.T) { - root := commandRegistryTestRoot("aitable record query") - exactSchemaCommand(root, "aitable record").Aliases = []string{"records"} - leaf := exactSchemaCommand(root, "aitable record query") - - report, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "aitable records query", - CanonicalPath: "aitable.query_records", - Reason: "Reviewed ancestor Cobra alias", - Reviewed: true, - }}, - }) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - if got := strings.Join(report.Commands, ","); got != "aitable records query" { - t.Fatalf("report commands = %q", got) - } - productID, toolName, _, ok := runtimeManualSchemaIdentity(leaf) - if !ok || productID+"."+toolName != "aitable.query_records" { - t.Fatalf("manual identity = %s.%s, ok=%v", productID, toolName, ok) - } -} - -func TestDecodeManualSchemaHintsRejectsUnknownFields(t *testing.T) { - _, err := decodeManualSchemaHints([]byte(`{"$schema":"./schema_manual_hints.schema.json","version":1,"commands":[],"allow_virtual_commands":true}`)) - if err == nil || !strings.Contains(err.Error(), "unknown field") { - t.Fatalf("error = %v", err) - } -} - -func TestDecodeManualSchemaHintsRequiresDiscoverableSchema(t *testing.T) { - _, err := decodeManualSchemaHints([]byte(`{"version":1,"commands":[]}`)) - if err == nil || !strings.Contains(err.Error(), "must declare $schema") { - t.Fatalf("error = %v", err) - } -} - -func TestDecodeManualSchemaHintsRequiresAgentHints(t *testing.T) { - _, err := decodeManualSchemaHints([]byte(`{"$schema":"./schema_manual_hints.schema.json","version":1,"commands":[]}`)) - if err == nil || !strings.Contains(err.Error(), "agent_hints.revisions must not be empty") { - t.Fatalf("error = %v", err) - } -} - -func TestValidateManualAgentHintSetAllowsLingeringProductDeclRows(t *testing.T) { - t.Cleanup(func() { ClearProductDeclForTest("sample") }) - RegisterProductDecl(ProductDecl{ - ID: "sample", - Selection: ProductSelectionDecl{ - AgentSummary: "Manage samples", - UseWhen: []string{"target is a sample"}, - AvoidWhen: []string{"target is another product"}, - }, - }) - hints := manualAgentHintSetFixture() - // Declared product may still appear in selection products{} during - // migration; with empty expected products it must not fail as unexpected. - if err := ValidateManualAgentHintSet(hints, map[string]bool{}, map[string]bool{"sample.search_items": true}); err != nil { - t.Fatalf("ValidateManualAgentHintSet() error = %v", err) - } -} - -func TestValidateManualAgentHintSetRequiresReviewedExactCoverage(t *testing.T) { - hints := manualAgentHintSetFixture() - if err := ValidateManualAgentHintSet(hints, map[string]bool{"sample": true}, map[string]bool{"sample.search_items": true}); err != nil { - t.Fatalf("ValidateManualAgentHintSet() error = %v", err) - } - - tests := []struct { - name string - mutate func(*ManualAgentHintSet) - wantErr string - }{ - { - name: "unknown revision", - mutate: func(hints *ManualAgentHintSet) { - tool := hints.Tools["sample.search_items"] - tool.Revision = "missing" - hints.Tools["sample.search_items"] = tool - }, - wantErr: "unknown revision", - }, - { - name: "unreviewed", - mutate: func(hints *ManualAgentHintSet) { - tool := hints.Tools["sample.search_items"] - tool.Reviewed = false - hints.Tools["sample.search_items"] = tool - }, - wantErr: "must be reviewed", - }, - { - name: "missing examples", - mutate: func(hints *ManualAgentHintSet) { - tool := hints.Tools["sample.search_items"] - tool.Examples = nil - hints.Tools["sample.search_items"] = tool - }, - wantErr: "requires non-empty examples", - }, - { - name: "confirmation bypass", - mutate: func(hints *ManualAgentHintSet) { - tool := hints.Tools["sample.search_items"] - tool.Examples = []string{"dws sample item search --query x --yes"} - hints.Tools["sample.search_items"] = tool - }, - wantErr: "must not bypass confirmation", - }, - { - name: "missing Registry tool", - mutate: func(hints *ManualAgentHintSet) { - delete(hints.Tools, "sample.search_items") - }, - wantErr: "missing=[sample.search_items]", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - candidate := manualAgentHintSetFixture() - test.mutate(&candidate) - err := ValidateManualAgentHintSet(candidate, map[string]bool{"sample": true}, map[string]bool{"sample.search_items": true}) - if err == nil || !strings.Contains(err.Error(), test.wantErr) { - t.Fatalf("error = %v, want containing %q", err, test.wantErr) - } - }) - } -} - -func TestDecodeManualSchemaHintsRejectsUnknownManualAgentField(t *testing.T) { - _, err := decodeManualSchemaHints([]byte(`{ - "$schema":"./schema_manual_hints.schema.json", - "version":1, - "commands":[], - "agent_hints":{ - "revisions":{"v1":{"generated_by":"ai","model":"gpt-5","prompt_version":"v1","reason":"fixture"}}, - "products":{}, - "tools":{}, - "allow_generated_overwrite":true - } -}`)) - if err == nil || !strings.Contains(err.Error(), "unknown field") { - t.Fatalf("error = %v", err) - } -} - -func TestValidateManualAgentHintExamplesUsesBoundCobraContract(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - root.PersistentFlags().String("format", "json", "output format") - alias := &cobra.Command{Use: "find", RunE: func(*cobra.Command, []string) error { return nil }} - alias.Flags().StringP("legacy-query", "l", "", "compatibility query") - leaf.Parent().AddCommand(alias) - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{ - CanonicalPath: "sample.search_items", - PrimaryCLIPath: "sample item search", - Aliases: []string{"sample item find"}, - }, - PrimaryCommand: leaf, - AliasCommands: []BoundAlias{{ - Path: "sample item find", - Command: alias, - Kind: AliasKindCompatibilityLeaf, - }}, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{ - "sample item search": boundSpec, - "sample item find": boundSpec, - }, - } - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.Examples = []string{"dws sample item find -l --format json"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err != nil { - t.Fatalf("ValidateManualAgentHintExamples() error = %v", err) - } - - tool.Examples = []string{"dws sample item search -q 'value with spaces' --format=json"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err != nil { - t.Fatalf("primary shorthand example error = %v", err) - } - - tool.Examples = []string{"dws sample item search -l value"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err == nil || !strings.Contains(err.Error(), "unknown shorthand flag -l") { - t.Fatalf("alias-only shorthand on primary error = %v", err) - } - - tool.Examples = []string{"dws sample item search --invented value"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err == nil || !strings.Contains(err.Error(), "unknown flag --invented") { - t.Fatalf("unknown flag error = %v", err) - } - - tool.Examples = []string{"dws sample item list --query value"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err == nil || !strings.Contains(err.Error(), "does not use its reviewed") { - t.Fatalf("wrong path error = %v", err) - } -} - -func TestValidateManualAgentHintExamplesRejectsUnsafeOrNonExecutingArgv(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{ - CanonicalPath: "sample.search_items", - PrimaryCLIPath: "sample item search", - }, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - tests := []struct { - name string - example string - wantErr string - }{ - {name: "quoted operators are data", example: `dws sample item search --query "A && B > C"`}, - {name: "quoted multiline value is one argument", example: "dws sample item search --query '{\n\"key\": \"value\"\n}'"}, - {name: "placeholder is data", example: `dws sample item search --query ,`}, - {name: "attached shorthand value", example: `dws sample item search -qvalue`}, - {name: "chaining", example: `dws sample item search --query x && dws sample item search --query y`, wantErr: "shell operator"}, - {name: "unquoted newline", example: "dws sample item search --query x\ndws sample item search --query y", wantErr: "unquoted newline shell operator"}, - {name: "semicolon", example: `dws sample item search --query x; dws sample item search --query y`, wantErr: "shell operator"}, - {name: "pipe", example: `dws sample item search --query x | tee out`, wantErr: "shell operator"}, - {name: "redirect", example: `dws sample item search --query x > out`, wantErr: "shell operator"}, - {name: "input redirect", example: `dws sample item search < input`, wantErr: "redirection operator"}, - {name: "command substitution", example: `dws sample item search --query $(whoami)`, wantErr: "shell expansion"}, - {name: "backticks", example: "dws sample item search --query `whoami`", wantErr: "shell expansion"}, - {name: "argument terminator", example: `dws sample item search -- --query x`, wantErr: "argument terminator"}, - {name: "help long", example: `dws sample item search --help`, wantErr: "not only --help"}, - {name: "help shorthand", example: `dws sample item search -h`, wantErr: "not only -h"}, - {name: "unknown shorthand", example: `dws sample item search -x value`, wantErr: "unknown shorthand flag -x"}, - {name: "unterminated quote", example: `dws sample item search --query "value`, wantErr: "unterminated quoted value"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.Examples = []string{test.example} - hints.Tools["sample.search_items"] = tool - err := ValidateManualAgentHintExamples(bound, hints) - if test.wantErr == "" { - if err != nil { - t.Fatalf("ValidateManualAgentHintExamples() error = %v", err) - } - return - } - if err == nil || !strings.Contains(err.Error(), test.wantErr) { - t.Fatalf("error = %v, want containing %q", err, test.wantErr) - } - }) - } -} - -func TestBuildManualAgentExampleExecutionPlanUsesExplicitReviewedDispositions(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{ - CanonicalPath: "sample.search_items", - PrimaryCLIPath: "sample item search", - }, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.Examples = []string{ - "dws sample item search --query first", - "dws sample item search --query second", - } - hints.Tools["sample.search_items"] = tool - - registry := manualAgentExampleSchemaRegistry("sample.search_items", SafetySpec{}) - plan, err := BuildManualAgentExampleExecutionPlan(bound, registry, hints) - if err != nil { - t.Fatalf("BuildManualAgentExampleExecutionPlan() error = %v", err) - } - if plan.Total != 2 || plan.Contract != 2 || plan.DryRun != 0 || plan.ContractOnly != 0 { - t.Fatalf("default plan counts = total:%d contract:%d dry_run:%d contract_only:%d", plan.Total, plan.Contract, plan.DryRun, plan.ContractOnly) - } - for _, execution := range plan.Examples { - if execution.Mode != ManualAgentExampleModeContract || execution.DryRun != nil { - t.Fatalf("example without an explicit capability = %#v, want contract-only validation", execution) - } - } - - registry.Products[0].Tools[0].DryRun = &DryRunSpec{PreviewKind: DryRunPreviewPlan} - plan, err = BuildManualAgentExampleExecutionPlan(bound, registry, hints) - if err != nil { - t.Fatalf("BuildManualAgentExampleExecutionPlan() with explicit dry_run error = %v", err) - } - if plan.Total != 2 || plan.Contract != 0 || plan.DryRun != 2 || plan.ContractOnly != 0 { - t.Fatalf("explicit capability plan = %#v", plan) - } - for _, execution := range plan.Examples { - if execution.Mode != ManualAgentExampleModeDryRun || execution.DryRun == nil { - t.Fatalf("explicit capability execution = %#v, want dry_run", execution) - } - } - - index := 1 - tool.ExampleDispositions = []ManualAgentExampleDisposition{{ - Index: &index, - Mode: ManualAgentExampleModeContractOnly, - ReasonCode: ManualAgentExampleReasonStatefulPreflight, - Reason: "The command checks an authenticated tenant before its dry-run branch.", - Reviewed: true, - }} - hints.Tools["sample.search_items"] = tool - plan, err = BuildManualAgentExampleExecutionPlan(bound, registry, hints) - if err != nil { - t.Fatalf("BuildManualAgentExampleExecutionPlan() with disposition error = %v", err) - } - if plan.Total != 2 || plan.DryRun != 1 || plan.ContractOnly != 1 || plan.ReviewedContractOnly != 1 || plan.ContractOnlyByReason[ManualAgentExampleReasonStatefulPreflight] != 1 { - t.Fatalf("reviewed plan = %#v", plan) - } - if plan.Examples[0].Mode != ManualAgentExampleModeDryRun || plan.Examples[1].Mode != ManualAgentExampleModeContractOnly { - t.Fatalf("resolved example modes = %#v", plan.Examples) - } - - tool.ExampleDispositions = nil - hints.Tools["sample.search_items"] = tool - registry = manualAgentExampleSchemaRegistry("sample.search_items", SafetySpec{Risk: "high", Confirmation: "user_required"}) - registry.Products[0].Tools[0].DryRun = &DryRunSpec{PreviewKind: DryRunPreviewPlan} - plan, err = BuildManualAgentExampleExecutionPlan(bound, registry, hints) - if err != nil { - t.Fatalf("BuildManualAgentExampleExecutionPlan() with typed safety error = %v", err) - } - if plan.DryRun != 2 || plan.ContractOnly != 0 { - t.Fatalf("typed safety plan = %#v", plan) - } - for _, execution := range plan.Examples { - if execution.Mode != ManualAgentExampleModeDryRun || execution.Source != ManualAgentExampleDispositionDefault { - t.Fatalf("typed safety execution = %#v, want default dry_run", execution) - } - } - tool.ExampleDispositions = []ManualAgentExampleDisposition{{ - Index: &index, - Mode: ManualAgentExampleModeContractOnly, - ReasonCode: ManualAgentExampleReasonStatefulPreflight, - Reason: "The command performs an authenticated stateful lookup before its dry-run branch.", - Reviewed: true, - }} - hints.Tools["sample.search_items"] = tool - plan, err = BuildManualAgentExampleExecutionPlan(bound, registry, hints) - if err != nil { - t.Fatalf("high-risk plan with reviewed stateful preflight disposition error = %v", err) - } - if plan.DryRun != 1 || plan.ContractOnly != 1 || plan.ReviewedContractOnly != 1 { - t.Fatalf("high-risk reviewed disposition plan = %#v", plan) - } -} - -func TestBuildManualAgentExampleExecutionPlanRejectsInvalidDisposition(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - zero, one := 0, 1 - tests := []struct { - name string - dispositions []ManualAgentExampleDisposition - wantErr string - }{ - {name: "missing index", dispositions: []ManualAgentExampleDisposition{{Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state", Reviewed: true}}, wantErr: "requires index"}, - {name: "out of range", dispositions: []ManualAgentExampleDisposition{{Index: &one, Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state", Reviewed: true}}, wantErr: "out of range"}, - {name: "duplicate", dispositions: []ManualAgentExampleDisposition{{Index: &zero, Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state", Reviewed: true}, {Index: &zero, Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state", Reviewed: true}}, wantErr: "duplicate"}, - {name: "not reviewed", dispositions: []ManualAgentExampleDisposition{{Index: &zero, Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state"}}, wantErr: "must be reviewed"}, - {name: "stores default", dispositions: []ManualAgentExampleDisposition{{Index: &zero, Mode: ManualAgentExampleModeDryRun, ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state", Reviewed: true}}, wantErr: "invalid mode"}, - {name: "unknown mode", dispositions: []ManualAgentExampleDisposition{{Index: &zero, Mode: "skip", ReasonCode: ManualAgentExampleReasonLocalState, Reason: "Requires local state", Reviewed: true}}, wantErr: "invalid mode"}, - {name: "unknown reason code", dispositions: []ManualAgentExampleDisposition{{Index: &zero, Mode: ManualAgentExampleModeContractOnly, ReasonCode: "safe", Reason: "Skip a safe example", Reviewed: true}}, wantErr: "invalid reason_code"}, - {name: "empty reason", dispositions: []ManualAgentExampleDisposition{{Index: &zero, Mode: ManualAgentExampleModeContractOnly, ReasonCode: ManualAgentExampleReasonLocalState, Reviewed: true}}, wantErr: "non-empty reason"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.ExampleDispositions = test.dispositions - hints.Tools["sample.search_items"] = tool - err := ValidateManualAgentHintExamples(bound, hints) - if err == nil || !strings.Contains(err.Error(), test.wantErr) { - t.Fatalf("error = %v, want containing %q", err, test.wantErr) - } - }) - } -} - -func TestManualAgentExampleContractRequiresCobraFlagsAndPositionalsEvenWhenContractOnly(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - if err := leaf.MarkFlagRequired("query"); err != nil { - t.Fatalf("MarkFlagRequired(query): %v", err) - } - leaf.Args = cobra.ExactArgs(1) - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - index := 0 - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.ExampleDispositions = []ManualAgentExampleDisposition{{ - Index: &index, - Mode: ManualAgentExampleModeContractOnly, - ReasonCode: ManualAgentExampleReasonStatefulPreflight, - Reason: "Reviewed interactive precondition", - Reviewed: true, - }} - - tool.Examples = []string{"dws sample item search item-id"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err == nil || !strings.Contains(err.Error(), "missing required flag") { - t.Fatalf("missing required flag error = %v", err) - } - - tool.Examples = []string{"dws sample item search --query value"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err == nil || !strings.Contains(err.Error(), "invalid positional arguments") { - t.Fatalf("missing positional error = %v", err) - } - - tool.Examples = []string{"dws sample item search --query value item-id"} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err != nil { - t.Fatalf("valid contract-only example error = %v", err) - } -} - -func TestManualAgentExampleContractEnforcesTypedFlagGroupsBeforeDisposition(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - leaf.Flags().String("robot-client-id", "", "robot client ID") - leaf.Flags().String("unified-app-id", "", "unified app ID") - leaf.Flags().String("start", "", "start value") - leaf.Flags().String("end", "", "end value") - leaf.Flags().String("brief", "", "brief output") - leaf.Flags().String("verbose-output", "", "verbose output") - AnnotateRuntimeConstraints(leaf, RuntimeSchemaConstraints{ - RequireOneOf: [][]string{{"robot-client-id", "unified-app-id"}}, - RequireTogether: [][]string{{"start", "end"}}, - MutuallyExclusive: [][]string{{"brief", "verbose-output"}}, - }) - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - index := 0 - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.ExampleDispositions = []ManualAgentExampleDisposition{{ - Index: &index, - Mode: ManualAgentExampleModeContractOnly, - ReasonCode: ManualAgentExampleReasonStatefulPreflight, - Reason: "A reviewed local connection is required after argument validation.", - Reviewed: true, - }} - - tests := []struct { - name string - example string - wantErr string - }{ - {name: "require one of", example: "dws sample item search --query value", wantErr: "missing require_one_of"}, - {name: "require together", example: "dws sample item search --query value --robot-client-id robot --start now", wantErr: "incomplete require_together"}, - {name: "mutually exclusive", example: "dws sample item search --query value --robot-client-id robot --brief yes --verbose-output yes", wantErr: "mutually_exclusive"}, - {name: "valid", example: "dws sample item search --query value --unified-app-id app --start now --end later --brief yes"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - tool.Examples = []string{test.example} - hints.Tools["sample.search_items"] = tool - err := ValidateManualAgentHintExamples(bound, hints) - if test.wantErr == "" { - if err != nil { - t.Fatalf("ValidateManualAgentHintExamples() error = %v", err) - } - return - } - if err == nil || !strings.Contains(err.Error(), test.wantErr) { - t.Fatalf("error = %v, want containing %q", err, test.wantErr) - } - }) - } -} - -func TestManualAgentExampleExecutionPlanEnforcesFinalTypedConstraints(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - leaf.Flags().String("robot-client-id", "", "robot client ID") - leaf.Flags().String("unified-app-id", "", "unified app ID") - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - hints := manualAgentHintSetFixture() - registry := manualAgentExampleSchemaRegistry("sample.search_items", SafetySpec{}) - registry.Products[0].Tools[0].Constraints = RuntimeSchemaConstraints{ - RequireOneOf: [][]string{{"robot-client-id", "unified-app-id"}}, - } - - if _, err := BuildManualAgentExampleExecutionPlan(bound, registry, hints); err == nil || !strings.Contains(err.Error(), "missing require_one_of") { - t.Fatalf("final typed constraint error = %v", err) - } - tool := hints.Tools["sample.search_items"] - tool.Examples = []string{"dws sample item search --query value --unified-app-id app"} - hints.Tools["sample.search_items"] = tool - if _, err := BuildManualAgentExampleExecutionPlan(bound, registry, hints); err != nil { - t.Fatalf("valid final typed constraint error = %v", err) - } -} - -func TestManualAgentExampleExecutionPlanRejectsManualConfirmationClassification(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - boundSpec := BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - bound := BoundCommandRegistry{ - Commands: []BoundCommandSpec{boundSpec}, - ByCanonical: map[string]BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]BoundCommandSpec{"sample item search": boundSpec}, - } - index := 0 - hints := manualAgentHintSetFixture() - tool := hints.Tools["sample.search_items"] - tool.ExampleDispositions = []ManualAgentExampleDisposition{{ - Index: &index, - Mode: ManualAgentExampleModeContractOnly, - ReasonCode: "confirmation_required", - Reason: "Do not author a typed safety fact manually.", - Reviewed: true, - }} - hints.Tools["sample.search_items"] = tool - if err := ValidateManualAgentHintExamples(bound, hints); err == nil || !strings.Contains(err.Error(), "invalid reason_code") { - t.Fatalf("manual confirmation disposition error = %v", err) - } -} - -func manualAgentExampleSchemaRegistry(canonical string, safety SafetySpec) SchemaRegistry { - return SchemaRegistry{Products: []ProductSpec{{ - ID: "sample", - Tools: []ToolSpec{{ - Identity: ToolIdentitySpec{CanonicalPath: canonical}, - Safety: safety, - }}, - }}} -} - -func manualAgentHintSetFixture() ManualAgentHintSet { - const revision = "ai-agent-contract-v1" - return ManualAgentHintSet{ - Revisions: map[string]ManualAgentHintRevision{ - revision: { - GeneratedBy: "ai", - Model: "gpt-5", - PromptVersion: "v1", - Reason: "Generate the reviewed fixture", - }, - }, - Products: map[string]ManualAgentProductHint{ - "sample": { - AgentSummary: "Manage sample items", - UseWhen: []string{"A sample item must be managed"}, - AvoidWhen: []string{"The target is not a sample item"}, - Reviewed: true, - Revision: revision, - Reason: "Reviewed product routing", - Evidence: []string{"sample product reference"}, - }, - }, - Tools: map[string]ManualAgentToolHint{ - "sample.search_items": { - AgentSummary: "Search sample items", - UseWhen: []string{"Existing sample items must be found"}, - AvoidWhen: []string{"A sample item must be created"}, - Examples: []string{"dws sample item search --query value"}, - Reviewed: true, - Revision: revision, - Reason: "Reviewed tool selection", - Evidence: []string{"dws sample item search --help"}, - }, - }, - } -} - -func manualSchemaHintTestTree() (*cobra.Command, *cobra.Command) { - root := &cobra.Command{Use: "dws"} - product := &cobra.Command{Use: "sample"} - group := &cobra.Command{Use: "item"} - leaf := &cobra.Command{Use: "search", RunE: func(*cobra.Command, []string) error { return nil }} - leaf.Flags().StringP("query", "q", "", "Original query text") - group.AddCommand(leaf) - product.AddCommand(group) - root.AddCommand(product) - return root, leaf -} - -func boolPointer(value bool) *bool { - return &value -} - -func stringPointer(value string) *string { - return &value -} diff --git a/internal/cli/schema_navigation_contract_test.go b/internal/cli/schema_navigation_contract_test.go index 102b784d..b8c5f3cd 100644 --- a/internal/cli/schema_navigation_contract_test.go +++ b/internal/cli/schema_navigation_contract_test.go @@ -4,15 +4,11 @@ package cli import ( - "reflect" "testing" ) -func TestToolSchemaHintCannotOwnCommandNavigation(t *testing.T) { - typeOfHint := reflect.TypeOf(ToolSchemaHint{}) - for _, field := range []string{"CanonicalPath", "PrimaryCLIPath", "Aliases", "Visibility"} { - if _, exists := typeOfHint.FieldByName(field); exists { - t.Fatalf("ToolSchemaHint.%s reintroduces a navigation source outside CommandRegistry", field) - } +func TestSchemaVisibilityRemainsRegistryNavigationClass(t *testing.T) { + if SchemaVisibilityPublic != "public" || SchemaVisibilityCompat != "compat" || SchemaVisibilityInternal != "internal" { + t.Fatalf("SchemaVisibility values changed: %q, %q, %q", SchemaVisibilityPublic, SchemaVisibilityCompat, SchemaVisibilityInternal) } } diff --git a/internal/cli/schema_parameter_mapping_completeness_test.go b/internal/cli/schema_parameter_mapping_completeness_test.go index 81020721..1dafa4e7 100644 --- a/internal/cli/schema_parameter_mapping_completeness_test.go +++ b/internal/cli/schema_parameter_mapping_completeness_test.go @@ -398,7 +398,7 @@ func TestRuntimeCommandParameterSpecsPreserveReviewedEmptyPropertyProvenance(t * cmd := &cobra.Command{Use: "query"} cmd.Flags().Bool("all", false, "fetch every page") - parameters, err := runtimeCommandParameterSpecs(cmd, "aitable.query_records", nil, nil, RuntimeSchemaConstraints{}) + parameters, err := runtimeCommandParameterSpecs(cmd, "aitable.query_records", nil, RuntimeSchemaConstraints{}) if err != nil { t.Fatal(err) } diff --git a/internal/cli/schema_parameter_precedence_test.go b/internal/cli/schema_parameter_precedence_test.go deleted file mode 100644 index 012f2405..00000000 --- a/internal/cli/schema_parameter_precedence_test.go +++ /dev/null @@ -1,893 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. - -package cli - -import ( - "encoding/json" - "reflect" - "strings" - "testing" -) - -func TestRuntimeSchemaScalarResolverUsesSourceRankNotInputOrder(t *testing.T) { - tests := []struct { - name string - high runtimeSchemaFieldCandidate - low runtimeSchemaFieldCandidate - want any - }{ - { - name: "higher source can lower non-cobra boolean", - high: runtimeSchemaCandidate(false, true, "tool_schema_hint"), - low: runtimeSchemaCandidate(true, true, "mcp_metadata"), - want: false, - }, - { - name: "higher source can raise boolean", - high: runtimeSchemaCandidate(true, true, "native_annotation"), - low: runtimeSchemaCandidate(false, true, "tool_schema_hint"), - want: true, - }, - { - name: "typed string beats hint string", - high: runtimeSchemaCandidate("native", true, "native_annotation"), - low: runtimeSchemaCandidate("hint", true, "tool_schema_hint"), - want: "native", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - for _, candidates := range [][]runtimeSchemaFieldCandidate{{test.low, test.high}, {test.high, test.low}} { - winner, err := resolveRuntimeSchemaCandidate("field", candidates...) - if err != nil { - t.Fatalf("resolveRuntimeSchemaCandidate() error = %v", err) - } - if !reflect.DeepEqual(winner.Value, test.want) { - t.Fatalf("winner = %#v, want %#v", winner.Value, test.want) - } - if winner.Source != test.high.Source { - t.Fatalf("winner source = %q, want %q", winner.Source, test.high.Source) - } - } - }) - } -} - -func TestResolveRequiredProjectionCannotLowerCobraHardRequired(t *testing.T) { - winner, err := resolveRequiredProjection(true, - runtimeSchemaManualCandidate(false, true, "reviewed lowering"), - runtimeSchemaCandidate(true, true, "cobra_hard_required"), - ) - if err != nil { - t.Fatalf("resolveRequiredProjection() error = %v", err) - } - if winner.Value != true { - t.Fatalf("winner value = %#v, want true", winner.Value) - } - if winner.Source != "cobra_hard_required" { - t.Fatalf("winner source = %q, want cobra_hard_required", winner.Source) - } - if winner.Resolution != "cobra_hard_required_floor" { - t.Fatalf("winner resolution = %q, want cobra_hard_required_floor", winner.Resolution) - } - foundManual := false - for _, candidate := range winner.Compared { - if candidate.Source == "reviewed_manual_hint" && candidate.Value == false { - foundManual = true - } - } - if !foundManual { - t.Fatalf("compared candidates omitted suppressed manual false: %#v", winner.Compared) - } -} - -func TestResolveRequiredProjectionAllowsRaiseWithoutHardRequired(t *testing.T) { - winner, err := resolveRequiredProjection(false, - runtimeSchemaManualCandidate(true, true, "reviewed raise"), - runtimeSchemaCandidate(false, true, "default"), - ) - if err != nil { - t.Fatalf("resolveRequiredProjection() error = %v", err) - } - if winner.Value != true || winner.Source != "reviewed_manual_hint" { - t.Fatalf("winner = %#v source=%q", winner.Value, winner.Source) - } -} - -func TestRuntimeSchemaParameterSourcePrecedenceMatrix(t *testing.T) { - tests := []struct { - source string - rank int - precedence string - }{ - {"reviewed_manual_hint", runtimeSchemaRankReviewedManual, runtimeSchemaPrecedenceReviewedManual}, - {"versioned_parameter_binding", runtimeSchemaRankVersionedBinding, runtimeSchemaPrecedenceVersionedBinding}, - {"require_one_of_constraint", runtimeSchemaRankConstraint, runtimeSchemaPrecedenceConstraint}, - {"typed_parameter_metadata", runtimeSchemaRankTypedMetadata, runtimeSchemaPrecedenceTypedMetadata}, - {"native_annotation", runtimeSchemaRankNativeAnnotation, runtimeSchemaPrecedenceNativeAnnotation}, - {"cobra_hard_required", runtimeSchemaRankCobraContract, runtimeSchemaPrecedenceCobra}, - {"tool_schema_hint", runtimeSchemaRankToolHint, runtimeSchemaPrecedenceToolHint}, - {"cobra_help", runtimeSchemaRankCobraHelp, runtimeSchemaPrecedenceCobraHelp}, - {"mcp_metadata", runtimeSchemaRankMCP, runtimeSchemaPrecedenceMCP}, - {"flag_name_inference", runtimeSchemaRankInference, runtimeSchemaPrecedenceInference}, - {"default", runtimeSchemaRankDefault, runtimeSchemaPrecedenceDefault}, - } - for index, test := range tests { - rank, precedence := runtimeSchemaSourcePriority(test.source) - if rank != test.rank || precedence != test.precedence { - t.Fatalf("source %q = rank %d/%q, want %d/%q", test.source, rank, precedence, test.rank, test.precedence) - } - if index > 0 && tests[index-1].rank <= test.rank { - t.Fatalf("precedence matrix is not strictly descending at %q > %q", tests[index-1].source, test.source) - } - } -} - -func TestRuntimeSchemaScalarResolverFailsEqualRankConflict(t *testing.T) { - winner, err := resolveRuntimeSchemaCandidate("required", - runtimeSchemaCandidate(false, true, "typed_parameter_metadata"), - runtimeSchemaCandidate(false, true, "typed_parameter_metadata"), - ) - if err != nil { - t.Fatalf("equal values should coalesce: %v", err) - } - provenance := runtimeSchemaFieldProvenance(winner) - selected := 0 - for _, candidate := range provenance.Candidates { - if candidate.Selected != nil && *candidate.Selected { - selected++ - } - } - if selected != 1 || len(provenance.Candidates) != 2 { - t.Fatalf("coalesced provenance = %#v", provenance) - } - - _, err = resolveRuntimeSchemaCandidate("required", - runtimeSchemaCandidate(false, true, "typed_parameter_metadata"), - runtimeSchemaCandidate(true, true, "typed_parameter_metadata"), - ) - if err == nil || !strings.Contains(err.Error(), "conflicting equal-precedence") { - t.Fatalf("equal-rank conflict error = %v", err) - } -} - -func TestRuntimeSchemaScalarResolverRejectsLowerRankConflictBehindWinner(t *testing.T) { - high := runtimeSchemaManualCandidate(false, true, "reviewed winner") - lowerA := runtimeSchemaStringCandidateAtRank(true, "lower-a", runtimeSchemaRankInference, runtimeSchemaPrecedenceInference) - lowerB := runtimeSchemaStringCandidateAtRank(false, "lower-b", runtimeSchemaRankInference, runtimeSchemaPrecedenceInference) - permutations := [][]runtimeSchemaFieldCandidate{ - {high, lowerA, lowerB}, - {high, lowerB, lowerA}, - {lowerA, high, lowerB}, - {lowerA, lowerB, high}, - {lowerB, high, lowerA}, - {lowerB, lowerA, high}, - } - - var stableError string - for index, candidates := range permutations { - _, err := resolveRuntimeSchemaCandidate("required", candidates...) - if err == nil || !strings.Contains(err.Error(), "conflicting equal-precedence") || - !strings.Contains(err.Error(), "lower-a") || !strings.Contains(err.Error(), "lower-b") { - t.Fatalf("permutation %d error = %v, want lower-rank conflict", index, err) - } - if stableError == "" { - stableError = err.Error() - } else if err.Error() != stableError { - t.Fatalf("permutation %d error = %q, want stable %q", index, err, stableError) - } - } -} - -func TestRuntimeCommandParameterSpecsBuildTypedContractDirectly(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - leaf.Flags().Int("limit", 20, "Optional page size") - if err := leaf.MarkFlagRequired("query"); err != nil { - t.Fatalf("MarkFlagRequired() error = %v", err) - } - setFlagAnnotation(leaf.Flags().Lookup("query"), runtimeSchemaFlagExampleAnnotation, "hello") - required := false - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Review optional Agent projection", - Reviewed: true, - Parameters: map[string]ManualSchemaParameterHint{ - "query": {Required: &required}, - }, - }}, - }) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - - specs, err := runtimeCommandParameterSpecs(leaf, "sample.search_items", nil, map[string]embeddedMCPParamMeta{ - "limit": {Default: "50"}, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameterSpecs() error = %v", err) - } - if got := []string{specs[0].Name, specs[1].Name}; !reflect.DeepEqual(got, []string{"limit", "query"}) { - t.Fatalf("typed parameter order = %#v", got) - } - limit, query := specs[0], specs[1] - if got := rawSchemaString(t, limit.Default); got != "20" { - t.Fatalf("typed CLI default = %q", got) - } - if got := rawSchemaString(t, limit.InterfaceDefault); got != "50" { - t.Fatalf("typed interface default = %q", got) - } - if !query.Required || !query.CLIRequired { - t.Fatalf("typed required projection = %v, CLIRequired = %v", query.Required, query.CLIRequired) - } - if got := rawSchemaString(t, query.Example); got != "hello" { - t.Fatalf("typed example = %q", got) - } - provenance := query.FieldProvenance["required"] - if provenance.Source != "cobra_hard_required" || provenance.Resolution != "cobra_hard_required_floor" { - t.Fatalf("typed required provenance = %#v", provenance) - } - if !typedProvenanceHasCandidate(t, provenance, "reviewed_manual_hint", false) { - t.Fatalf("typed provenance omitted suppressed manual false: %#v", provenance) - } - if !typedProvenanceHasCandidate(t, provenance, "cobra_hard_required", true) { - t.Fatalf("typed provenance omitted Cobra observation: %#v", provenance) - } - for _, parameter := range []ParameterSpec{limit, query} { - emptyRequiredWhen, ok := parameter.FieldProvenance["required_when"] - if !ok || string(emptyRequiredWhen.Value) != `""` || emptyRequiredWhen.Source != "default" { - t.Fatalf("%s empty required_when provenance = %#v", parameter.Name, emptyRequiredWhen) - } - } - - // The compatibility wrapper serializes the typed values and preserves the - // historical JSON string representation of defaults/examples. - payload, err := runtimeCommandParameters(leaf, "sample.search_items", nil, map[string]embeddedMCPParamMeta{ - "limit": {Default: "50"}, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - limitPayload := schemaParameterEntry(t, payload, "limit") - queryPayload := schemaParameterEntry(t, payload, "query") - if limitPayload["default"] != "20" || limitPayload["interface_default"] != "50" { - t.Fatalf("wire defaults = %#v", limitPayload) - } - if queryPayload["example"] != "hello" || queryPayload["required"] != true || queryPayload["cli_required"] != true { - t.Fatalf("wire query = %#v", queryPayload) - } -} - -func rawSchemaString(t *testing.T, value json.RawMessage) string { - t.Helper() - var decoded string - if err := json.Unmarshal(value, &decoded); err != nil { - t.Fatalf("decode raw Schema string %q: %v", value, err) - } - return decoded -} - -func typedProvenanceHasCandidate(t *testing.T, provenance FieldProvenance, source string, value bool) bool { - t.Helper() - for _, candidate := range provenance.Candidates { - if candidate.Source != source { - continue - } - var decoded bool - if err := json.Unmarshal(candidate.Value, &decoded); err != nil { - t.Fatalf("decode provenance candidate %q: %v", candidate.Value, err) - } - if decoded == value { - return true - } - } - return false -} - -func TestRuntimeSchemaParameterPrecedenceManualWinsAllProjectedSources(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - flag := leaf.Flags().Lookup("query") - setFlagAnnotation(flag, runtimeSchemaFlagBindingPropertyAnnotation, "boundQuery") - setFlagAnnotation(flag, runtimeSchemaFlagTypeAnnotation, "boolean") - setFlagAnnotation(flag, runtimeSchemaFlagDescriptionAnnotation, "Native description") - setFlagAnnotation(flag, runtimeSchemaFlagRequiredAnnotation, "false") - setFlagAnnotation(flag, runtimeSchemaFlagRequiredWhenAnnotation, "native condition") - setFlagAnnotation(flag, runtimeSchemaFlagMetadataRequiredAnnotation, "false") - setFlagAnnotation(flag, runtimeSchemaFlagMetadataRequiredWhenAnnotation, "typed condition") - - description := "Reviewed description" - property := "manualQuery" - interfaceType := "object" - required := true - requiredWhen := "manual condition" - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Reviewed conflict resolution", - Reviewed: true, - Parameters: map[string]ManualSchemaParameterHint{ - "query": { - Description: &description, - Property: &property, - InterfaceType: &interfaceType, - Required: &required, - RequiredWhen: &requiredWhen, - }, - }, - }}, - }) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - - parameters, err := runtimeCommandParameters(leaf, "sample.search_items", map[string]ParameterSchemaHint{ - "manualQuery": { - Type: "string", - Description: "Tool hint description", - Required: boolPointer(false), - RequiredWhen: "tool hint condition", - }, - }, map[string]embeddedMCPParamMeta{ - "manualQuery": { - Type: "array", - Description: "MCP description", - Required: boolPointer(false), - RequiredWhen: "MCP condition", - }, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["description"] != description || query["property"] != property || query["interface_type"] != interfaceType || query["required"] != true || query["required_when"] != requiredWhen { - t.Fatalf("resolved query = %#v", query) - } - for _, field := range []string{"description", "property", "interface_type", "required", "required_when"} { - if source := schemaParameterFieldSource(t, query, field); source != "reviewed_manual_hint" { - t.Fatalf("%s source = %q, want reviewed_manual_hint", field, source) - } - } - if reason := schemaParameterFieldReviewReason(t, query, "property"); reason != "Reviewed conflict resolution" { - t.Fatalf("property review_reason = %q", reason) - } - - // Manual review is a separate resolver input; it must not erase the - // independently owned annotations that it outranks. - if got := firstFlagAnnotation(flag, runtimeSchemaFlagTypeAnnotation); got != "boolean" { - t.Fatalf("runtime type annotation = %q", got) - } - if got := firstFlagAnnotation(flag, runtimeSchemaFlagRequiredWhenAnnotation); got != "native condition" { - t.Fatalf("runtime required_when annotation = %q", got) - } -} - -func TestRuntimeSchemaParameterPrecedenceManualCannotLowerCobraHardRequired(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - if err := leaf.MarkFlagRequired("query"); err != nil { - t.Fatalf("MarkFlagRequired() error = %v", err) - } - required := false - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Review optional projection", - Reviewed: true, - Parameters: map[string]ManualSchemaParameterHint{ - "query": {Required: &required}, - }, - }}, - }) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - parameters, err := runtimeCommandParameters(leaf, "sample.search_items", nil, nil, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["required"] != true { - t.Fatalf("required = %#v, want true", query["required"]) - } - if source := schemaParameterFieldSource(t, query, "required"); source != "cobra_hard_required" { - t.Fatalf("required source = %q", source) - } - if resolution := schemaParameterFieldResolution(t, query, "required"); resolution != "cobra_hard_required_floor" { - t.Fatalf("required resolution = %q", resolution) - } - if query["cli_required"] != true { - t.Fatalf("cli_required = %#v, want true", query["cli_required"]) - } - if !schemaParameterFieldHasCandidate(t, query, "required", "reviewed_manual_hint", false) { - t.Fatalf("required provenance does not retain suppressed manual false: %#v", schemaParameterFieldProvenance(t, query, "required")) - } - if !schemaParameterFieldHasCandidate(t, query, "required", "cobra_hard_required", true) { - t.Fatalf("required provenance does not retain Cobra observation: %#v", schemaParameterFieldProvenance(t, query, "required")) - } - if required, annotated := runtimeFlagRequiredState(leaf.Flags().Lookup("query")); !required || !annotated { - t.Fatalf("runtimeFlagRequiredState() = %v, %v", required, annotated) - } -} - -func TestRuntimeSchemaHardRequiredFloorReachesFinalParameterPayload(t *testing.T) { - root, leaf := manualSchemaHintTestTree() - if err := leaf.MarkFlagRequired("query"); err != nil { - t.Fatalf("MarkFlagRequired() error = %v", err) - } - required := false - _, err := applyManualSchemaHints(root, ManualSchemaHintSnapshot{ - Schema: manualSchemaHintSchemaRef, - Version: manualSchemaHintVersion, - Commands: []ManualSchemaCommandHint{{ - CLIPath: "sample item search", - CanonicalPath: "sample.search_items", - Reason: "Attempted optional projection must not weaken Cobra hard-required", - Reviewed: true, - Parameters: map[string]ManualSchemaParameterHint{ - "query": {Required: &required}, - }, - }}, - }) - if err != nil { - t.Fatalf("applyManualSchemaHints() error = %v", err) - } - - specs, err := runtimeCommandParameterSpecs(leaf, "sample.search_items", nil, nil, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameterSpecs() error = %v", err) - } - var query ParameterSpec - for _, spec := range specs { - if spec.Name == "query" { - query = spec - break - } - } - if query.Name == "" { - t.Fatal("missing query parameter spec") - } - payload, err := query.ToPayload() - if err != nil { - t.Fatalf("ToPayload() error = %v", err) - } - if payload["required"] != true { - t.Fatalf("final payload required = %#v, want true", payload["required"]) - } - if payload["cli_required"] != true { - t.Fatalf("final payload cli_required = %#v, want true", payload["cli_required"]) - } - provenance, _ := payload["field_provenance"].(map[string]any) - requiredProvenance, _ := provenance["required"].(map[string]any) - if schemaString(requiredProvenance["source"]) != "cobra_hard_required" { - t.Fatalf("final required provenance source = %#v", requiredProvenance["source"]) - } - if schemaString(requiredProvenance["resolution"]) != "cobra_hard_required_floor" { - t.Fatalf("final required provenance resolution = %#v", requiredProvenance["resolution"]) - } - if requiredProvenance["value"] != true { - t.Fatalf("final required provenance value = %#v, want true", requiredProvenance["value"]) - } -} - -func TestRuntimeSchemaParameterPrecedenceConstraintCannotLowerCobraHardRequired(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - leaf.Flags().String("scope", "", "Optional scope") - if err := leaf.MarkFlagRequired("query"); err != nil { - t.Fatalf("MarkFlagRequired() error = %v", err) - } - parameters, err := runtimeCommandParameters(leaf, "sample.search_items", nil, nil, RuntimeSchemaConstraints{ - RequireOneOf: [][]string{{"query", "scope"}}, - }) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["required"] != true || query["cli_required"] != true { - t.Fatalf("resolved query = %#v", query) - } - if source := schemaParameterFieldSource(t, query, "required"); source != "cobra_hard_required" { - t.Fatalf("required source = %q", source) - } - if !schemaParameterFieldHasCandidate(t, query, "required", "require_one_of_constraint", false) { - t.Fatalf("required provenance omitted constraint candidate: %#v", schemaParameterFieldProvenance(t, query, "required")) - } -} - -func TestRuntimeSchemaParameterPrecedenceBindingSelectsMCPMetadata(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - canonical := "sample.search_items" - applyRuntimeSchemaParameterBindingsFrom(leaf, canonical, map[string]map[string]string{ - canonical: {"query": "queryText"}, - }) - - parameters, err := runtimeCommandParameters(leaf, canonical, nil, map[string]embeddedMCPParamMeta{ - "queryText": { - Type: "object", - Description: "MCP query object", - Required: boolPointer(true), - RequiredWhen: "MCP condition", - }, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["property"] != "queryText" || query["interface_type"] != "object" || query["required"] != true || query["required_when"] != "MCP condition" { - t.Fatalf("resolved query = %#v", query) - } - if source := schemaParameterFieldSource(t, query, "property"); source != "versioned_parameter_binding" { - t.Fatalf("property source = %q", source) - } - if source := schemaParameterFieldSource(t, query, "interface_type"); source != "mcp_metadata" { - t.Fatalf("interface_type source = %q", source) - } - if source := schemaParameterFieldSource(t, query, "required"); source != "mcp_metadata" { - t.Fatalf("required source = %q", source) - } - if source := schemaParameterFieldSource(t, query, "required_when"); source != "mcp_metadata" { - t.Fatalf("required_when source = %q", source) - } -} - -func schemaParameterFieldHasCandidate(t *testing.T, parameter map[string]any, field, source string, value any) bool { - t.Helper() - items, ok := schemaParameterFieldProvenance(t, parameter, field)["candidates"].([]map[string]any) - if !ok { - if raw, rawOK := schemaParameterFieldProvenance(t, parameter, field)["candidates"].([]any); rawOK { - for _, candidate := range raw { - item, _ := candidate.(map[string]any) - if schemaString(item["source"]) == source && schemaParameterJSONEqual(item["value"], value) { - return true - } - } - } - return false - } - for _, item := range items { - if schemaString(item["source"]) == source && schemaParameterJSONEqual(item["value"], value) { - return true - } - } - return false -} - -func schemaParameterJSONEqual(left, right any) bool { - leftJSON, leftErr := json.Marshal(left) - rightJSON, rightErr := json.Marshal(right) - return leftErr == nil && rightErr == nil && string(leftJSON) == string(rightJSON) -} - -func TestRuntimeSchemaParameterPrecedenceTypedMetadataBeatsToolHint(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - canonical := "sample.search_items" - flag := leaf.Flags().Lookup("query") - setFlagAnnotation(flag, "x-cli-format", "native-format") - setFlagAnnotation(flag, runtimeSchemaFlagExampleAnnotation, "native-example") - setFlagAnnotationValues(flag, "x-cli-enum", "native-a", "native-b") - previous, existed := runtimeSchemaParameterMetadataByCanonical[canonical] - runtimeSchemaParameterMetadataByCanonical[canonical] = RuntimeSchemaParameterMetadata{ - Required: []string{"query"}, - RequiredWhen: map[string]string{"query": "typed condition"}, - Formats: map[string]string{"query": "typed-format"}, - Examples: map[string]string{"query": "typed-example"}, - Enums: map[string][]string{"query": {"typed-a", "typed-b"}}, - } - t.Cleanup(func() { - if existed { - runtimeSchemaParameterMetadataByCanonical[canonical] = previous - } else { - delete(runtimeSchemaParameterMetadataByCanonical, canonical) - } - }) - applyRuntimeSchemaParameterMetadata(leaf, canonical) - - parameters, err := runtimeCommandParameters(leaf, canonical, map[string]ParameterSchemaHint{ - "query": {Required: boolPointer(false), RequiredWhen: "tool hint condition"}, - }, map[string]embeddedMCPParamMeta{ - "query": {Format: "mcp-format", Enum: []string{"mcp-a", "mcp-b"}}, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["required"] != true || query["required_when"] != "typed condition" || - query["format"] != "typed-format" || query["example"] != "typed-example" || - !schemaParameterJSONEqual(query["enum"], []string{"typed-a", "typed-b"}) { - t.Fatalf("resolved query = %#v", query) - } - for _, test := range []struct { - field string - value any - }{ - {field: "required", value: true}, - {field: "required_when", value: "typed condition"}, - {field: "format", value: "typed-format"}, - {field: "example", value: "typed-example"}, - {field: "enum", value: []string{"typed-a", "typed-b"}}, - } { - t.Run(test.field, func(t *testing.T) { - if source := schemaParameterFieldSource(t, query, test.field); source != "typed_parameter_metadata" { - t.Fatalf("%s source = %q", test.field, source) - } - if !schemaParameterFieldHasCandidate(t, query, test.field, "typed_parameter_metadata", test.value) { - t.Fatalf("%s provenance omitted typed candidate %#v: %#v", test.field, test.value, schemaParameterFieldProvenance(t, query, test.field)) - } - }) - } - for _, candidate := range []struct { - field string - source string - value any - }{ - {field: "format", source: "native_annotation", value: "native-format"}, - {field: "format", source: "mcp_metadata", value: "mcp-format"}, - {field: "example", source: "native_annotation", value: "native-example"}, - {field: "enum", source: "native_annotation", value: []string{"native-a", "native-b"}}, - {field: "enum", source: "mcp_metadata", value: []string{"mcp-a", "mcp-b"}}, - } { - if !schemaParameterFieldHasCandidate(t, query, candidate.field, candidate.source, candidate.value) { - t.Errorf("%s provenance omitted %s=%#v: %#v", candidate.field, candidate.source, candidate.value, schemaParameterFieldProvenance(t, query, candidate.field)) - } - } -} - -func TestRuntimeSchemaFormatPrecedenceMatrix(t *testing.T) { - tests := []struct { - name string - native string - mcp string - usage string - want string - wantSource string - wantSources []string - }{ - { - name: "native beats mcp and inference", - native: "native-format", - mcp: "mcp-format", - usage: "RFC3339 timestamp", - want: "native-format", - wantSource: "native_annotation", - wantSources: []string{"native_annotation", "mcp_metadata", "usage_format_inference", "default"}, - }, - { - name: "mcp beats inference", - mcp: "mcp-format", - usage: "RFC3339 timestamp", - want: "mcp-format", - wantSource: "mcp_metadata", - wantSources: []string{"mcp_metadata", "usage_format_inference", "default"}, - }, - { - name: "inference beats default", - usage: "RFC3339 timestamp", - want: "date-time", - wantSource: "usage_format_inference", - wantSources: []string{"usage_format_inference", "default"}, - }, - {name: "empty default stays omitted"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - flag := leaf.Flags().Lookup("query") - if test.native != "" { - setFlagAnnotation(flag, "x-cli-format", test.native) - } - if test.usage != "" { - flag.Usage = test.usage - } - parameters, err := runtimeCommandParameters(leaf, "sample.search_items", nil, map[string]embeddedMCPParamMeta{ - "query": {Format: test.mcp}, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if test.want == "" { - if _, ok := query["format"]; ok { - t.Fatalf("empty format must stay omitted: %#v", query) - } - provenance, _ := query["field_provenance"].(map[string]any) - if _, ok := provenance["format"]; ok { - t.Fatalf("default-only omitted format must not publish provenance: %#v", provenance["format"]) - } - return - } - if query["format"] != test.want { - t.Fatalf("format = %#v, want %q", query["format"], test.want) - } - if source := schemaParameterFieldSource(t, query, "format"); source != test.wantSource { - t.Fatalf("format source = %q, want %q", source, test.wantSource) - } - for _, source := range test.wantSources { - value := any("") - switch source { - case "native_annotation": - value = test.native - case "mcp_metadata": - value = test.mcp - case "usage_format_inference": - value = "date-time" - } - if !schemaParameterFieldHasCandidate(t, query, "format", source, value) { - t.Errorf("format provenance omitted %s=%#v: %#v", source, value, schemaParameterFieldProvenance(t, query, "format")) - } - } - }) - } -} - -func TestRuntimeSchemaEnumCandidatesUseUnifiedConflictGate(t *testing.T) { - winner, err := resolveRuntimeSchemaCandidate("enum", - runtimeSchemaEnumCandidate([]string{" a ", "a", "b", "b"}, "typed_parameter_metadata"), - ) - if err != nil { - t.Fatalf("duplicate enum normalization error = %v", err) - } - if !schemaParameterJSONEqual(winner.Value, []string{"a", "b"}) { - t.Fatalf("normalized enum winner = %#v, want [a b]", winner.Value) - } - - if _, err := resolveRuntimeSchemaCandidate("enum", - runtimeSchemaEnumCandidate([]string{"a", "b"}, "typed_parameter_metadata"), - runtimeSchemaEnumCandidate([]string{"a", "b"}, "typed_parameter_metadata"), - ); err != nil { - t.Fatalf("equal enum candidates must coalesce: %v", err) - } - if _, err := resolveRuntimeSchemaCandidate("enum", - runtimeSchemaEnumCandidate([]string{"a", "b"}, "typed_parameter_metadata"), - runtimeSchemaEnumCandidate([]string{"a", "c"}, "typed_parameter_metadata"), - ); err == nil || !strings.Contains(err.Error(), "conflicting equal-precedence") { - t.Fatalf("conflicting enum candidates error = %v", err) - } -} - -func TestRuntimeSchemaParameterPrecedenceNativeAndCobraBeatHints(t *testing.T) { - _, leaf := manualSchemaHintTestTree() - flag := leaf.Flags().Lookup("query") - setFlagAnnotation(flag, runtimeSchemaFlagTypeAnnotation, "boolean") - setFlagAnnotation(flag, runtimeSchemaFlagDescriptionAnnotation, "Native description") - setFlagAnnotation(flag, runtimeSchemaFlagRequiredAnnotation, "false") - - parameters, err := runtimeCommandParameters(leaf, "sample.search_items", map[string]ParameterSchemaHint{ - "query": { - Type: "object", - Description: "Tool hint description", - Required: boolPointer(true), - }, - }, map[string]embeddedMCPParamMeta{ - "query": { - Type: "array", - Description: "MCP description", - Required: boolPointer(true), - }, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["interface_type"] != "boolean" || query["description"] != "Native description" || query["required"] != false { - t.Fatalf("resolved query = %#v", query) - } - for _, field := range []string{"interface_type", "description", "required"} { - if source := schemaParameterFieldSource(t, query, field); source != "native_annotation" { - t.Fatalf("%s source = %q, want native_annotation", field, source) - } - } - - // Without a native required override, the executable Cobra contract wins - // over descriptive Tool/MCP hints, while CLIRequired remains independently - // observable in the final projection. - flag.Annotations[runtimeSchemaFlagRequiredAnnotation] = nil - if err := leaf.MarkFlagRequired("query"); err != nil { - t.Fatalf("MarkFlagRequired() error = %v", err) - } - parameters, err = runtimeCommandParameters(leaf, "sample.search_items", map[string]ParameterSchemaHint{ - "query": {Required: boolPointer(false)}, - }, map[string]embeddedMCPParamMeta{ - "query": {Required: boolPointer(false)}, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query = schemaParameterEntry(t, parameters, "query") - if query["required"] != true || query["cli_required"] != true { - t.Fatalf("Cobra-required query = %#v", query) - } - if source := schemaParameterFieldSource(t, query, "required"); source != "cobra_hard_required" { - t.Fatalf("required source = %q, want cobra_hard_required", source) - } -} - -func TestRuntimeSchemaParameterPrecedenceToolHintBeatsMCPWithoutChangingFlagIdentity(t *testing.T) { - // Fixture-only ParameterSchemaHint injection: production RegisterSchemaHints - // stays empty after ParamDecl migration. - _, leaf := manualSchemaHintTestTree() - parameters, err := runtimeCommandParameters(leaf, "sample.search_items", map[string]ParameterSchemaHint{ - "query": { - FlagName: "query", - Type: "object", - Description: "Tool hint description", - Required: boolPointer(true), - RequiredWhen: "tool condition", - }, - }, map[string]embeddedMCPParamMeta{ - "query": { - Type: "array", - Description: "MCP description", - Required: boolPointer(false), - RequiredWhen: "MCP condition", - }, - }, RuntimeSchemaConstraints{}) - if err != nil { - t.Fatalf("runtimeCommandParameters() error = %v", err) - } - query := schemaParameterEntry(t, parameters, "query") - if query["interface_type"] != "object" || query["required"] != true || query["required_when"] != "tool condition" { - t.Fatalf("resolved query = %#v", query) - } - // Cobra Help owns the CLI description and outranks descriptive hints. - if query["description"] != "Original query text" { - t.Fatalf("description = %#v, want Cobra usage", query["description"]) - } - for _, field := range []string{"interface_type", "required", "required_when"} { - if source := schemaParameterFieldSource(t, query, field); source != "tool_schema_hint" { - t.Fatalf("%s source = %q, want tool_schema_hint", field, source) - } - } - - _, err = runtimeCommandParameters(leaf, "sample.search_items", map[string]ParameterSchemaHint{ - "query": {FlagName: "renamed-query"}, - }, nil, RuntimeSchemaConstraints{}) - if err == nil || !strings.Contains(err.Error(), "does not identify the existing Cobra flag") { - t.Fatalf("phantom flag rename error = %v", err) - } -} - -func schemaParameterEntry(t *testing.T, parameters map[string]any, name string) map[string]any { - t.Helper() - entry, ok := parameters[name].(map[string]any) - if !ok { - t.Fatalf("parameter %q = %#v", name, parameters[name]) - } - return entry -} - -func schemaParameterFieldSource(t *testing.T, parameter map[string]any, field string) string { - t.Helper() - return schemaString(schemaParameterFieldProvenance(t, parameter, field)["source"]) -} - -func schemaParameterFieldResolution(t *testing.T, parameter map[string]any, field string) string { - t.Helper() - return schemaString(schemaParameterFieldProvenance(t, parameter, field)["resolution"]) -} - -func schemaParameterFieldReviewReason(t *testing.T, parameter map[string]any, field string) string { - t.Helper() - return schemaString(schemaParameterFieldProvenance(t, parameter, field)["review_reason"]) -} - -func schemaParameterFieldProvenance(t *testing.T, parameter map[string]any, field string) map[string]any { - t.Helper() - provenance, ok := parameter["field_provenance"].(map[string]any) - if !ok { - t.Fatalf("field_provenance = %#v", parameter["field_provenance"]) - } - fieldProvenance, ok := provenance[field].(map[string]any) - if !ok { - t.Fatalf("field provenance for %q = %#v", field, provenance[field]) - } - return fieldProvenance -} diff --git a/internal/cli/schema_registry_coverage_more_test.go b/internal/cli/schema_registry_coverage_more_test.go deleted file mode 100644 index daeca9e6..00000000 --- a/internal/cli/schema_registry_coverage_more_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package cli - -import ( - "errors" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestCrossPlatformCoverageCommandRegistryLoaderErrorWrappers(t *testing.T) { - previousRegistry := loadReviewedCommandRegistry - previousBindings := validateReviewedParameterBindings - previousManual := loadReviewedManualSchemaHints - t.Cleanup(func() { - loadReviewedCommandRegistry = previousRegistry - validateReviewedParameterBindings = previousBindings - loadReviewedManualSchemaHints = previousManual - }) - - loadReviewedCommandRegistry = func() (CommandRegistry, error) { return CommandRegistry{}, errors.New("registry failed") } - // ValidateCommandRegistrySource first decodes the input independently, then - // compares against embedded. The decode path succeeds even when the lazy - // accessor is broken, so this test catches the accessor failure. - merged, err := EmbeddedCommandRegistryMergedJSON() - if err != nil { - t.Fatalf("EmbeddedCommandRegistryMergedJSON() error = %v", err) - } - if _, err := ValidateCommandRegistrySource(merged); err == nil || !strings.Contains(err.Error(), "registry failed") { - t.Fatalf("ValidateCommandRegistrySource() error = %v", err) - } - if _, err := EmbeddedCommandRegistrySourceHash(); err == nil || !strings.Contains(err.Error(), "registry failed") { - t.Fatalf("EmbeddedCommandRegistrySourceHash() error = %v", err) - } - if _, err := BuildEffectiveCommandRegistry(&cobra.Command{Use: "dws"}); err == nil || !strings.Contains(err.Error(), "registry failed") { - t.Fatalf("BuildEffectiveCommandRegistry() registry error = %v", err) - } - - loadReviewedCommandRegistry = previousRegistry - validateReviewedParameterBindings = func() error { return errors.New("bindings failed") } - if _, err := BuildEffectiveCommandRegistry(&cobra.Command{Use: "dws"}); err == nil || !strings.Contains(err.Error(), "bindings failed") { - t.Fatalf("BuildEffectiveCommandRegistry() bindings error = %v", err) - } - - validateReviewedParameterBindings = previousBindings - loadReviewedManualSchemaHints = func() (ManualSchemaHintSnapshot, error) { - return ManualSchemaHintSnapshot{}, errors.New("manual failed") - } - if _, err := BuildEffectiveCommandRegistry(&cobra.Command{Use: "dws"}); err == nil || !strings.Contains(err.Error(), "manual failed") { - t.Fatalf("BuildEffectiveCommandRegistry() manual error = %v", err) - } -} - -func TestCrossPlatformCoverageCommandRegistryDecodeAndIndexRemainingEdges(t *testing.T) { - base := func(products string) string { - return `{"$schema":"./schema_command_registry.schema.json","version":1,"products":` + products + `}` - } - validTool := `{"canonical_path":"sample.get","cli_path":"sample get"}` - for _, data := range []string{ - base(`[{"id":"sample","tools":[`+validTool+`]}]`) + ` {}`, - `{"$schema":"./schema_command_registry.schema.json","version":2,"products":[]}`, - `{"$schema":"wrong","version":1,"products":[]}`, - base(`[]`), - base(`[{"id":" sample","tools":[` + validTool + `]}]`), - base(`[{"id":"sample","tools":[` + validTool + `]},{"id":"sample","tools":[` + validTool + `]}]`), - base(`[{"id":"sample","tools":[]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"other.get","cli_path":"sample get"}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","source_product_id":" bad","cli_path":"sample get"}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","source_product_id":"","cli_path":"sample get"}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","cli_path":"bad *"}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","cli_path":"sample get","aliases":["bad *"]}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","cli_path":"sample get","aliases":["sample get"]}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","cli_path":"sample get","aliases":["sample read","sample read"]}]}]`), - base(`[{"id":"sample","tools":[{"canonical_path":"sample.get","cli_path":"sample get","visibility":"other"}]}]`), - } { - _, err := decodeCommandRegistry([]byte(data)) - if err == nil { - t.Fatalf("invalid registry succeeded: %s", data) - } - } - - commands := []CommandSpec{{CanonicalPath: "sample.get", PrimaryCLIPath: "sample get"}} - for _, candidate := range []CommandSpec{ - {CanonicalPath: "invalid", PrimaryCLIPath: "sample get"}, - {CanonicalPath: "sample.get", SourceProductID: "bad!", PrimaryCLIPath: "sample get"}, - {CanonicalPath: "sample.get", PrimaryCLIPath: "bad *"}, - {CanonicalPath: "sample.get", PrimaryCLIPath: "sample get", Aliases: []string{"bad *"}}, - {CanonicalPath: "sample.get", PrimaryCLIPath: "sample get", Visibility: "other"}, - } { - if _, _, _, err := indexCommandSpecs([]CommandSpec{candidate}); err == nil { - t.Fatalf("invalid indexed command succeeded: %#v", candidate) - } - } - if _, _, _, err := indexCommandSpecs(append(commands, commands[0])); err == nil { - t.Fatal("duplicate canonical command succeeded") - } - if _, _, _, err := indexCommandSpecs([]CommandSpec{ - commands[0], {CanonicalPath: "sample.read", PrimaryCLIPath: "sample read", Aliases: []string{"sample get"}}, - }); err == nil { - t.Fatal("duplicate CLI path succeeded") - } - if _, _, _, err := indexCommandSpecs([]CommandSpec{{CanonicalPath: "sample.get", PrimaryCLIPath: "sample.get"}}); err == nil { - t.Fatal("CLI path conflicting with canonical identity succeeded") - } - - left, err := newCommandRegistry(commands) - if err != nil { - t.Fatal(err) - } - right := cloneCommandRegistry(left) - right.ByCanonical["sample.get"] = CommandSpec{CanonicalPath: "sample.get", PrimaryCLIPath: "different"} - if equalCommandRegistries(left, right) { - t.Fatal("different registry details compared equal") - } - _ = hashCommandSpecs([]CommandSpec{{CanonicalPath: "sample.get", PrimaryCLIPath: "sample get"}}) -} - -func TestCrossPlatformCoverageEffectiveCommandRegistryRemainingErrors(t *testing.T) { - if _, err := BuildEffectiveCommandRegistry(nil); err == nil { - t.Fatal("nil effective root succeeded") - } - if _, err := buildEffectiveCommandRegistry(nil, CommandRegistry{}, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion}); err == nil { - t.Fatal("nil internal effective root succeeded") - } - root := &cobra.Command{Use: "dws"} - sample := &cobra.Command{Use: "sample"} - get := &cobra.Command{Use: "get", Aliases: []string{"read"}, Run: func(*cobra.Command, []string) {}} - group := &cobra.Command{Use: "group"} - sample.AddCommand(get, group) - root.AddCommand(sample) - if _, err := buildEffectiveCommandRegistry(root, CommandRegistry{}, ManualSchemaHintSnapshot{}); err == nil { - t.Fatal("invalid manual hint version succeeded") - } - if _, err := buildEffectiveCommandRegistry(root, CommandRegistry{Commands: []CommandSpec{{CanonicalPath: "invalid", PrimaryCLIPath: "bad"}}}, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion}); err == nil { - t.Fatal("invalid reviewed registry succeeded") - } - - entry := func(path, canonical string) ManualSchemaCommandHint { - return ManualSchemaCommandHint{CLIPath: path, CanonicalPath: canonical, Reviewed: true, Reason: "fixture"} - } - for _, commands := range [][]ManualSchemaCommandHint{ - {entry("bad *", "sample.get")}, - {entry("sample get", "sample.get"), entry("sample get", "sample.get")}, - {{CLIPath: "sample get", CanonicalPath: "sample.get", Reviewed: false}}, - {entry("sample get", "invalid")}, - {entry("sample missing", "sample.missing")}, - {entry("sample group", "sample.group")}, - } { - if _, err := buildEffectiveCommandRegistry(root, CommandRegistry{}, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: commands}); err == nil { - t.Fatalf("invalid manual commands succeeded: %#v", commands) - } - } - - base, err := newCommandRegistry([]CommandSpec{{CanonicalPath: "sample.get", PrimaryCLIPath: "sample get"}}) - if err != nil { - t.Fatal(err) - } - if effective, err := buildEffectiveCommandRegistry(root, base, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{entry("sample get", "sample.get")}}); err != nil || len(effective.Commands) != 1 { - t.Fatalf("existing manual command = %#v, %v", effective, err) - } - if _, err := buildEffectiveCommandRegistry(root, base, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{entry("sample read", "sample.other")}}); err == nil { - t.Fatal("Cobra alias canonical conflict succeeded") - } - if _, err := buildEffectiveCommandRegistry(root, CommandRegistry{}, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{entry("sample read", "sample.get")}}); err == nil || !strings.Contains(err.Error(), "real command path") { - t.Fatalf("unregistered Cobra alias error = %v", err) - } - if _, err := buildEffectiveCommandRegistry(root, base, ManualSchemaHintSnapshot{Version: manualSchemaHintVersion, Commands: []ManualSchemaCommandHint{entry("sample read", "sample.get")}}); err == nil || !strings.Contains(err.Error(), "cannot create an alias") { - t.Fatalf("matching Cobra alias error = %v", err) - } - if clone := cloneCommandRegistry(CommandRegistry{Commands: []CommandSpec{{CanonicalPath: "invalid"}}}); len(clone.Commands) != 0 { - t.Fatalf("invalid cloned registry = %#v", clone) - } -} diff --git a/internal/cli/schema_registry_test_helpers_test.go b/internal/cli/schema_registry_test_helpers_test.go index eba86442..20e98494 100644 --- a/internal/cli/schema_registry_test_helpers_test.go +++ b/internal/cli/schema_registry_test_helpers_test.go @@ -22,14 +22,8 @@ func boundTestCommandRegistry(root *cobra.Command) (BoundCommandRegistry, error) } path := normalizeSchemaCLIPath(strings.Join(commandPathParts(leaf), " ")) productID, toolName, source := runtimeSchemaAnnotations(leaf) - reason := "" if productID == "" || toolName == "" { - var ok bool - productID, toolName, reason, ok = runtimeManualSchemaIdentity(leaf) - if !ok { - return - } - source = "reviewed_manual_hint" + return } canonical := productID + "." + toolName grouped[canonical] = append(grouped[canonical], CommandSpec{ @@ -37,7 +31,7 @@ func boundTestCommandRegistry(root *cobra.Command) (BoundCommandRegistry, error) SourceProductID: productID, PrimaryCLIPath: path, Source: defaultString(strings.TrimSpace(source), "test_registry"), - ReviewReason: reason, + ReviewReason: "test fixture native identity", }) }) diff --git a/internal/cli/schema_runtime_registry.go b/internal/cli/schema_runtime_registry.go index a58c6300..b0554f35 100644 --- a/internal/cli/schema_runtime_registry.go +++ b/internal/cli/schema_runtime_registry.go @@ -19,7 +19,6 @@ type runtimeSchemaMetadataSources struct { } var ( - resolveApplyManualSchemaHints = ApplyEmbeddedManualSchemaHints resolveEffectiveCommandRegistry = BuildEffectiveCommandRegistry resolveBoundCommandRegistry = BindEffectiveCommandRegistry resolveAssembleSchemaRegistry = AssembleSchemaRegistryFromBound @@ -76,17 +75,14 @@ func embeddedRuntimeSchemaMetadataSources() runtimeSchemaMetadataSources { } // ResolveSchemaBuild is the only assembly path from executable Cobra commands -// and reviewed metadata into the typed Agent contract. It applies reviewed -// manual annotations once, resolves identity once, binds Cobra once, and -// assembles one SchemaRegistry. Catalog gates and serialization consume the -// returned value directly; they never re-read annotations or merge sources. +// and reviewed metadata into the typed Agent contract. It resolves identity +// once, binds Cobra once, and assembles one SchemaRegistry from ContractFinal / +// ProductDecl leaf declarations. Catalog gates and serialization consume the +// returned value directly; they never re-read overlays or merge sources. func ResolveSchemaBuild(root *cobra.Command) (ResolvedSchemaBuild, error) { if root == nil { return ResolvedSchemaBuild{}, fmt.Errorf("resolve Schema build: root is nil") } - if _, err := resolveApplyManualSchemaHints(root); err != nil { - return ResolvedSchemaBuild{}, fmt.Errorf("apply reviewed manual Schema hints: %w", err) - } effective, err := resolveEffectiveCommandRegistry(root) if err != nil { return ResolvedSchemaBuild{}, fmt.Errorf("build effective Schema command registry: %w", err) @@ -207,15 +203,13 @@ func runtimeToolSpecFromMetadata(entry runtimeSchemaEntry, metadata runtimeSchem func runtimeToolSpecFromContractFinal(entry runtimeSchemaEntry, final ContractFinalPayload, metadata runtimeSchemaMetadataSources) (ToolSpec, error) { canonicalPath := entry.ProductID + "." + entry.ToolName constraints := runtimeCommandConstraints(entry.Command) - hint := runtimeSchemaHintForEntry(entry) embeddedMeta, _ := embeddedMCPMetadataForEntryFrom(entry, metadata.Agent, metadata.MCP) // Apply parameter declarations from the ContractFinalPayload before the // resolver reads them. The decls were put there by AttachSchema at // DeclareLeafMetadata time; now that all flags exist on the fully-built - // command tree, they can be emitted as dws.schema.* annotations (rank - // native_annotation 620, outranking tool_schema_hint 500). + // command tree, they can be emitted as dws.schema.* annotations. ApplyParamDecls(entry.Command, final.Parameters) - parameters, err := resolveRuntimeParameters(entry.Command, canonicalPath, hint.Parameters, embeddedMeta.Parameters, constraints) + parameters, err := resolveRuntimeParameters(entry.Command, canonicalPath, embeddedMeta.Parameters, constraints) if err != nil { return ToolSpec{}, fmt.Errorf("resolve Contract Schema parameters for %s: %w", canonicalPath, err) } @@ -461,14 +455,13 @@ func runtimeToolSpecFromLegacyMetadata(entry runtimeSchemaEntry, metadata runtim if err != nil { return ToolSpec{}, fmt.Errorf("resolve reviewed dry-run capability for %s: %w", canonicalPath, err) } - hint := runtimeSchemaHintForEntry(entry) embeddedMeta, hasEmbeddedMeta := embeddedMCPMetadataForEntryFrom(entry, metadata.Agent, metadata.MCP) title, description, metadataSource, textProvenance, err := resolveRuntimeToolText(entry, metadata) if err != nil { return ToolSpec{}, fmt.Errorf("resolve Schema text metadata for %s: %w", canonicalPath, err) } constraints := runtimeCommandConstraints(entry.Command) - parameters, err := resolveRuntimeParameters(entry.Command, canonicalPath, hint.Parameters, embeddedMeta.Parameters, constraints) + parameters, err := resolveRuntimeParameters(entry.Command, canonicalPath, embeddedMeta.Parameters, constraints) if err != nil { return ToolSpec{}, fmt.Errorf("resolve Schema parameters for %s: %w", canonicalPath, err) } diff --git a/internal/cli/schema_runtime_registry_coverage_more_test.go b/internal/cli/schema_runtime_registry_coverage_more_test.go deleted file mode 100644 index e1a680b9..00000000 --- a/internal/cli/schema_runtime_registry_coverage_more_test.go +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -package cli - -import ( - "encoding/json" - "errors" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestCrossPlatformCoverageResolveAndAssembleSchemaRegistryErrorBoundaries(t *testing.T) { - oldApply := resolveApplyManualSchemaHints - oldEffective := resolveEffectiveCommandRegistry - oldBind := resolveBoundCommandRegistry - oldAssemble := resolveAssembleSchemaRegistry - oldDelivery := resolveValidateParameterDelivery - oldValidateBindings := assembleValidateBindings - oldCollect := assembleCollectEntries - oldTool := assembleRuntimeToolSpec - oldTyped := assembleTypedRegistry - oldMarshal := assembleMarshalRaw - t.Cleanup(func() { - resolveApplyManualSchemaHints = oldApply - resolveEffectiveCommandRegistry = oldEffective - resolveBoundCommandRegistry = oldBind - resolveAssembleSchemaRegistry = oldAssemble - resolveValidateParameterDelivery = oldDelivery - assembleValidateBindings = oldValidateBindings - assembleCollectEntries = oldCollect - assembleRuntimeToolSpec = oldTool - assembleTypedRegistry = oldTyped - assembleMarshalRaw = oldMarshal - }) - - if _, err := ResolveSchemaBuild(nil); err == nil || !strings.Contains(err.Error(), "root is nil") { - t.Fatalf("nil root error = %v", err) - } - if _, err := AssembleSchemaRegistry(nil); err == nil || !strings.Contains(err.Error(), "root is nil") { - t.Fatalf("AssembleSchemaRegistry(nil) error = %v", err) - } - root := &cobra.Command{Use: "dws"} - resolveApplyManualSchemaHints = func(*cobra.Command) (ManualSchemaHintReport, error) { - return ManualSchemaHintReport{}, errors.New("apply failed") - } - if _, err := ResolveSchemaBuild(root); err == nil || !strings.Contains(err.Error(), "apply failed") { - t.Fatalf("apply error = %v", err) - } - resolveApplyManualSchemaHints = func(*cobra.Command) (ManualSchemaHintReport, error) { return ManualSchemaHintReport{}, nil } - resolveEffectiveCommandRegistry = func(*cobra.Command) (EffectiveCommandRegistry, error) { - return EffectiveCommandRegistry{}, errors.New("effective failed") - } - if _, err := ResolveSchemaBuild(root); err == nil || !strings.Contains(err.Error(), "effective failed") { - t.Fatalf("effective error = %v", err) - } - resolveEffectiveCommandRegistry = func(*cobra.Command) (EffectiveCommandRegistry, error) { return EffectiveCommandRegistry{}, nil } - resolveBoundCommandRegistry = func(*cobra.Command, EffectiveCommandRegistry) (BoundCommandRegistry, error) { - return BoundCommandRegistry{}, errors.New("bind failed") - } - if _, err := ResolveSchemaBuild(root); err == nil || !strings.Contains(err.Error(), "bind failed") { - t.Fatalf("bind error = %v", err) - } - resolveBoundCommandRegistry = func(*cobra.Command, EffectiveCommandRegistry) (BoundCommandRegistry, error) { - return BoundCommandRegistry{}, nil - } - resolveAssembleSchemaRegistry = func(BoundCommandRegistry) (SchemaRegistry, error) { - return SchemaRegistry{}, errors.New("assemble failed") - } - if _, err := ResolveSchemaBuild(root); err == nil || !strings.Contains(err.Error(), "assemble failed") { - t.Fatalf("assemble error = %v", err) - } - resolveAssembleSchemaRegistry = func(BoundCommandRegistry) (SchemaRegistry, error) { return SchemaRegistry{}, nil } - resolveValidateParameterDelivery = func(BoundCommandRegistry, SchemaRegistry) error { return errors.New("delivery failed") } - if _, err := AssembleSchemaRegistry(root); err == nil || !strings.Contains(err.Error(), "delivery failed") { - t.Fatalf("delivery error = %v", err) - } - - assembleValidateBindings = func() error { return errors.New("bindings failed") } - if _, err := AssembleSchemaRegistryFromBound(BoundCommandRegistry{}); err == nil || !strings.Contains(err.Error(), "bindings failed") { - t.Fatalf("bindings error = %v", err) - } - assembleValidateBindings = func() error { return nil } - assembleCollectEntries = func(BoundCommandRegistry) ([]runtimeSchemaEntry, error) { return nil, errors.New("collect failed") } - if _, err := assembleSchemaRegistryFromBound(BoundCommandRegistry{}, runtimeSchemaMetadataSources{}); err == nil || !strings.Contains(err.Error(), "collect failed") { - t.Fatalf("collect error = %v", err) - } - assembleCollectEntries = func(BoundCommandRegistry) ([]runtimeSchemaEntry, error) { - return []runtimeSchemaEntry{{ProductID: "sample", ToolName: "run"}}, nil - } - assembleRuntimeToolSpec = func(runtimeSchemaEntry, runtimeSchemaMetadataSources) (ToolSpec, error) { - return ToolSpec{}, errors.New("tool failed") - } - if _, err := assembleSchemaRegistryFromBound(BoundCommandRegistry{}, runtimeSchemaMetadataSources{}); err == nil || !strings.Contains(err.Error(), "tool failed") { - t.Fatalf("tool error = %v", err) - } - - assembleCollectEntries = func(BoundCommandRegistry) ([]runtimeSchemaEntry, error) { return nil, nil } - assembleTypedRegistry = func(string, []ProductSpec) (SchemaRegistry, error) { - return SchemaRegistry{}, errors.New("typed failed") - } - if _, err := assembleSchemaRegistryFromBound(BoundCommandRegistry{}, runtimeSchemaMetadataSources{}); err == nil || !strings.Contains(err.Error(), "typed failed") { - t.Fatalf("typed registry error = %v", err) - } - assembleTypedRegistry = func(string, []ProductSpec) (SchemaRegistry, error) { return SchemaRegistry{}, nil } - assembleMarshalRaw = func(any) (json.RawMessage, error) { return nil, errors.New("marshal failed") } - if _, err := assembleSchemaRegistryFromBound(BoundCommandRegistry{}, runtimeSchemaMetadataSources{}); err == nil || !strings.Contains(err.Error(), "interface metadata") { - t.Fatalf("interface marshal error = %v", err) - } - calls := 0 - assembleMarshalRaw = func(any) (json.RawMessage, error) { - calls++ - if calls == 2 { - return nil, errors.New("marshal failed") - } - return json.RawMessage(`{}`), nil - } - if _, err := assembleSchemaRegistryFromBound(BoundCommandRegistry{}, runtimeSchemaMetadataSources{}); err == nil || !strings.Contains(err.Error(), "Agent metadata") { - t.Fatalf("agent marshal error = %v", err) - } -} - -func TestCrossPlatformCoverageRuntimeToolSpecDependencyErrors(t *testing.T) { - oldDryRun := resolveReviewedDryRun - oldText := resolveRuntimeToolText - oldParameters := resolveRuntimeParameters - t.Cleanup(func() { - resolveReviewedDryRun = oldDryRun - resolveRuntimeToolText = oldText - resolveRuntimeParameters = oldParameters - }) - entry := runtimeSchemaEntry{ProductID: "sample", ToolName: "run", Command: &cobra.Command{Use: "run"}} - metadata := runtimeSchemaMetadataSources{} - resolveReviewedDryRun = func(string) (*DryRunSpec, error) { return nil, errors.New("dry run failed") } - if _, err := runtimeToolSpecFromMetadata(entry, metadata); err == nil || !strings.Contains(err.Error(), "dry run failed") { - t.Fatalf("dry-run error = %v", err) - } - resolveReviewedDryRun = func(string) (*DryRunSpec, error) { return nil, nil } - resolveRuntimeToolText = func(runtimeSchemaEntry, runtimeSchemaMetadataSources) (string, string, string, map[string]FieldProvenance, error) { - return "", "", "", nil, errors.New("text failed") - } - if _, err := runtimeToolSpecFromMetadata(entry, metadata); err == nil || !strings.Contains(err.Error(), "text failed") { - t.Fatalf("text error = %v", err) - } - resolveRuntimeToolText = func(runtimeSchemaEntry, runtimeSchemaMetadataSources) (string, string, string, map[string]FieldProvenance, error) { - return "title", "description", "source", map[string]FieldProvenance{}, nil - } - resolveRuntimeParameters = func(*cobra.Command, string, map[string]ParameterSchemaHint, map[string]embeddedMCPParamMeta, RuntimeSchemaConstraints) ([]ParameterSpec, error) { - return nil, errors.New("parameters failed") - } - if _, err := runtimeToolSpecFromMetadata(entry, metadata); err == nil || !strings.Contains(err.Error(), "parameters failed") { - t.Fatalf("parameter error = %v", err) - } - if _, err := marshalSchemaRaw(func() {}); err == nil { - t.Fatal("marshalSchemaRaw accepted an unsupported value") - } -} - -func TestCrossPlatformCoverageRuntimeSchemaRegistryPayloadBranches(t *testing.T) { - oldSnapshot := renderRegistrySnapshot - oldPayload := renderRegistryPayload - oldProduct := renderRegistryProductSummary - oldToolPayload := renderRegistryToolPayload - oldToolSummary := renderRegistryToolSummary - t.Cleanup(func() { - renderRegistrySnapshot = oldSnapshot - renderRegistryPayload = oldPayload - renderRegistryProductSummary = oldProduct - renderRegistryToolPayload = oldToolPayload - renderRegistryToolSummary = oldToolSummary - }) - - tool, err := ToolSpecFromRuntime(RuntimeToolSpecInput{Identity: ToolIdentitySpec{ - ProductID: "sample", Name: "get", CLIName: "get", CanonicalPath: "sample.get", Path: "sample.get", - CLIPath: "sample item get", PrimaryCLIPath: "sample item get", Aliases: []string{"sample item fetch"}, - }}) - if err != nil { - t.Fatal(err) - } - registry, err := SchemaRegistryFromRuntime("fixture", []ProductSpec{{ID: "sample", Name: "Sample", Tools: []ToolSpec{tool}}}) - if err != nil { - t.Fatal(err) - } - if _, err := runtimeSchemaPayloadFromRegistry(SchemaRegistry{Products: []ProductSpec{{ID: " "}}}, nil); err == nil { - t.Fatal("invalid registry payload succeeded") - } - - renderRegistrySnapshot = func(SchemaRegistry) (SchemaSnapshotPayload, error) { - return SchemaSnapshotPayload{}, errors.New("snapshot failed") - } - if _, err := runtimeSchemaPayloadFromRegistry(registry, nil); err == nil || !strings.Contains(err.Error(), "snapshot failed") { - t.Fatalf("snapshot error = %v", err) - } - renderRegistrySnapshot = oldSnapshot - renderRegistryToolPayload = func(ToolSpec) (map[string]any, error) { return nil, errors.New("tool payload failed") } - if _, err := runtimeSchemaPayloadFromRegistry(registry, []string{"sample.get"}); err == nil || !strings.Contains(err.Error(), "tool payload failed") { - t.Fatalf("tool payload error = %v", err) - } - renderRegistryToolPayload = oldToolPayload - renderRegistryProductSummary = func(ProductSpec) (map[string]any, error) { return nil, errors.New("product failed") } - if _, err := runtimeSchemaPayloadFromRegistry(registry, []string{"sample"}); err == nil || !strings.Contains(err.Error(), "product failed") { - t.Fatalf("product summary error = %v", err) - } - renderRegistryProductSummary = oldProduct - renderRegistryToolSummary = func(ToolSpec) (map[string]any, error) { return nil, errors.New("summary failed") } - if _, err := runtimeSchemaPayloadFromRegistry(registry, []string{"sample item"}); err == nil || !strings.Contains(err.Error(), "summary failed") { - t.Fatalf("group summary error = %v", err) - } - renderRegistryToolSummary = oldToolSummary - - for _, path := range []string{"sample", "sample item", "sample.get", "sample item fetch"} { - if _, err := runtimeSchemaPayloadFromRegistry(registry, []string{path}); err != nil { - t.Errorf("payload %q error = %v", path, err) - } - } - if _, err := runtimeSchemaPayloadFromRegistry(registry, []string{"sample other"}); err == nil || !strings.Contains(err.Error(), "unknown runtime schema path") { - t.Fatalf("unknown path error = %v", err) - } - if got := schemaToolForResolvedPath(tool, "sample item fetch"); !got.Identity.IsAlias || got.Identity.CLIPath != "sample item fetch" { - t.Fatalf("alias view = %#v", got.Identity) - } - if got := schemaToolForResolvedPath(tool, "other"); got.Identity.IsAlias { - t.Fatalf("unknown path became alias = %#v", got.Identity) - } - if !schemaToolUnderGroup(tool, "sample item") || schemaToolUnderGroup(tool, "sample other") { - t.Fatal("group containment mismatch") - } - - renderRegistryPayload = func(SchemaRegistry) (map[string]any, error) { return nil, errors.New("all failed") } - if _, err := runtimeSchemaAllPayloadFromRegistry(registry); err == nil || !strings.Contains(err.Error(), "all failed") { - t.Fatalf("all payload error = %v", err) - } -} - -func TestCrossPlatformCoverageRuntimeRegistryIdentityAndAgentMetadataRemainingEdges(t *testing.T) { - baseTool := ToolSpec{Identity: ToolIdentitySpec{ - ProductID: "sample", Name: "run", CanonicalPath: "sample.run", Path: "sample.run", - CLIPath: "sample run", PrimaryCLIPath: "sample run", Aliases: []string{"sample execute"}, Source: "registry", - }} - baseCommand := CommandSpec{CanonicalPath: "sample.run", PrimaryCLIPath: "sample run", Aliases: []string{"sample execute"}, Visibility: SchemaVisibilityPublic, Source: "registry"} - makeEffective := func(command CommandSpec) EffectiveCommandRegistry { - effective, err := newEffectiveCommandRegistry([]CommandSpec{command}) - if err != nil { - t.Fatal(err) - } - return effective - } - - if err := validateSchemaRegistryAgainstCommandRegistry(SchemaRegistry{Products: []ProductSpec{{ID: " "}}}, makeEffective(baseCommand)); err == nil { - t.Fatal("invalid registry passed command registry validation") - } - if err := validateSchemaRegistryAgainstCommandRegistry(SchemaRegistry{}, makeEffective(baseCommand)); err == nil || !strings.Contains(err.Error(), "contains 0 canonical") { - t.Fatalf("count mismatch = %v", err) - } - if err := validateSchemaRegistryAgainstCommandRegistry(SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{baseTool}}}}, makeEffective(baseCommand)); err != nil { - t.Fatalf("valid registry identity rejected: %v", err) - } - emptySourceProductEffective := EffectiveCommandRegistry{ByCanonical: map[string]CommandSpec{ - "sample.run": baseCommand, - }} - if err := validateSchemaRegistryAgainstCommandRegistry(SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{baseTool}}}}, emptySourceProductEffective); err != nil { - t.Fatalf("empty expected source product fallback rejected: %v", err) - } - other := baseTool - other.Identity.ProductID, other.Identity.Name, other.Identity.CanonicalPath, other.Identity.Path = "other", "run", "other.run", "other.run" - if err := validateSchemaRegistryAgainstCommandRegistry(SchemaRegistry{Products: []ProductSpec{{ID: "other", Tools: []ToolSpec{other}}}}, makeEffective(baseCommand)); err == nil || !strings.Contains(err.Error(), "is missing") { - t.Fatalf("missing canonical = %v", err) - } - - mutations := []struct { - name string - tool func(ToolSpec) ToolSpec - want string - }{ - {name: "canonical cli", tool: func(v ToolSpec) ToolSpec { v.Identity.CLIPath = "sample execute"; return v }, want: "must equal primary"}, - {name: "alias marker", tool: func(v ToolSpec) ToolSpec { v.Identity.IsAlias = true; return v }, want: "is_alias=false"}, - {name: "primary", tool: func(v ToolSpec) ToolSpec { - v.Identity.CLIPath, v.Identity.PrimaryCLIPath = "sample other", "sample other" - return v - }, want: "primary path"}, - {name: "aliases", tool: func(v ToolSpec) ToolSpec { v.Identity.Aliases = []string{"sample alternate"}; return v }, want: "aliases"}, - } - for _, tc := range mutations { - t.Run(tc.name, func(t *testing.T) { - tool := tc.tool(baseTool) - err := validateSchemaRegistryAgainstCommandRegistry(SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{tool}}}}, makeEffective(baseCommand)) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - oldMetadata := finalSchemaAgentMetadata - t.Cleanup(func() { finalSchemaAgentMetadata = oldMetadata }) - registry := SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{baseTool}}}} - finalSchemaAgentMetadata = func() embeddedAgentMetadata { - return embeddedAgentMetadata{Tools: map[string]agentToolMetadata{"missing": {}}} - } - if err := validateSchemaRegistryAgentMetadata(registry); err == nil || !strings.Contains(err.Error(), "does not resolve") || !strings.Contains(err.Error(), "has no generated") { - t.Fatalf("unresolved metadata error = %v", err) - } - finalSchemaAgentMetadata = func() embeddedAgentMetadata { - return embeddedAgentMetadata{Tools: map[string]agentToolMetadata{"sample.run": {}, "sample execute": {}}} - } - if err := validateSchemaRegistryAgentMetadata(registry); err == nil || !strings.Contains(err.Error(), "both resolve") { - t.Fatalf("duplicate metadata error = %v", err) - } - finalSchemaAgentMetadata = func() embeddedAgentMetadata { - return embeddedAgentMetadata{Tools: map[string]agentToolMetadata{"sample.run": {}}} - } - if err := validateSchemaRegistryAgentMetadata(registry); err != nil { - t.Fatalf("valid metadata rejected: %v", err) - } - if err := validateSchemaRegistryAgentMetadata(SchemaRegistry{Products: []ProductSpec{{ID: " "}}}); err == nil { - t.Fatal("invalid registry metadata validation succeeded") - } -} - -func TestCrossPlatformCoverageFinalSchemaProvenanceRejectsInvalidTool(t *testing.T) { - registry := SchemaRegistry{Products: []ProductSpec{{ - ID: "sample", - Selection: SelectionSpec{AgentSummary: "summary", UseWhen: []string{}, AvoidWhen: []string{}}, - FieldProvenance: map[string]FieldProvenance{}, - Tools: []ToolSpec{{Identity: ToolIdentitySpec{CanonicalPath: "invalid"}}}, - }}} - err := validateFinalSchemaProvenanceCoverage(registry) - if err == nil || !strings.Contains(err.Error(), "no provenance") { - t.Fatalf("provenance error = %v", err) - } -} - -// TestCrossPlatformCoverageLegacyMetadataContractOverlaysAndDryRunProvenance -// pins the legacy (non-declared) assembly overlays: embedded Contract Risk and -// runtime-gate annotations rewrite safety with reviewed provenance, and a -// reviewed dry-run capability records its provenance winner. -func TestCrossPlatformCoverageLegacyMetadataContractOverlaysAndDryRunProvenance(t *testing.T) { - entry := runtimeSchemaEntry{ - ProductID: "sample", - ToolName: "run", - CLIName: "run", - CLIPath: "sample run", - PrimaryCLIPath: "sample run", - Source: "test", - IdentityField: commandRegistryIdentityProvenance(BoundCommandSpec{CommandSpec: CommandSpec{ - CanonicalPath: "sample.run", - PrimaryCLIPath: "sample run", - Source: "test", - }}), - } - - riskCmd := &cobra.Command{Use: "run", Short: "short", Long: "long"} - AnnotateRuntimeRisk(riskCmd, "write") - entry.Command = riskCmd - spec, err := runtimeToolSpecFromLegacyMetadata(entry, runtimeSchemaMetadataSources{}) - if err != nil { - t.Fatalf("legacy risk overlay error = %v", err) - } - if spec.Safety.Effect != "write" || spec.Safety.Risk != "medium" || spec.Safety.Confirmation != "user_required" { - t.Fatalf("legacy risk safety = %#v", spec.Safety) - } - for _, field := range []string{"risk", "confirmation", "effect"} { - if got := spec.FieldProvenance[field]; got.Source != "corecmd.contract" || got.SourceRef != "dws.schema.risk" { - t.Fatalf("risk provenance[%s] = %#v", field, got) - } - } - - gateCmd := &cobra.Command{Use: "run", Short: "short", Long: "long"} - AnnotateRuntimeGate(gateCmd, "devAppRequireWriteGuard") - entry.Command = gateCmd - spec, err = runtimeToolSpecFromLegacyMetadata(entry, runtimeSchemaMetadataSources{}) - if err != nil { - t.Fatalf("legacy gate overlay error = %v", err) - } - if spec.Safety.Confirmation != "user_required" || spec.Safety.Effect != "write" { - t.Fatalf("legacy gate safety = %#v", spec.Safety) - } - for _, field := range []string{"runtime_gate", "confirmation"} { - if got := spec.FieldProvenance[field]; got.Source != "corecmd.contract" || got.SourceRef != "dws.schema.runtime_gate" { - t.Fatalf("gate provenance[%s] = %#v", field, got) - } - } - - oldDryRun := resolveReviewedDryRun - t.Cleanup(func() { resolveReviewedDryRun = oldDryRun }) - resolveReviewedDryRun = func(string) (*DryRunSpec, error) { - return &DryRunSpec{PreviewKind: DryRunPreviewInvocation}, nil - } - entry.Command = &cobra.Command{Use: "run", Short: "short", Long: "long"} - spec, err = runtimeToolSpecFromLegacyMetadata(entry, runtimeSchemaMetadataSources{}) - if err != nil { - t.Fatalf("legacy dry-run error = %v", err) - } - if spec.DryRun == nil || spec.DryRun.PreviewKind != DryRunPreviewInvocation { - t.Fatalf("legacy dry_run = %#v", spec.DryRun) - } - if got := spec.FieldProvenance["dry_run"]; got.Source != "reviewed_dry_run_registry" { - t.Fatalf("dry_run provenance = %#v", got) - } -} diff --git a/internal/cli/schema_small_coverage_test.go b/internal/cli/schema_small_coverage_test.go deleted file mode 100644 index 0f03aded..00000000 --- a/internal/cli/schema_small_coverage_test.go +++ /dev/null @@ -1,616 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package cli - -import ( - "bytes" - "encoding/json" - "errors" - "io/fs" - "strings" - "sync" - "testing" - "testing/fstest" - - "github.com/spf13/cobra" -) - -func TestCrossPlatformCoverageSchemaCommandRemainingBranches(t *testing.T) { - for _, tc := range []struct { - name string - args []string - wantErr string - }{ - {"cli path conflict", []string{"sample", "--cli-path", "sample get"}, "mutually exclusive"}, - {"list alias", []string{"list"}, ""}, - {"all path conflict", []string{"sample", "--all"}, "--all cannot"}, - {"cli path", []string{"--cli-path", "aisearch person"}, ""}, - {"all", []string{"--all"}, ""}, - {"overview", nil, ""}, - {"missing", []string{"definitely-missing-schema-path"}, ""}, - {"compact", []string{"--compact"}, ""}, - } { - t.Run(tc.name, func(t *testing.T) { - cmd := NewSchemaCommand(nil) - var output bytes.Buffer - cmd.SetOut(&output) - cmd.SetErr(&output) - cmd.SetArgs(tc.args) - err := cmd.Execute() - if tc.name == "missing" { - if err == nil { - t.Fatal("missing schema path succeeded") - } - return - } - if tc.wantErr != "" { - if err == nil || !strings.Contains(err.Error(), tc.wantErr) { - t.Fatalf("error = %v, want %q", err, tc.wantErr) - } - return - } - if err != nil || output.Len() == 0 { - t.Fatalf("schema output length = %d, error = %v", output.Len(), err) - } - }) - } -} - -func TestCrossPlatformCoverageSchemaCommandCatalogFailure(t *testing.T) { - original := schemaCommandCatalogError - t.Cleanup(func() { schemaCommandCatalogError = original }) - schemaCommandCatalogError = func() error { return errors.New("catalog failed") } - cmd := NewSchemaCommand(nil) - cmd.SetArgs(nil) - if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "catalog failed") { - t.Fatalf("catalog failure error = %v", err) - } -} - -func TestCrossPlatformCoverageSchemaSupportParameterAndIdentityEdges(t *testing.T) { - if hasRuntimeSchemaCommand(nil) { - t.Fatal("nil command has runtime schema") - } - root := &cobra.Command{Use: "root"} - help := &cobra.Command{Use: "help", Run: func(*cobra.Command, []string) {}} - hidden := &cobra.Command{Use: "hidden", Hidden: true} - leaf := &cobra.Command{Use: "leaf", Run: func(*cobra.Command, []string) {}} - AttachRuntimeSchema(leaf, "sample", "get", "fixture") - hidden.AddCommand(leaf) - root.AddCommand(help, hidden) - var visited []*cobra.Command - walkLeafCommands(root, func(command *cobra.Command) { visited = append(visited, command) }) - if len(visited) != 1 || visited[0] != leaf { - t.Fatalf("visited leaves = %#v", visited) - } - - original := runtimeSchemaParameterMetadataByCanonical - t.Cleanup(func() { runtimeSchemaParameterMetadataByCanonical = original }) - runtimeSchemaParameterMetadataByCanonical = map[string]RuntimeSchemaParameterMetadata{} - applyRuntimeSchemaParameterMetadata(leaf, "missing") - - provenance := commandRegistryIdentityProvenance(BoundCommandSpec{ - CommandSpec: CommandSpec{CanonicalPath: "sample.get", PrimaryCLIPath: "sample get", Source: "reviewed_manual_hint"}, - AliasCommands: []BoundAlias{{Path: "sample alias", Command: nil}}, - }) - if provenance.Precedence != "reviewed_manual" || len(provenance.Candidates) != 1 { - t.Fatalf("identity provenance = %#v", provenance) - } -} - -func TestCrossPlatformCoverageAgentMetadataRemainingPureEdges(t *testing.T) { - provenance := resolvedFieldProvenance(make(chan int), " source ", " ref ", " precedence ", " resolution ", " reason ") - if string(provenance.Value) != "null" || len(provenance.Candidates) != 1 { - t.Fatalf("fallback provenance = %#v", provenance) - } -} - -func TestCrossPlatformCoverageEmbeddedAgentMetadataLoaderEdges(t *testing.T) { - // Temporary MapFS fixtures only — the retired split-domain loader seam must - // stay fail-soft without a committed schema_agent_metadata/ directory. - for _, source := range []fs.FS{ - fstest.MapFS{}, - fstest.MapFS{"schema_agent_metadata/index.json": {Data: []byte("{")}}, - fstest.MapFS{"schema_agent_metadata/index.json": {Data: []byte(`{"domains":[" "]}`)}}, - fstest.MapFS{"schema_agent_metadata/index.json": {Data: []byte(`{"domains":["bad/path"]}`)}}, - fstest.MapFS{"schema_agent_metadata/index.json": {Data: []byte(`{"domains":["sample"]}`)}}, - fstest.MapFS{ - "schema_agent_metadata/index.json": {Data: []byte(`{"domains":["sample"]}`)}, - "schema_agent_metadata/sample.json": {Data: []byte("{")}, - }, - fstest.MapFS{ - "schema_agent_metadata/index.json": {Data: []byte(`{"domains":["sample"]}`)}, - "schema_agent_metadata/sample.json": {Data: []byte(`{"product_id":"other"}`)}, - }, - } { - metadata := loadEmbeddedAgentMetadataFrom(source) - if metadata.Products == nil || metadata.Tools == nil || len(metadata.Tools) != 0 { - t.Fatalf("empty fallback metadata = %#v", metadata) - } - } - valid := fstest.MapFS{ - "schema_agent_metadata/index.json": {Data: []byte(`{"domains":["sample"]}`)}, - "schema_agent_metadata/sample.json": {Data: []byte(`{"product_id":"sample","tools":{"sample.get":{}}}`)}, - } - metadata := loadEmbeddedAgentMetadataFrom(valid) - if metadata.Products == nil || len(metadata.Tools) != 1 { - t.Fatalf("loaded agent metadata = %#v", metadata) - } -} - -func TestCrossPlatformCoverageManualAgentSelectionRemainingEdges(t *testing.T) { - baseBound := manualAgentSelectionBoundFixture() - baseHints := manualAgentHintSetFixture() - for _, tc := range []struct { - name string - mutate func(*BoundCommandRegistry, *ManualAgentHintSet) - }{ - {"invalid bound canonical", func(bound *BoundCommandRegistry, hints *ManualAgentHintSet) { - item := bound.Commands[0] - item.CanonicalPath = "invalid" - bound.Commands[0] = item - }}, - {"missing by canonical", func(bound *BoundCommandRegistry, hints *ManualAgentHintSet) { - delete(bound.ByCanonical, "sample.search_items") - }}, - {"missing use when", func(_ *BoundCommandRegistry, hints *ManualAgentHintSet) { - item := hints.Tools["sample.search_items"] - item.UseWhen = nil - hints.Tools["sample.search_items"] = item - }}, - {"missing avoid when", func(_ *BoundCommandRegistry, hints *ManualAgentHintSet) { - item := hints.Tools["sample.search_items"] - item.AvoidWhen = nil - hints.Tools["sample.search_items"] = item - }}, - {"blank use when", func(_ *BoundCommandRegistry, hints *ManualAgentHintSet) { - item := hints.Tools["sample.search_items"] - item.UseWhen = []string{" "} - hints.Tools["sample.search_items"] = item - }}, - {"blank avoid when", func(_ *BoundCommandRegistry, hints *ManualAgentHintSet) { - item := hints.Tools["sample.search_items"] - item.AvoidWhen = []string{" "} - hints.Tools["sample.search_items"] = item - }}, - } { - t.Run(tc.name, func(t *testing.T) { - bound, hints := baseBound, baseHints - bound.Commands = append([]BoundCommandSpec(nil), baseBound.Commands...) - bound.ByCanonical = cloneBoundByCanonical(baseBound.ByCanonical) - bound.ByCLIPath = cloneBoundByCanonical(baseBound.ByCLIPath) - hints.Tools = cloneManualAgentTools(baseHints.Tools) - tc.mutate(&bound, &hints) - if _, _, err := BuildManualAgentSelectionEvalFixture(bound, hints); err == nil { - t.Fatal("invalid selection fixture succeeded") - } - }) - } - - canonical := "sample.search_items" - boundItem := baseBound.ByCanonical[canonical] - for _, tc := range []struct { - name string - mutate func(*BoundCommandRegistry, *BoundCommandSpec) - }{ - {"mismatch", func(_ *BoundCommandRegistry, item *BoundCommandSpec) { item.CanonicalPath = "other.tool" }}, - {"nil command", func(_ *BoundCommandRegistry, item *BoundCommandSpec) { item.PrimaryCommand = nil }}, - {"non runnable", func(_ *BoundCommandRegistry, item *BoundCommandSpec) { - item.PrimaryCommand = &cobra.Command{Use: "leaf"} - }}, - {"empty path", func(_ *BoundCommandRegistry, item *BoundCommandSpec) { item.PrimaryCLIPath = " " }}, - {"path mismatch", func(bound *BoundCommandRegistry, _ *BoundCommandSpec) { delete(bound.ByCLIPath, "sample item search") }}, - } { - t.Run("binding "+tc.name, func(t *testing.T) { - bound := baseBound - bound.ByCLIPath = cloneBoundByCanonical(baseBound.ByCLIPath) - item := boundItem - tc.mutate(&bound, &item) - if err := validateManualAgentSelectionBinding(bound, canonical, item); err == nil { - t.Fatal("invalid binding succeeded") - } - }) - } - - originalMarshal := marshalManualAgentSelectionFixture - t.Cleanup(func() { marshalManualAgentSelectionFixture = originalMarshal }) - marshalManualAgentSelectionFixture = func(any) ([]byte, error) { return nil, errors.New("marshal failed") } - if _, err := manualAgentSelectionFixtureDigest(ManualAgentSelectionFixture{}); err == nil { - t.Fatal("selection digest marshal failure succeeded") - } - if _, _, err := BuildManualAgentSelectionEvalFixture(baseBound, baseHints); err == nil { - t.Fatal("selection fixture ignored digest failure") - } - marshalManualAgentSelectionFixture = json.Marshal -} - -func cloneBoundByCanonical(source map[string]BoundCommandSpec) map[string]BoundCommandSpec { - out := make(map[string]BoundCommandSpec, len(source)) - for key, value := range source { - out[key] = value - } - return out -} - -func cloneManualAgentTools(source map[string]ManualAgentToolHint) map[string]ManualAgentToolHint { - out := make(map[string]ManualAgentToolHint, len(source)) - for key, value := range source { - out[key] = value - } - return out -} - -func TestCrossPlatformCoverageHintDirectoryLoaderRemainingEdges(t *testing.T) { - directoryJSON := fstest.MapFS{"bad.json": {Mode: fs.ModeDir}} - invalidJSON := fstest.MapFS{"bad.json": {Data: []byte("{")}} - if _, err := loadParameterCommandsFromMetadata(fstest.MapFS{}, "["); err == nil { - t.Fatal("invalid metadata glob succeeded") - } - emptyCommands, err := loadParameterCommandsFromMetadata(fstest.MapFS{}, "*.json") - if err != nil || len(emptyCommands) != 0 { - t.Fatalf("empty metadata directory = %#v, %v", emptyCommands, err) - } - if _, err := loadParameterCommandsFromMetadata(directoryJSON, "*.json"); err == nil { - t.Fatal("metadata directory read succeeded") - } - if _, err := loadParameterCommandsFromMetadata(invalidJSON, "*.json"); err == nil { - t.Fatal("invalid metadata JSON succeeded") - } - missingPath := fstest.MapFS{"metadata.json": {Data: []byte(`{"version":1,"tools":{"sample.get":{"parameters":{"value":{"description":"value"}}}}}`)}} - if _, err := loadParameterCommandsFromMetadata(missingPath, "*.json"); err == nil { - t.Fatal("parameter metadata without cli_path succeeded") - } - validMetadata := fstest.MapFS{"metadata.json": {Data: []byte(`{"version":1,"tools":{"skip":{},"sample.get":{"cli_path":" sample get ","parameters":{"value":{"description":"value"}}}}}`)}} - commands, err := loadParameterCommandsFromMetadata(validMetadata, "*.json") - if err != nil || len(commands) != 1 || commands[0].Reason == "" || commands[0].CLIPath != "sample get" { - t.Fatalf("metadata commands = %#v, %v", commands, err) - } - - if _, err := loadAgentHintsFromSelection(fstest.MapFS{}, "["); err == nil { - t.Fatal("invalid selection glob succeeded") - } - if _, err := loadAgentHintsFromSelection(directoryJSON, "*.json"); err == nil { - t.Fatal("selection directory read succeeded") - } - if _, err := loadAgentHintsFromSelection(invalidJSON, "*.json"); err == nil { - t.Fatal("invalid selection JSON succeeded") - } - validSelection := fstest.MapFS{"sample.json": {Data: []byte(`{ - "version":1, - "products":{"sample":{"agent_summary":"summary","use_when":["use"],"avoid_when":["avoid"],"reviewed":true}}, - "tools":{"sample.get":{"agent_summary":"summary","use_when":["use"],"avoid_when":["avoid"],"examples":["dws sample get"],"reviewed":true}} - }`)}} - hints, err := loadAgentHintsFromSelection(validSelection, "*.json") - if err != nil || hints.Products["sample"].Reason == "" || len(hints.Products["sample"].Evidence) != 1 || hints.Tools["sample.get"].Reason == "" || len(hints.Tools["sample.get"].Evidence) != 1 { - t.Fatalf("selection hints = %#v, %v", hints, err) - } - - if _, err := loadManualSchemaHintsFromHintDirs(invalidJSON, "*.json", validSelection, "*.json"); err == nil { - t.Fatal("wrapper metadata failure succeeded") - } - if _, err := loadManualSchemaHintsFromHintDirs(validMetadata, "*.json", invalidJSON, "*.json"); err == nil { - t.Fatal("wrapper selection failure succeeded") - } - invalidSelection := fstest.MapFS{"sample.json": {Data: []byte(`{"version":1,"tools":{"sample.get":{}}}`)}} - if _, err := loadManualSchemaHintsFromHintDirs(validMetadata, "*.json", invalidSelection, "*.json"); err == nil { - t.Fatal("wrapper selection validation failure succeeded") - } - - // Optional metadata: nil FS or empty glob must still load selection. - for _, tc := range []struct { - name string - fs fs.FS - glob string - }{ - {name: "nil FS", fs: nil, glob: "*.json"}, - {name: "empty glob", fs: validMetadata, glob: ""}, - {name: "empty match", fs: fstest.MapFS{}, glob: "*.json"}, - } { - snap, err := loadManualSchemaHintsFromHintDirs(tc.fs, tc.glob, validSelection, "*.json") - if err != nil { - t.Fatalf("optional metadata %s: %v", tc.name, err) - } - if len(snap.Commands) != 0 { - t.Fatalf("optional metadata %s: unexpected commands %#v", tc.name, snap.Commands) - } - if _, ok := snap.AgentHints.Tools["sample.get"]; !ok { - t.Fatalf("optional metadata %s: selection tools missing", tc.name) - } - } - if commands, err := loadParameterCommandsFromMetadata(nil, "*.json"); err != nil || len(commands) != 0 { - t.Fatalf("nil metadata FS = %#v, %v", commands, err) - } -} - -func TestCrossPlatformCoverageInterfaceRegistryRemainingEdges(t *testing.T) { - ref := &embeddedMCPInterfaceRef{ProductID: "sample", RPCName: "get"} - for _, tools := range []map[string]embeddedMCPToolMetadata{ - {" ": {InterfaceRef: ref}}, - {"sample.none": {}}, - {"sample.incomplete": {InterfaceRef: &embeddedMCPInterfaceRef{ProductID: "sample"}}}, - } { - if _, err := buildInterfaceRegistry(tools); err == nil { - t.Fatalf("invalid interface registry succeeded: %#v", tools) - } - } - if err := validateSchemaRegistryInterfacesWithMetadata(SchemaRegistry{}, embeddedMCPMetadata{Tools: map[string]embeddedMCPToolMetadata{"bad": {}}}); err == nil { - t.Fatal("invalid embedded interface metadata succeeded") - } - - interfaces, err := buildInterfaceRegistry(map[string]embeddedMCPToolMetadata{"sample.get": {InterfaceRef: ref}}) - if err != nil { - t.Fatal(err) - } - for _, candidate := range []*InterfaceRefSpec{nil, {ProductID: "sample"}, {ProductID: "sample", RPCName: "missing"}} { - if err := validateToolInterfaceRef("sample.get", "mcp", candidate, interfaces); err == nil { - t.Fatalf("invalid interface ref succeeded: %#v", candidate) - } - } - - registry := SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{{ - Identity: ToolIdentitySpec{ProductID: "", Name: ""}, - Interface: InterfaceSpec{Mode: "remote", Availability: "available"}, - }}}}} - if err := validateSchemaRegistryInterfacesWithMetadata(registry, embeddedMCPMetadata{Tools: map[string]embeddedMCPToolMetadata{"sample.get": {InterfaceRef: ref}}}); err == nil || !strings.Contains(err.Error(), "") { - t.Fatalf("unknown canonical interface error = %v", err) - } -} - -func TestCrossPlatformCoverageReviewedDryRunCapabilityRemainingEdges(t *testing.T) { - originalGroups := reviewedDryRunCapabilityGroups - t.Cleanup(func() { - reviewedDryRunCapabilityGroups = originalGroups - resetReviewedDryRunCapabilitiesForTest() - }) - for _, groups := range [][]dryRunCapabilityGroup{ - {{PreviewKind: "invalid", CanonicalPaths: []string{"sample.get"}}}, - {{PreviewKind: DryRunPreviewRequest, CanonicalPaths: []string{" "}}}, - {{PreviewKind: DryRunPreviewRequest, CanonicalPaths: []string{"sample.z", "sample.a"}}}, - {{PreviewKind: DryRunPreviewRequest, CanonicalPaths: []string{"sample.get"}}, {PreviewKind: DryRunPreviewPlan, CanonicalPaths: []string{"sample.get"}}}, - } { - reviewedDryRunCapabilityGroups = groups - resetReviewedDryRunCapabilitiesForTest() - if _, err := loadReviewedDryRunCapabilities(); err == nil { - t.Fatalf("invalid dry-run groups succeeded: %#v", groups) - } - if _, err := reviewedDryRunCapability("sample.get"); err == nil { - t.Fatal("dry-run lookup ignored registry error") - } - if err := ValidateReviewedDryRunCapabilityDelivery(SchemaRegistry{}); err == nil { - t.Fatal("dry-run delivery ignored registry error") - } - } - - reviewedDryRunCapabilityGroups = []dryRunCapabilityGroup{{PreviewKind: DryRunPreviewRequest, CanonicalPaths: []string{"sample.get"}}} - resetReviewedDryRunCapabilitiesForTest() - capabilities, err := ReviewedDryRunCapabilities() - if err != nil { - t.Fatal(err) - } - delete(capabilities, "sample.get") - second, err := ReviewedDryRunCapabilities() - if err != nil { - t.Fatal(err) - } - // Defensive-copy semantics (not exact size): declared capabilities from - // other in-process assemblies may legitimately share the index. - if _, ok := second["sample.get"]; !ok { - t.Fatalf("deleting from the returned copy mutated the registry: %#v", second) - } - if spec, err := reviewedDryRunCapability("missing"); err != nil || spec != nil { - t.Fatalf("missing dry-run capability = %#v, %v", spec, err) - } - if err := ValidateReviewedDryRunCapabilityDelivery(SchemaRegistry{}); err == nil { - t.Fatal("missing final dry-run capability succeeded") - } - registry := SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{ - {Identity: ToolIdentitySpec{CanonicalPath: "sample.get"}, DryRun: &DryRunSpec{PreviewKind: DryRunPreviewPlan}}, - {Identity: ToolIdentitySpec{CanonicalPath: "sample.other"}, DryRun: &DryRunSpec{PreviewKind: DryRunPreviewRequest}}, - }}}} - if err := ValidateReviewedDryRunCapabilityDelivery(registry); err == nil { - t.Fatal("mismatched and unreviewed dry-run capabilities succeeded") - } -} - -func resetReviewedDryRunCapabilitiesForTest() { - reviewedDryRunCapabilitiesLazy.once = sync.Once{} - reviewedDryRunCapabilitiesLazy.byCanonical = nil - reviewedDryRunCapabilitiesLazy.err = nil -} - -// TestCrossPlatformCoverageDeclaredDryRunCapabilityAutoReviewed pins the single-source rule: a -// Contract-declared dry_run needs no manual allowlist entry to pass the -// delivery gate, and a conflicting manual entry is a hard error. -func TestCrossPlatformCoverageDeclaredDryRunCapabilityAutoReviewed(t *testing.T) { - clearDeclaredDryRunCapabilitiesForTest() - t.Cleanup(clearDeclaredDryRunCapabilitiesForTest) - originalGroups := reviewedDryRunCapabilityGroups - t.Cleanup(func() { - reviewedDryRunCapabilityGroups = originalGroups - resetReviewedDryRunCapabilitiesForTest() - }) - reviewedDryRunCapabilityGroups = nil - resetReviewedDryRunCapabilitiesForTest() - - registry := SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{ - {Identity: ToolIdentitySpec{CanonicalPath: "sample.declared"}, DryRun: &DryRunSpec{PreviewKind: DryRunPreviewInvocation}}, - }}}} - // No manual entry: unreviewed before recording, reviewed after. - if err := ValidateReviewedDryRunCapabilityDelivery(registry); err == nil { - t.Fatal("unrecorded declared dry-run capability succeeded") - } - recordDeclaredDryRunCapability("sample.declared", DryRunSpec{PreviewKind: DryRunPreviewInvocation}) - if err := ValidateReviewedDryRunCapabilityDelivery(registry); err != nil { - t.Fatalf("declared dry-run capability must be auto-reviewed: %v", err) - } - - // A conflicting manual entry for the same canonical is a hard error. - reviewedDryRunCapabilityGroups = []dryRunCapabilityGroup{ - {PreviewKind: DryRunPreviewPlan, CanonicalPaths: []string{"sample.declared"}}, - } - resetReviewedDryRunCapabilitiesForTest() - if _, err := loadReviewedDryRunCapabilities(); err == nil { - t.Fatal("conflicting manual + declared dry-run capability succeeded") - } -} - -func TestCrossPlatformCoverageSchemaSnapshotAdapterRemainingEdges(t *testing.T) { - if _, err := schemaToolSpecFromPayload(map[string]any{"bad": make(chan int)}); err == nil { - t.Fatal("unmarshalable tool payload succeeded") - } - if err := decodeStrictSchemaJSON([]byte(`{} {}`), &map[string]any{}); err == nil || !strings.Contains(err.Error(), "multiple") { - t.Fatalf("multiple JSON values error = %v", err) - } - if err := decodeStrictSchemaJSON([]byte(`{} trailing`), &map[string]any{}); err == nil || !strings.Contains(err.Error(), "trailing") { - t.Fatalf("trailing JSON error = %v", err) - } - if schemaJSONEqual(make(chan int), map[string]any{}) { - t.Fatal("unmarshalable JSON values compared equal") - } - - registry, snapshot := validSnapshotAdapterFixture(t) - if _, _, err := schemaRegistryFromSnapshot(SchemaCatalogSnapshot{Catalog: map[string]any{"bad": make(chan int)}}); err == nil { - t.Fatal("unmarshalable catalog succeeded") - } - if _, _, err := schemaRegistryFromSnapshot(SchemaCatalogSnapshot{Catalog: map[string]any{"unknown": true}}); err == nil { - t.Fatal("unknown catalog field succeeded") - } - - canonical := registry.Products[0].Tools[0].Identity.CanonicalPath - missing := cloneSnapshotForCoverage(t, snapshot) - delete(missing.Tools, canonical) - if _, _, err := schemaRegistryFromSnapshot(missing); err == nil || !strings.Contains(err.Error(), "no full ToolSpec") { - t.Fatalf("missing detail error = %v", err) - } - badTool := cloneSnapshotForCoverage(t, snapshot) - badTool.Tools[canonical]["unknown"] = true - if _, _, err := schemaRegistryFromSnapshot(badTool); err == nil || !strings.Contains(err.Error(), "decode Schema ToolSpec") { - t.Fatalf("invalid detail error = %v", err) - } - wrongProduct := cloneSnapshotForCoverage(t, snapshot) - wrongProduct.Tools[canonical]["product_id"] = "other" - wrongProduct.Tools[canonical]["canonical_path"] = "other.get" - if _, _, err := schemaRegistryFromSnapshot(wrongProduct); err == nil || !strings.Contains(err.Error(), "belongs to product") { - t.Fatalf("wrong product error = %v", err) - } - extra := cloneSnapshotForCoverage(t, snapshot) - extra.Tools["sample.extra"] = cloneCoverageSchemaMap(extra.Tools[canonical]) - if _, _, err := schemaRegistryFromSnapshot(extra); err == nil || !strings.Contains(err.Error(), "absent from typed products") { - t.Fatalf("extra detail error = %v", err) - } - - roundTrip := cloneSnapshotForCoverage(t, snapshot) - products := schemaMapSlice(roundTrip.Catalog["products"]) - tools := schemaMapSlice(products[0]["tools"]) - tools[0]["title"] = "changed summary" - products[0]["tools"] = tools - roundTrip.Catalog["products"] = products - if _, _, err := schemaRegistryFromSnapshot(roundTrip); err == nil { - t.Fatal("inconsistent summary round trip succeeded") - } - - invalidRegistry := SchemaRegistry{Products: []ProductSpec{{ID: " "}}} - if err := validateSnapshotTypedRoundTrip(snapshot, invalidRegistry); err == nil { - t.Fatal("invalid registry snapshot round trip succeeded") - } - missingRendered := cloneSnapshotForCoverage(t, snapshot) - missingRendered.Tools["sample.extra"] = map[string]any{} - if err := validateSnapshotTypedRoundTrip(missingRendered, registry); err == nil || !strings.Contains(err.Error(), "dropped full tool") { - t.Fatalf("dropped full tool error = %v", err) - } - changedTool := cloneSnapshotForCoverage(t, snapshot) - changedTool.Tools[canonical]["title"] = "changed" - if err := validateSnapshotTypedRoundTrip(changedTool, registry); err == nil || !strings.Contains(err.Error(), "changed full tool") { - t.Fatalf("changed full tool error = %v", err) - } - changedProducts := cloneSnapshotForCoverage(t, snapshot) - products = schemaMapSlice(changedProducts.Catalog["products"]) - products[0]["name"] = "changed" - changedProducts.Catalog["products"] = products - if err := validateSnapshotTypedRoundTrip(changedProducts, registry); err == nil || !strings.Contains(err.Error(), "product/tool summaries") { - t.Fatalf("changed products error = %v", err) - } - changedCatalog := cloneSnapshotForCoverage(t, snapshot) - changedCatalog.Catalog["source"] = "changed" - if err := validateSnapshotTypedRoundTrip(changedCatalog, registry); err == nil || !strings.Contains(err.Error(), "complete Catalog") { - t.Fatalf("changed catalog error = %v", err) - } -} - -func validSnapshotAdapterFixture(t *testing.T) (SchemaRegistry, SchemaCatalogSnapshot) { - t.Helper() - tool, err := ToolSpecFromRuntime(RuntimeToolSpecInput{Identity: ToolIdentitySpec{ - ProductID: "sample", Name: "get", CLIName: "get", CLIPath: "sample get", - }}) - if err != nil { - t.Fatal(err) - } - registry, err := SchemaRegistryFromRuntime("fixture", []ProductSpec{{ID: "sample", Name: "Sample", Tools: []ToolSpec{tool}}}) - if err != nil { - t.Fatal(err) - } - payload, err := registry.ToSnapshotPayload() - if err != nil { - t.Fatal(err) - } - return registry, SchemaCatalogSnapshot{Catalog: payload.Catalog, Tools: payload.Tools} -} - -func cloneSnapshotForCoverage(t *testing.T, source SchemaCatalogSnapshot) SchemaCatalogSnapshot { - t.Helper() - data, err := json.Marshal(source) - if err != nil { - t.Fatal(err) - } - var out SchemaCatalogSnapshot - if err := json.Unmarshal(data, &out); err != nil { - t.Fatal(err) - } - return out -} - -func cloneCoverageSchemaMap(source map[string]any) map[string]any { - out := make(map[string]any, len(source)) - for key, value := range source { - out[key] = value - } - return out -} - -// TestCrossPlatformCoverageDeclaredDryRunCapabilityIndexGuards pins the defensive edges of the -// declared dry-run index: blank canonicals are ignored, a successful lookup -// returns a copy, and corrupted index entries fail closed. -func TestCrossPlatformCoverageDeclaredDryRunCapabilityIndexGuards(t *testing.T) { - clearDeclaredDryRunCapabilitiesForTest() - t.Cleanup(clearDeclaredDryRunCapabilitiesForTest) - - recordDeclaredDryRunCapability(" ", DryRunSpec{PreviewKind: DryRunPreviewInvocation}) - capabilities, err := loadReviewedDryRunCapabilities() - if err != nil { - t.Fatal(err) - } - for canonical := range capabilities { - if strings.TrimSpace(canonical) == "" { - t.Fatal("blank canonical must not be recorded") - } - } - - recordDeclaredDryRunCapability("sample.lookup", DryRunSpec{PreviewKind: DryRunPreviewInvocation}) - spec, err := reviewedDryRunCapability(" sample.lookup ") - if err != nil || spec == nil || spec.PreviewKind != DryRunPreviewInvocation { - t.Fatalf("declared capability lookup = %#v, %v", spec, err) - } - - clearDeclaredDryRunCapabilitiesForTest() - declaredDryRunCapabilities.Store(42, DryRunSpec{PreviewKind: DryRunPreviewInvocation}) - if _, err := loadReviewedDryRunCapabilities(); err == nil || !strings.Contains(err.Error(), "non-string key") { - t.Fatalf("non-string key error = %v", err) - } - declaredDryRunCapabilities.Delete(42) - - declaredDryRunCapabilities.Store("sample.bad", "not-a-spec") - if _, err := loadReviewedDryRunCapabilities(); err == nil || !strings.Contains(err.Error(), "non-DryRunSpec value") { - t.Fatalf("non-DryRunSpec value error = %v", err) - } -} diff --git a/internal/cli/schema_text_precedence_test.go b/internal/cli/schema_text_precedence_test.go deleted file mode 100644 index 4ae83083..00000000 --- a/internal/cli/schema_text_precedence_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package cli - -import ( - "sort" - "testing" -) - -func TestRuntimeToolTextPrefersCobraHelpOverGenericMCPMetadata(t *testing.T) { - entry := runtimeSchemaEntry{ - ProductID: "aitable", - ToolName: "view_get_filter", - Title: "获取视图 filter 配置", - Description: "获取指定视图当前的筛选规则数组。", - PrimaryCLIPath: "aitable view get filter", - } - metadata := runtimeSchemaMetadataSources{MCP: embeddedMCPMetadata{Tools: map[string]embeddedMCPToolMetadata{ - "aitable.view_get_filter": { - Title: "get_views", - Description: "获取数据表的全部视图。", - }, - }}} - - title, description, metadataSource, provenance, err := runtimeToolTextMetadataFromMetadata(entry, metadata) - if err != nil { - t.Fatal(err) - } - if title != entry.Title || description != entry.Description { - t.Fatalf("tool text = %q / %q, want Cobra Help %q / %q", title, description, entry.Title, entry.Description) - } - if metadataSource != "" { - t.Fatalf("metadata source = %q, want Cobra Help", metadataSource) - } - for _, field := range []string{"title", "description"} { - if provenance[field].Source != "cobra_help" { - t.Fatalf("%s provenance = %#v, want cobra_help", field, provenance[field]) - } - } -} - -func TestProductionRegisterSchemaHintsHasNoSubstantiveOverlays(t *testing.T) { - // Framework expectation after ParamDecl / ContractFinal migration: production - // RegisterSchemaHints must not publish tool_schema_hint overlays. Temporary - // registry injection is allowed only inside unit-test fixtures. - if got := len(defaultSchemaHintRegistry.tools); got != 0 { - paths := make([]string, 0, got) - for path := range defaultSchemaHintRegistry.tools { - paths = append(paths, path) - } - sort.Strings(paths) - t.Fatalf("production schema hint registry has %d tool overlay(s), want 0; paths=%v", got, paths) - } -} - -func TestRuntimeToolTextKeepsReviewedHintAboveCobraHelp(t *testing.T) { - // Production RegisterSchemaHints maps are empty; inject a temporary - // reviewed tool_schema_hint so this precedence unit test stays self-contained. - originalHints := defaultSchemaHintRegistry - t.Cleanup(func() { defaultSchemaHintRegistry = originalHints }) - defaultSchemaHintRegistry = newSchemaHintRegistry() - const wantDescription = "查询 AI 表格记录。默认返回单页;传 --all 时自动翻页累计全部记录。" - RegisterSchemaHints("aitable", map[string]ToolSchemaHint{ - "query_records": {Description: wantDescription}, - }) - - entry := runtimeSchemaEntry{ - ProductID: "aitable", - ToolName: "query_records", - Title: "查询记录", - Description: "CLI 查询记录说明。", - PrimaryCLIPath: "aitable record query", - } - metadata := runtimeSchemaMetadataSources{MCP: embeddedMCPMetadata{Tools: map[string]embeddedMCPToolMetadata{ - "aitable.query_records": { - Title: "query_records", - Description: "通用 RPC 查询说明。", - }, - }}} - - title, description, metadataSource, provenance, err := runtimeToolTextMetadataFromMetadata(entry, metadata) - if err != nil { - t.Fatal(err) - } - if title != entry.Title { - t.Fatalf("title = %q, want Cobra Help title %q", title, entry.Title) - } - if description != wantDescription { - t.Fatalf("description = %q, want reviewed hint %q", description, wantDescription) - } - if metadataSource != "tool-schema-hint" { - t.Fatalf("metadata source = %q, want tool-schema-hint", metadataSource) - } - if provenance["title"].Source != "cobra_help" || provenance["description"].Source != "tool_schema_hint" { - t.Fatalf("provenance = %#v", provenance) - } -} diff --git a/internal/cli/schema_wukong_agent_hints_audit.json b/internal/cli/schema_wukong_agent_hints_audit.json deleted file mode 100644 index aa5a946d..00000000 --- a/internal/cli/schema_wukong_agent_hints_audit.json +++ /dev/null @@ -1,954 +0,0 @@ -{ - "version": 1, - "source": { - "kind": "imported", - "name": "dws-wukong-envelope", - "repository": "dws-wukong", - "revision": "4574f7022c32cf4c033e9b7b4156e2fec815fed8", - "channel": "prod", - "source_hash": "sha256:62d219f6115eeb6029c437965c4bcdd626e6320f53bf9d123a10e6638a286489" - }, - "coverage": { - "source_products": 31, - "matched_products": 17, - "source_tools": 279, - "eligible_tools": 245, - "matched_tools": 147, - "unmatched_tools": 98 - }, - "skipped": [ - { - "product_id": "bot", - "rpc_name": "add_robot_to_group", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "bot", - "rpc_name": "batch_recall_robot_users_msg", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "bot", - "rpc_name": "batch_send_robot_msg_to_users", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "bot", - "rpc_name": "recall_robot_group_message", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "bot", - "rpc_name": "search_my_robots", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "bot", - "rpc_name": "send_message_by_custom_robot", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "bot", - "rpc_name": "send_robot_group_message", - "source": "dws-wukong-discovery.bot.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "chat", - "rpc_name": "batch_recall_robot_users_msg", - "source": "dws-wukong-discovery.chat.prod.json", - "reason": "hidden" - }, - { - "product_id": "chat", - "rpc_name": "batch_send_robot_msg_to_users", - "source": "dws-wukong-discovery.chat.prod.json", - "reason": "hidden" - }, - { - "product_id": "chat", - "rpc_name": "list_individual_chat_message", - "source": "dws-wukong-discovery.chat.prod.json", - "reason": "hidden" - }, - { - "product_id": "chat", - "rpc_name": "send_direct_message_as_user", - "source": "dws-wukong-discovery.chat.prod.json", - "reason": "hidden" - }, - { - "product_id": "credit-bid", - "rpc_name": "ep_dossier_bidding_query", - "source": "dws-wukong-discovery.credit-bid.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-contact", - "rpc_name": "ep_contactinfo_ext_query", - "source": "dws-wukong-discovery.credit-contact.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-equity", - "rpc_name": "ep_dossier_invest_query", - "source": "dws-wukong-discovery.credit-equity.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-equity", - "rpc_name": "ep_dossier_shareholder_query", - "source": "dws-wukong-discovery.credit-equity.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-ip", - "rpc_name": "ep_dossier_copyright_query", - "source": "dws-wukong-discovery.credit-ip.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-ip", - "rpc_name": "ep_dossier_icpregistration_query", - "source": "dws-wukong-discovery.credit-ip.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-ip", - "rpc_name": "ep_dossier_patent_query", - "source": "dws-wukong-discovery.credit-ip.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-ip", - "rpc_name": "ep_dossier_trademark_query", - "source": "dws-wukong-discovery.credit-ip.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_adminpenalty_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_consum_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_courtnotice_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_dishonest_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_equitypledge_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_execute_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_finalcase_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_legalassist_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_litigation_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_owetax_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_taxviolation_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "credit-risk", - "rpc_name": "ep_dossier_verdict_query", - "source": "dws-wukong-discovery.credit-risk.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "doc-comment", - "rpc_name": "create_comment", - "source": "dws-wukong-discovery.doc-comment.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "doc-comment", - "rpc_name": "list_comments", - "source": "dws-wukong-discovery.doc-comment.prod.json", - "reason": "missing_cli_name" - }, - { - "product_id": "doc-comment", - "rpc_name": "reply_comment", - "source": "dws-wukong-discovery.doc-comment.prod.json", - "reason": "missing_cli_name" - } - ], - "unmatched": [ - { - "product_id": "aiapp", - "rpc_name": "create_ai_app", - "cli_path": "aiapp create", - "source": "dws-wukong-discovery.aiapp.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aiapp", - "rpc_name": "modify_ai_app", - "cli_path": "aiapp modify", - "source": "dws-wukong-discovery.aiapp.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aiapp", - "rpc_name": "query_ai_app", - "cli_path": "aiapp query", - "source": "dws-wukong-discovery.aiapp.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aidesign", - "rpc_name": "edit", - "cli_path": "aidesign edit", - "source": "dws-wukong-discovery.aidesign.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aidesign", - "rpc_name": "generate", - "cli_path": "aidesign generate", - "source": "dws-wukong-discovery.aidesign.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aidesign", - "rpc_name": "generate_with_image", - "cli_path": "aidesign generate-with-image", - "source": "dws-wukong-discovery.aidesign.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aidesign", - "rpc_name": "generate_with_template", - "cli_path": "aidesign generate-with-template", - "source": "dws-wukong-discovery.aidesign.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aidesign", - "rpc_name": "isolate", - "cli_path": "aidesign isolate", - "source": "dws-wukong-discovery.aidesign.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aidesign", - "rpc_name": "upscale", - "cli_path": "aidesign upscale", - "source": "dws-wukong-discovery.aidesign.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "aitable", - "rpc_name": "get_views", - "cli_path": "aitable view get", - "source": "dws-wukong-discovery.aitable.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "aitable base get", - "aitable chart get", - "aitable chart share get" - ] - }, - { - "product_id": "aitable", - "rpc_name": "update_view", - "cli_path": "aitable view update", - "source": "dws-wukong-discovery.aitable.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "aitable base update", - "aitable chart share update", - "aitable chart update" - ] - }, - { - "product_id": "blackboard", - "rpc_name": "create_blackboard", - "cli_path": "blackboard create", - "source": "dws-wukong-discovery.blackboard.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "blackboard", - "rpc_name": "list_user_blackboards", - "cli_path": "blackboard list", - "source": "dws-wukong-discovery.blackboard.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "conference", - "rpc_name": "create_meeting_reservation", - "cli_path": "conference meeting create", - "source": "dws-wukong-discovery.conference.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "contact", - "rpc_name": "search_label_by_name", - "cli_path": "contact label get", - "source": "dws-wukong-discovery.contact.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "contact user get", - "contact user profile get" - ] - }, - { - "product_id": "contact", - "rpc_name": "get_org_labels", - "cli_path": "contact label list", - "source": "dws-wukong-discovery.contact.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "contact", - "rpc_name": "get_label_members_by_labelId", - "cli_path": "contact label list-members", - "source": "dws-wukong-discovery.contact.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "contact dept list-members" - ] - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_annualreport_query", - "cli_path": "credit annual", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_bidding_query", - "cli_path": "credit bidding", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_branch_query", - "cli_path": "credit branch", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_certificate_query", - "cli_path": "credit cert-info", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_reginfochange_query", - "cli_path": "credit change", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_invest_query", - "cli_path": "credit equity invest", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_shareholder_query", - "cli_path": "credit equity shareholder", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_basicinfo_query", - "cli_path": "credit info", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_copyright_query", - "cli_path": "credit ip copyright", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_icpregistration_query", - "cli_path": "credit ip icp", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_patent_query", - "cli_path": "credit ip patent", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_trademark_query", - "cli_path": "credit ip trademark", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_contactinfo_ext_query", - "cli_path": "credit kp", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_license_query", - "cli_path": "credit license", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_member_query", - "cli_path": "credit member", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_legalassist_query", - "cli_path": "credit risk assist", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_consum_query", - "cli_path": "credit risk consum", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_courtnotice_query", - "cli_path": "credit risk court", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_dishonest_query", - "cli_path": "credit risk dishonest", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_execute_query", - "cli_path": "credit risk execute", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_finalcase_query", - "cli_path": "credit risk finalcase", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_litigation_query", - "cli_path": "credit risk litigation", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_owetax_query", - "cli_path": "credit risk owetax", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_adminpenalty_query", - "cli_path": "credit risk penalty", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_equitypledge_query", - "cli_path": "credit risk pledge", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_taxviolation_query", - "cli_path": "credit risk taxviolation", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_dossier_verdict_query", - "cli_path": "credit risk verdict", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "credit", - "rpc_name": "ep_info_search_query", - "cli_path": "credit search", - "source": "dws-wukong-discovery.credit-ep.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "docparse", - "rpc_name": "parse_pdf_to_markdown", - "cli_path": "docparse convert", - "source": "dws-wukong-discovery.docparse.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "drive", - "rpc_name": "get_recent_list", - "cli_path": "drive recent", - "source": "dws-wukong-discovery.drive.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "page_query_enterprise_account", - "cli_path": "finance account list", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "create_bank_trade_detail", - "cli_path": "finance bank create", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_bank_trade_detail", - "cli_path": "finance bank query", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "search_category", - "cli_path": "finance category search", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "save_company", - "cli_path": "finance company save", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "search_company", - "cli_path": "finance company search", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "update_company", - "cli_path": "finance company update", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_customer_by_name", - "cli_path": "finance customer get", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "page_query_customer_list", - "cli_path": "finance customer list", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "save_customer", - "cli_path": "finance customer save", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_digital_account", - "cli_path": "finance digital-invoice account", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "login_digital_invoice", - "cli_path": "finance digital-invoice do-login", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_invoice_login_status", - "cli_path": "finance digital-invoice do-login-status", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_face_qr_code", - "cli_path": "finance digital-invoice face-qr", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_face_auth_status", - "cli_path": "finance digital-invoice face-status", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_invoice_file", - "cli_path": "finance digital-invoice file", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "intelligent_goods_code", - "cli_path": "finance digital-invoice goods-code", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "issue_digital_invoice", - "cli_path": "finance digital-invoice issue", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_invoice_skill_version", - "cli_path": "finance digital-invoice skill-version", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "upload_invoice_sms_code", - "cli_path": "finance digital-invoice sms-code", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "intelligent_title", - "cli_path": "finance digital-invoice title", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "skill_query_invoice_issue_result", - "cli_path": "finance invoice issue-result", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "recommend_category_form_invoice", - "cli_path": "finance invoice recommend-category", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "skill_upload_invoice", - "cli_path": "finance invoice upload", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_fund_journal_by_day", - "cli_path": "finance journal daily", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_fund_journal_detail_url", - "cli_path": "finance journal detail-url", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "list_receiptor_account", - "cli_path": "finance payment account-list", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_receiptor_management_url", - "cli_path": "finance payment account-url", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_cashier_url", - "cli_path": "finance payment cashier-url", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "create_payment_order", - "cli_path": "finance payment create", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_wait_orders", - "cli_path": "finance payment list", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "query_payer_account_list", - "cli_path": "finance payment payer-list", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "skill_generate_receipt", - "cli_path": "finance receipt create", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "create_collection_receipt", - "cli_path": "finance receipt create-collection", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "search_supplier", - "cli_path": "finance supplier search", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_voucher_entries_approval", - "cli_path": "finance voucher entries", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "finance", - "rpc_name": "get_voucher_by_approval_no", - "cli_path": "finance voucher generate", - "source": "dws-wukong-discovery.finance.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "law", - "rpc_name": "case-search", - "cli_path": "law case", - "source": "dws-wukong-discovery.law.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "law", - "rpc_name": "legal-consult", - "cli_path": "law consult", - "source": "dws-wukong-discovery.law.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "law", - "rpc_name": "law-search", - "cli_path": "law search", - "source": "dws-wukong-discovery.law.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "mail", - "rpc_name": "create_mail_tag", - "cli_path": "mail tag create", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "mail contact create", - "mail draft create", - "mail folder create" - ] - }, - { - "product_id": "mail", - "rpc_name": "delete_mail_tag", - "cli_path": "mail tag delete", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "mail folder delete", - "mail template delete" - ] - }, - { - "product_id": "mail", - "rpc_name": "update_mail_tag", - "cli_path": "mail tag update", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "mail contact update", - "mail draft update", - "mail folder update" - ] - }, - { - "product_id": "mail", - "rpc_name": "mail_thread_batch_trash", - "cli_path": "mail thread batch-trash", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "mail", - "rpc_name": "mail_thread_batch_update", - "cli_path": "mail thread batch-update", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "mail", - "rpc_name": "mail_thread_list", - "cli_path": "mail thread list", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "mail attachment list", - "mail contact list", - "mail folder list" - ] - }, - { - "product_id": "mail", - "rpc_name": "mail_thread_trash", - "cli_path": "mail thread trash", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "mail", - "rpc_name": "mail_thread_update", - "cli_path": "mail thread update", - "source": "dws-wukong-discovery.mail.prod.json", - "reason": "not_in_public_command_surface", - "candidates": [ - "mail contact update", - "mail draft update", - "mail folder update" - ] - }, - { - "product_id": "oa", - "rpc_name": "oa_ding_user", - "cli_path": "oa approval ding-info", - "source": "dws-wukong-discovery.oa.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "oa", - "rpc_name": "revert_task", - "cli_path": "oa approval revert-task", - "source": "dws-wukong-discovery.oa.prod.json", - "reason": "not_in_public_command_surface" - }, - { - "product_id": "oa", - "rpc_name": "search_form", - "cli_path": "oa approval search-forms", - "source": "dws-wukong-discovery.oa.prod.json", - "reason": "not_in_public_command_surface" - } - ] -} diff --git a/internal/generator/agentmetadata/hints.go b/internal/generator/agentmetadata/hints.go index 5b91030b..b6f199ad 100644 --- a/internal/generator/agentmetadata/hints.go +++ b/internal/generator/agentmetadata/hints.go @@ -14,674 +14,16 @@ package agentmetadata import ( - "encoding/json" "fmt" - "path/filepath" - "sort" "strings" ) -const HintFileVersion = 1 -const HintIndexFormat = "dws-agent-hint-index" - -// HintFile is a versioned source contract. Imported files provide a reviewed -// baseline; explicit files can override scalar fields while Skills continue to -// supply routing and workflow context. -type HintFile struct { - Version int `json:"version"` - Source HintSource `json:"source"` - Coverage HintCoverage `json:"coverage,omitempty"` - Products map[string]HintProduct `json:"products,omitempty"` - Tools map[string]HintTool `json:"tools,omitempty"` - ReferenceReview map[string]ReferenceReview `json:"reference_review,omitempty"` -} - -// HintIndex is the human-authored entrypoint. It maps product IDs to optional -// metadata HintFile paths (legacy safety/interface/parameters/runtime_gate -// overlays; may be omitted once leaf facts live on Contract) and optional -// selection (Agent routing prose) HintFile paths. Selection may be omitted -// or {} once ProductDecl/ContractFinal cover routing. Index-level -// runtime_gates were removed; residual gates live on each metadata tool as -// runtime_gate. -type HintIndex struct { - Version int `json:"version"` - Format string `json:"format"` - Source HintSource `json:"source"` - Coverage HintCoverage `json:"coverage,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` - Selection map[string]string `json:"selection,omitempty"` - ReferenceReview map[string]ReferenceReview `json:"reference_review,omitempty"` -} - -type HintSource struct { - Kind string `json:"kind"` - Name string `json:"name"` - Reviewed bool `json:"reviewed,omitempty"` - Repository string `json:"repository,omitempty"` - Revision string `json:"revision,omitempty"` - Channel string `json:"channel,omitempty"` - SourceHash string `json:"source_hash,omitempty"` -} - -type HintCoverage struct { - SourceProducts int `json:"source_products,omitempty"` - MatchedProducts int `json:"matched_products,omitempty"` - SourceTools int `json:"source_tools,omitempty"` - EligibleTools int `json:"eligible_tools,omitempty"` - MatchedTools int `json:"matched_tools,omitempty"` - UnmatchedTools int `json:"unmatched_tools,omitempty"` -} - -type HintProduct struct { - AgentSummary string `json:"agent_summary,omitempty"` - UseWhen []string `json:"use_when,omitempty"` - AvoidWhen []string `json:"avoid_when,omitempty"` - SourceRefs []string `json:"source_refs,omitempty"` - agentSummaryPresent bool -} - -type HintTool struct { - AgentSummary string `json:"agent_summary,omitempty"` - UseWhen []string `json:"use_when,omitempty"` - AvoidWhen []string `json:"avoid_when,omitempty"` - Prerequisites []string `json:"prerequisites,omitempty"` - Tips []string `json:"tips,omitempty"` - Effect string `json:"effect,omitempty"` - Risk string `json:"risk,omitempty"` - Confirmation string `json:"confirmation,omitempty"` - Idempotency string `json:"idempotency,omitempty"` - RuntimeGate string `json:"runtime_gate,omitempty"` - CLIPath string `json:"cli_path,omitempty"` - Parameters map[string]json.RawMessage `json:"parameters,omitempty"` - WorkflowRefs []string `json:"workflow_refs,omitempty"` - Examples []string `json:"examples,omitempty"` - Reviewed *bool `json:"reviewed,omitempty"` - ReviewReason string `json:"review_reason,omitempty"` - SourceRefs []string `json:"source_refs,omitempty"` - InterfaceRef *InterfaceRef `json:"interface_ref,omitempty"` - InterfaceMode string `json:"interface_mode,omitempty"` - Availability string `json:"availability,omitempty"` - InterfaceReason string `json:"interface_reason,omitempty"` - agentSummaryPresent bool - effectPresent bool - riskPresent bool - confirmationPresent bool - idempotencyPresent bool - interfaceRefPresent bool - interfaceModePresent bool - availabilityPresent bool - interfaceReasonPresent bool -} - -func (hint *HintProduct) UnmarshalJSON(data []byte) error { - type wire HintProduct - var decoded wire - if err := json.Unmarshal(data, &decoded); err != nil { - return err - } - var fields map[string]json.RawMessage - _ = json.Unmarshal(data, &fields) - *hint = HintProduct(decoded) - _, hint.agentSummaryPresent = fields["agent_summary"] - return nil -} - -func (hint *HintTool) UnmarshalJSON(data []byte) error { - type wire HintTool - var decoded wire - if err := json.Unmarshal(data, &decoded); err != nil { - return err - } - var fields map[string]json.RawMessage - _ = json.Unmarshal(data, &fields) - *hint = HintTool(decoded) - _, hint.agentSummaryPresent = fields["agent_summary"] - _, hint.effectPresent = fields["effect"] - _, hint.riskPresent = fields["risk"] - _, hint.confirmationPresent = fields["confirmation"] - _, hint.idempotencyPresent = fields["idempotency"] - _, hint.interfaceRefPresent = fields["interface_ref"] - _, hint.interfaceModePresent = fields["interface_mode"] - _, hint.availabilityPresent = fields["availability"] - _, hint.interfaceReasonPresent = fields["interface_reason"] - return nil -} - -type hintFileRole string - -const ( - hintRoleImported hintFileRole = "imported" - hintRoleMetadata hintFileRole = "metadata" - hintRoleSelection hintFileRole = "selection" - hintRoleIndex hintFileRole = "index" - hintRoleLegacy hintFileRole = "legacy" -) - -type parsedHintSource struct { - file sourceFile - hint HintFile - role hintFileRole -} - -func parseHintSources(out *File, files []sourceFile, opts Options, stats *Stats, origins sourceTracker) (usedSelection bool, err error) { +// parseHintSources formerly loaded schema_hints/ HintFiles. That directory is +// fully retired: ProductDecl / ContractFinal own routing and leaf facts. +// An empty HintsDir is a no-op; any non-empty value fails closed. +func parseHintSources(_ *File, _ []sourceFile, opts Options, _ *Stats, _ sourceTracker) (usedSelection bool, err error) { if strings.TrimSpace(opts.HintsDir) == "" { return false, nil } - hintsRoot := filepath.Clean(resolvePath(opts.Root, opts.HintsDir)) - hintsPrefix := hintsRoot + string(filepath.Separator) - byPath := map[string]sourceFile{} - for _, file := range files { - cleaned := filepath.Clean(file.path) - if cleaned == hintsRoot || strings.HasPrefix(cleaned, hintsPrefix) { - byPath[cleaned] = file - } - } - - indexPath := filepath.Join(hintsRoot, "index.json") - type selectedHint struct { - file sourceFile - role hintFileRole - } - selected := []selectedHint{} - if indexFile, ok := byPath[indexPath]; ok { - var index HintIndex - if err := json.Unmarshal(indexFile.data, &index); err != nil { - return false, fmt.Errorf("decode Agent hint index %s: %w", indexFile.display, err) - } - if index.Version != HintFileVersion { - return false, fmt.Errorf("decode Agent hint index %s: unsupported version %d", indexFile.display, index.Version) - } - if strings.TrimSpace(index.Format) != HintIndexFormat { - return false, fmt.Errorf("decode Agent hint index %s: unsupported format %q", indexFile.display, index.Format) - } - kind := strings.ToLower(strings.TrimSpace(index.Source.Kind)) - if kind == "" { - kind = "explicit" - } - if kind != "explicit" { - return false, fmt.Errorf("decode Agent hint index %s: unsupported source kind %q", indexFile.display, index.Source.Kind) - } - appendMapped := func(label string, mapping map[string]string, role hintFileRole) error { - productIDs := make([]string, 0, len(mapping)) - for productID := range mapping { - productIDs = append(productIDs, productID) - } - sort.Strings(productIDs) - for _, productID := range productIDs { - rel := strings.TrimSpace(mapping[productID]) - if rel == "" { - return fmt.Errorf("decode Agent hint index %s: %s %s missing path", indexFile.display, label, productID) - } - productPath := filepath.Clean(filepath.Join(hintsRoot, filepath.FromSlash(rel))) - if productPath != hintsRoot && !strings.HasPrefix(productPath, hintsPrefix) { - return fmt.Errorf("decode Agent hint index %s: %s %s path escapes hints root", indexFile.display, label, productID) - } - file, ok := byPath[productPath] - if !ok { - return fmt.Errorf("decode Agent hint index %s: %s %s file missing: %s", indexFile.display, label, productID, rel) - } - selected = append(selected, selectedHint{file: file, role: role}) - } - return nil - } - // Metadata and selection maps are both optional end-states once - // ProductDecl / ContractFinal own routing and leaf facts. Omitted - // or empty maps pave directory deletion; mapped files still load - // when present (including empty products{}/tools{} shells). - if len(index.Metadata) > 0 { - if err := appendMapped("metadata", index.Metadata, hintRoleMetadata); err != nil { - return false, err - } - } - if len(index.Selection) > 0 { - if err := appendMapped("selection", index.Selection, hintRoleSelection); err != nil { - return false, err - } - usedSelection = true - } - importedRoot := filepath.Join(hintsRoot, "imported") - importedPrefix := importedRoot + string(filepath.Separator) - imported := []sourceFile{} - for path, file := range byPath { - if path == importedRoot || strings.HasPrefix(path, importedPrefix) { - imported = append(imported, file) - } - } - sort.Slice(imported, func(i, j int) bool { return imported[i].display < imported[j].display }) - prefixed := make([]selectedHint, 0, len(imported)+len(selected)) - for _, file := range imported { - prefixed = append(prefixed, selectedHint{file: file, role: hintRoleImported}) - } - selected = append(prefixed, selected...) - if len(index.ReferenceReview) > 0 || index.Coverage != (HintCoverage{}) { - index.Source.Kind = kind - payload, _ := json.Marshal(HintFile{ - Version: HintFileVersion, - Source: index.Source, - Coverage: index.Coverage, - ReferenceReview: index.ReferenceReview, - }) - selected = append(selected, selectedHint{ - file: sourceFile{ - path: indexPath, - display: indexFile.display, - data: payload, - }, - role: hintRoleIndex, - }) - } - } else { - for _, file := range files { - cleaned := filepath.Clean(file.path) - if cleaned == hintsRoot || strings.HasPrefix(cleaned, hintsPrefix) { - selected = append(selected, selectedHint{file: file, role: hintRoleLegacy}) - } - } - } - - parsed := []parsedHintSource{} - for _, item := range selected { - file := item.file - var hint HintFile - if err := json.Unmarshal(file.data, &hint); err != nil { - return false, fmt.Errorf("decode Agent hint %s: %w", file.display, err) - } - if hint.Version != HintFileVersion { - return false, fmt.Errorf("decode Agent hint %s: unsupported version %d", file.display, hint.Version) - } - if err := validateHintFileRole(file.display, hint, item.role); err != nil { - return false, err - } - kind := strings.ToLower(strings.TrimSpace(hint.Source.Kind)) - if kind == "" { - kind = "explicit" - } - if kind != "explicit" && kind != "imported" { - return false, fmt.Errorf("decode Agent hint %s: unsupported source kind %q", file.display, hint.Source.Kind) - } - for path, tool := range hint.Tools { - tool.InterfaceMode = strings.TrimSpace(tool.InterfaceMode) - tool.Availability = strings.TrimSpace(tool.Availability) - tool.InterfaceReason = strings.TrimSpace(tool.InterfaceReason) - if tool.InterfaceMode == "unavailable" { - return false, fmt.Errorf("decode Agent hint %s: tool %s uses legacy interface_mode=unavailable; migrate to interface_mode=mcp, local, or composite with availability=unavailable", file.display, path) - } - if tool.InterfaceMode != "" && tool.InterfaceMode != "mcp" && tool.InterfaceMode != "composite" && tool.InterfaceMode != "local" { - return false, fmt.Errorf("decode Agent hint %s: tool %s has unsupported interface_mode %q", file.display, path, tool.InterfaceMode) - } - if tool.Availability != "" && tool.Availability != "available" && tool.Availability != "unavailable" { - return false, fmt.Errorf("decode Agent hint %s: tool %s has unsupported availability %q", file.display, path, tool.Availability) - } - if tool.InterfaceRef == nil { - hint.Tools[path] = tool - continue - } - tool.InterfaceRef.ProductID = strings.TrimSpace(tool.InterfaceRef.ProductID) - tool.InterfaceRef.RPCName = strings.TrimSpace(tool.InterfaceRef.RPCName) - if tool.InterfaceRef.ProductID == "" || tool.InterfaceRef.RPCName == "" { - return false, fmt.Errorf("decode Agent hint %s: tool %s has incomplete interface_ref", file.display, path) - } - hint.Tools[path] = tool - } - for path, review := range hint.ReferenceReview { - status := strings.TrimSpace(review.Status) - target := normalizeCommandPath(review.Target) - reason := strings.TrimSpace(review.Reason) - switch status { - case "alias": - if target == "" { - return false, fmt.Errorf("decode Agent hint %s: reference %s alias is missing target", file.display, path) - } - case "group", "stale", "out_of_surface": - if target != "" { - return false, fmt.Errorf("decode Agent hint %s: reference %s status %s cannot have target", file.display, path, status) - } - default: - return false, fmt.Errorf("decode Agent hint %s: reference %s has unsupported status %q", file.display, path, status) - } - if reason == "" { - return false, fmt.Errorf("decode Agent hint %s: reference %s is missing reason", file.display, path) - } - review.Status, review.Target, review.Reason = status, target, reason - hint.ReferenceReview[path] = review - } - hint.Source.Kind = kind - parsed = append(parsed, parsedHintSource{file: file, hint: hint, role: item.role}) - } - sort.Slice(parsed, func(i, j int) bool { - left, right := hintKindPriority(parsed[i].hint.Source.Kind), hintKindPriority(parsed[j].hint.Source.Kind) - if left != right { - return left < right - } - return parsed[i].file.display < parsed[j].file.display - }) - for _, source := range parsed { - if err := applyHintSource(out, source, stats, origins); err != nil { - return false, err - } - } - return usedSelection, nil -} - -func validateHintFileRole(display string, hint HintFile, role hintFileRole) error { - switch role { - case hintRoleMetadata: - for path, tool := range hint.Tools { - if hasSelectionOnlyFields(tool) { - return fmt.Errorf("decode Agent hint %s: metadata tool %s must not carry selection fields", display, path) - } - gate := strings.TrimSpace(tool.RuntimeGate) - if gate != "" && gate != "none" && gate != "confirm_delete" && gate != "typed_yes" && gate != "confirm_dangerous" { - return fmt.Errorf("decode Agent hint %s: tool %s has unsupported runtime_gate %q", display, path, gate) - } - conf := strings.TrimSpace(tool.Confirmation) - if gate != "" && gate != "none" && conf != "" && conf != "user_required" { - return fmt.Errorf("decode Agent hint %s: tool %s runtime_gate=%s requires confirmation=user_required", display, path, gate) - } - if (gate == "" || gate == "none") && conf == "user_required" { - return fmt.Errorf("decode Agent hint %s: tool %s confirmation=user_required requires runtime_gate != none", display, path) - } - } - for path := range hint.Products { - return fmt.Errorf("decode Agent hint %s: metadata file must not carry products routing block (%s)", display, path) - } - case hintRoleSelection: - for path, tool := range hint.Tools { - if hasMetadataOnlyFields(tool) { - return fmt.Errorf("decode Agent hint %s: selection tool %s must not carry metadata fields", display, path) - } - } - } - return nil -} - -func hasSelectionOnlyFields(tool HintTool) bool { - return scalarIsPresent(tool.AgentSummary, tool.agentSummaryPresent) || - tool.UseWhen != nil || - tool.AvoidWhen != nil || - tool.Examples != nil || - tool.Prerequisites != nil || - tool.Tips != nil || - tool.WorkflowRefs != nil -} - -func hasMetadataOnlyFields(tool HintTool) bool { - return scalarIsPresent(tool.Effect, tool.effectPresent) || - scalarIsPresent(tool.Risk, tool.riskPresent) || - scalarIsPresent(tool.Confirmation, tool.confirmationPresent) || - scalarIsPresent(tool.Idempotency, tool.idempotencyPresent) || - strings.TrimSpace(tool.RuntimeGate) != "" || - len(tool.Parameters) > 0 || - strings.TrimSpace(tool.CLIPath) != "" || - tool.InterfaceRef != nil || tool.interfaceRefPresent || - scalarIsPresent(tool.InterfaceMode, tool.interfaceModePresent) || - scalarIsPresent(tool.Availability, tool.availabilityPresent) || - scalarIsPresent(tool.InterfaceReason, tool.interfaceReasonPresent) -} - -func hintKindPriority(kind string) int { - if kind == "imported" { - return 0 - } - return 1 -} - -func applyHintSource(out *File, parsed parsedHintSource, stats *Stats, origins sourceTracker) error { - hint := parsed.hint - sourceLabel := hintSourceLabel(hint.Source, parsed.file.display) - sourceRef := parsed.file.display - stats.HintFiles++ - for rawPath, review := range hint.ReferenceReview { - path := normalizeCommandPath(rawPath) - if path == "" { - continue - } - review.Status = strings.TrimSpace(review.Status) - review.Target = normalizeCommandPath(review.Target) - review.Reason = strings.TrimSpace(review.Reason) - stats.referenceReviews[path] = review - } - - productIDs := make([]string, 0, len(hint.Products)) - for productID := range hint.Products { - productIDs = append(productIDs, productID) - } - sort.Strings(productIDs) - for _, rawProductID := range productIDs { - productID := strings.TrimSpace(rawProductID) - if productID == "" { - continue - } - incoming := hint.Products[rawProductID] - metadata := out.Products[productID] - rank := hintSelectionRank(hint.Source, nil) - incomingSummaryPresent := scalarIsPresent(incoming.AgentSummary, incoming.agentSummaryPresent) - previousRank := metadata.agentSummaryRank - if err := mergeRankedStringValue( - &metadata.AgentSummary, &metadata.agentSummaryPresent, &metadata.agentSummaryRank, &metadata.agentSummaryOrigin, - incoming.AgentSummary, incomingSummaryPresent, rank, sourceRef, productID, "agent_summary", - ); err != nil { - return err - } - if incomingSummaryPresent && metadata.agentSummaryOrigin == sourceRef && rank >= previousRank { - metadata.AgentSummarySource = sourceLabel - } - useWhenPresent := incoming.UseWhen != nil - recordProductListCandidate(&metadata, "use_when", incoming.UseWhen, useWhenPresent, rank, sourceRef) - if err := mergeRankedStringList(&metadata.UseWhen, &metadata.useWhenPresent, &metadata.useWhenRank, &metadata.useWhenOrigin, incoming.UseWhen, useWhenPresent, rank, sourceRef, productID, "use_when"); err != nil { - return err - } - avoidWhenPresent := incoming.AvoidWhen != nil - recordProductListCandidate(&metadata, "avoid_when", incoming.AvoidWhen, avoidWhenPresent, rank, sourceRef) - if err := mergeRankedStringList(&metadata.AvoidWhen, &metadata.avoidWhenPresent, &metadata.avoidWhenRank, &metadata.avoidWhenOrigin, incoming.AvoidWhen, avoidWhenPresent, rank, sourceRef, productID, "avoid_when"); err != nil { - return err - } - metadata.SourceRefs = append(metadata.SourceRefs, parsed.file.display) - metadata.SourceRefs = append(metadata.SourceRefs, incoming.SourceRefs...) - out.Products[productID] = metadata - stats.HintProducts++ - } - - toolPaths := make([]string, 0, len(hint.Tools)) - for path := range hint.Tools { - toolPaths = append(toolPaths, path) - } - sort.Strings(toolPaths) - for _, rawPath := range toolPaths { - path := normalizeHintToolPath(rawPath) - if path == "" { - continue - } - incoming := hint.Tools[rawPath] - if !hasAgentHintFields(incoming) { - continue - } - metadata := out.Tools[path] - rank := hintSelectionRank(hint.Source, incoming.Reviewed) - reviewReason := hintCandidateReviewReason(hint.Source, incoming) - previousSummaryRank := metadata.agentSummaryRank - incomingSummaryPresent := scalarIsPresent(incoming.AgentSummary, incoming.agentSummaryPresent) - recordStringFieldCandidate(&metadata, "agent_summary", incoming.AgentSummary, incomingSummaryPresent, rank, sourceRef, reviewReason) - if err := mergeRankedStringValue( - &metadata.AgentSummary, &metadata.agentSummaryPresent, &metadata.agentSummaryRank, &metadata.agentSummaryOrigin, - incoming.AgentSummary, incomingSummaryPresent, rank, sourceRef, path, "agent_summary", - ); err != nil { - return err - } - if incomingSummaryPresent && metadata.agentSummaryOrigin == sourceRef && rank >= previousSummaryRank { - metadata.AgentSummarySource = sourceLabel - } - for _, list := range []struct { - name string - target *[]string - targetPresent *bool - targetRank *int - origin *string - incoming []string - }{ - {"use_when", &metadata.UseWhen, &metadata.useWhenPresent, &metadata.useWhenRank, &metadata.useWhenOrigin, incoming.UseWhen}, - {"avoid_when", &metadata.AvoidWhen, &metadata.avoidWhenPresent, &metadata.avoidWhenRank, &metadata.avoidWhenOrigin, incoming.AvoidWhen}, - {"prerequisites", &metadata.Prerequisites, &metadata.prerequisitesPresent, &metadata.prerequisitesRank, &metadata.prerequisitesOrigin, incoming.Prerequisites}, - {"tips", &metadata.Tips, &metadata.tipsPresent, &metadata.tipsRank, &metadata.tipsOrigin, incoming.Tips}, - {"workflow_refs", &metadata.WorkflowRefs, &metadata.workflowRefsPresent, &metadata.workflowRefsRank, &metadata.workflowRefsOrigin, incoming.WorkflowRefs}, - {"examples", &metadata.Examples, &metadata.examplesPresent, &metadata.examplesRank, &metadata.examplesOrigin, incoming.Examples}, - } { - incomingPresent := list.incoming != nil - recordListFieldCandidate(&metadata, list.name, list.incoming, incomingPresent, rank, sourceRef, reviewReason) - if err := mergeRankedStringList(list.target, list.targetPresent, list.targetRank, list.origin, list.incoming, incomingPresent, rank, sourceRef, path, list.name); err != nil { - return err - } - } - if err := mergeEffectValue(&metadata, incoming.Effect, scalarIsPresent(incoming.Effect, incoming.effectPresent), "agent-hint", rank, sourceRef, reviewReason, path); err != nil { - return err - } - if err := mergeRiskValue(&metadata, incoming.Risk, scalarIsPresent(incoming.Risk, incoming.riskPresent), rank, sourceRef, reviewReason, path); err != nil { - return err - } - if err := mergeConfirmationValue(&metadata, incoming.Confirmation, scalarIsPresent(incoming.Confirmation, incoming.confirmationPresent), rank, sourceRef, reviewReason, path); err != nil { - return err - } - idempotencyPresent := scalarIsPresent(incoming.Idempotency, incoming.idempotencyPresent) - recordStringFieldCandidate(&metadata, "idempotency", incoming.Idempotency, idempotencyPresent, rank, sourceRef, reviewReason) - if err := mergeRankedStringValue( - &metadata.Idempotency, &metadata.idempotencyPresent, &metadata.idempotencyRank, &metadata.idempotencyOrigin, - incoming.Idempotency, idempotencyPresent, rank, sourceRef, path, "idempotency", - ); err != nil { - return err - } - if incoming.Reviewed != nil { - recordTypedFieldCandidateValue(&metadata, "reviewed", *incoming.Reviewed, true, rank, sourceRef, reviewReason) - if metadata.Reviewed == nil || rank > metadata.reviewedRank { - value := *incoming.Reviewed - metadata.Reviewed = &value - metadata.reviewedRank = rank - metadata.reviewedOrigin = sourceRef - } else if rank == metadata.reviewedRank && *metadata.Reviewed == *incoming.Reviewed { - metadata.reviewedOrigin = stableSource(metadata.reviewedOrigin, sourceRef) - } - } - if incoming.InterfaceRef != nil || incoming.interfaceRefPresent { - candidate := ToolMetadata{ - InterfaceRef: incoming.InterfaceRef, - interfaceRefPresent: true, - interfaceRefRank: rank, - interfaceRefOrigin: sourceRef, - } - var value any - if incoming.InterfaceRef != nil { - value = incoming.InterfaceRef.ProductID + "." + incoming.InterfaceRef.RPCName - } - recordTypedFieldCandidateValue(&candidate, "interface_ref", value, true, rank, sourceRef, reviewReason) - if err := mergeRankedInterfaceRef(&metadata, candidate, path); err != nil { - return err - } - } - for _, field := range []struct { - name string - target *string - targetPresent *bool - targetRank *int - origin *string - incoming string - incomingPresent bool - }{ - {"interface_mode", &metadata.InterfaceMode, &metadata.interfaceModePresent, &metadata.interfaceModeRank, &metadata.interfaceModeOrigin, incoming.InterfaceMode, scalarIsPresent(incoming.InterfaceMode, incoming.interfaceModePresent)}, - {"availability", &metadata.Availability, &metadata.availabilityPresent, &metadata.availabilityRank, &metadata.availabilityOrigin, incoming.Availability, scalarIsPresent(incoming.Availability, incoming.availabilityPresent)}, - {"interface_reason", &metadata.InterfaceReason, &metadata.interfaceReasonPresent, &metadata.interfaceReasonRank, &metadata.interfaceReasonOrigin, incoming.InterfaceReason, scalarIsPresent(incoming.InterfaceReason, incoming.interfaceReasonPresent)}, - } { - recordStringFieldCandidate(&metadata, field.name, field.incoming, field.incomingPresent, rank, sourceRef, reviewReason) - if err := mergeRankedStringValue(field.target, field.targetPresent, field.targetRank, field.origin, field.incoming, field.incomingPresent, rank, sourceRef, path, field.name); err != nil { - return err - } - } - metadata.SourceRefs = append(metadata.SourceRefs, parsed.file.display) - metadata.SourceRefs = append(metadata.SourceRefs, incoming.SourceRefs...) - out.Tools[path] = metadata - origins.add(path, parsed.file.display, 0) - stats.HintTools++ - if strings.EqualFold(strings.TrimSpace(incoming.Risk), "high") { - stats.RiskRules++ - } - } - return nil -} - -func hasAgentHintFields(hint HintTool) bool { - return scalarIsPresent(hint.AgentSummary, hint.agentSummaryPresent) || - hint.UseWhen != nil || - hint.AvoidWhen != nil || - hint.Prerequisites != nil || - hint.Tips != nil || - scalarIsPresent(hint.Effect, hint.effectPresent) || - scalarIsPresent(hint.Risk, hint.riskPresent) || - scalarIsPresent(hint.Confirmation, hint.confirmationPresent) || - scalarIsPresent(hint.Idempotency, hint.idempotencyPresent) || - strings.TrimSpace(hint.RuntimeGate) != "" || - len(hint.Parameters) > 0 || - hint.WorkflowRefs != nil || - hint.Examples != nil || - hint.Reviewed != nil || - len(hint.SourceRefs) > 0 || - hint.InterfaceRef != nil || hint.interfaceRefPresent || - scalarIsPresent(hint.InterfaceMode, hint.interfaceModePresent) || - scalarIsPresent(hint.Availability, hint.availabilityPresent) || - scalarIsPresent(hint.InterfaceReason, hint.interfaceReasonPresent) -} - -func hintSelectionRank(source HintSource, reviewed *bool) int { - if reviewed != nil { - if *reviewed { - return selectionRankReviewedExplicit - } - return selectionRankUnreviewedExplicit - } - if source.Reviewed { - return selectionRankReviewedExplicit - } - if strings.EqualFold(strings.TrimSpace(source.Kind), "imported") { - return selectionRankImported - } - return selectionRankExplicit -} - -func hintCandidateReviewReason(source HintSource, tool HintTool) string { - if reason := strings.TrimSpace(tool.ReviewReason); reason != "" { - return reason - } - label := hintSourceLabel(source, "Agent hint") - if tool.Reviewed != nil && *tool.Reviewed { - return label + " marks this tool as reviewed" - } - if source.Reviewed { - return label + " is a reviewed Agent hint source" - } - if strings.EqualFold(strings.TrimSpace(source.Kind), "explicit") && tool.Reviewed == nil { - return label + " is an explicit Agent hint source" - } - return "" -} - -func normalizeHintToolPath(raw string) string { - raw = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(raw), "dws ")) - if raw == "" { - return "" - } - if !strings.ContainsAny(raw, " \t") && strings.Contains(raw, ".") { - return raw - } - return normalizeCommandPath(raw) -} - -func hintSourceLabel(source HintSource, fallback string) string { - name := strings.TrimSpace(source.Name) - if name == "" { - name = fallback - } - if revision := strings.TrimSpace(source.Revision); revision != "" { - if len(revision) > 12 { - revision = revision[:12] - } - name += "@" + revision - } - return name + return false, fmt.Errorf("schema_hints/ is retired; clear HintsDir and declare ProductDecl/ContractFinal instead (got %q)", opts.HintsDir) } diff --git a/internal/generator/agentmetadata/hints_coverage_test.go b/internal/generator/agentmetadata/hints_coverage_test.go deleted file mode 100644 index a7048ea0..00000000 --- a/internal/generator/agentmetadata/hints_coverage_test.go +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package agentmetadata - -import ( - "encoding/json" - "path/filepath" - "strings" - "testing" -) - -func parseCoverageHints(root string, bodies map[string]string) (bool, error) { - files := make([]sourceFile, 0, len(bodies)) - for relative, body := range bodies { - files = append(files, sourceFile{ - path: filepath.Join(root, "hints", filepath.FromSlash(relative)), - display: filepath.ToSlash(filepath.Join("hints", relative)), - data: []byte(body), - }) - } - out := &File{Products: map[string]ProductMetadata{}, Tools: map[string]ToolMetadata{}} - stats := &Stats{referenceReviews: map[string]ReferenceReview{}} - return parseHintSources(out, files, Options{Root: root, HintsDir: "hints"}, stats, sourceTracker{}) -} - -func validCoverageIndex(metadataPath, selectionPath string) string { - return `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"metadata":{"sample":"` + metadataPath + `"},"selection":{"sample":"` + selectionPath + `"}}` -} - -func TestCrossPlatformCoverageHintUnmarshalAndIndexValidationEdges(t *testing.T) { - if err := json.Unmarshal([]byte(`{`), &HintProduct{}); err == nil { - t.Fatal("invalid HintProduct succeeded") - } - if err := json.Unmarshal([]byte(`{`), &HintTool{}); err == nil { - t.Fatal("invalid HintTool succeeded") - } - if used, err := parseHintSources(&File{}, nil, Options{}, &Stats{}, sourceTracker{}); err != nil || used { - t.Fatalf("empty hints = %v, %v", used, err) - } - - root := t.TempDir() - validHint := `{"version":1,"source":{"kind":"explicit"}}` - tests := []struct { - name string - index string - files map[string]string - want string - }{ - {"invalid json", `{`, nil, "decode Agent hint index"}, - {"version", `{"version":2}`, nil, "unsupported version"}, - {"format", `{"version":1,"format":"other"}`, nil, "unsupported format"}, - {"source kind", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"imported"}}`, nil, "unsupported source kind"}, - {"empty metadata path", validCoverageIndex("", "selection.json"), map[string]string{"selection.json": validHint}, "missing path"}, - {"escaping metadata path", validCoverageIndex("../outside.json", "selection.json"), map[string]string{"selection.json": validHint}, "escapes hints root"}, - {"missing metadata file", validCoverageIndex("metadata.json", "selection.json"), map[string]string{"selection.json": validHint}, "file missing"}, - {"empty selection path", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"selection":{"sample":""}}`, nil, "missing path"}, - {"escaping selection path", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"selection":{"sample":"../outside.json"}}`, nil, "escapes hints root"}, - {"missing selection file", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"selection":{"sample":"selection.json"}}`, nil, "file missing"}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - bodies := map[string]string{"index.json": tc.index} - for path, body := range tc.files { - bodies[path] = body - } - _, err := parseCoverageHints(root, bodies) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - // Metadata and selection maps may each be omitted or empty. - for _, tc := range []struct { - name string - index string - files map[string]string - used bool - }{ - {"metadata omitted", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"selection":{"sample":"selection.json"}}`, map[string]string{"selection.json": validHint}, true}, - {"metadata empty", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"metadata":{},"selection":{"sample":"selection.json"}}`, map[string]string{"selection.json": validHint}, true}, - {"selection omitted", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"metadata":{}}`, nil, false}, - {"selection empty", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"selection":{}}`, nil, false}, - {"both empty", `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"metadata":{},"selection":{}}`, nil, false}, - } { - t.Run(tc.name, func(t *testing.T) { - bodies := map[string]string{"index.json": tc.index} - for path, body := range tc.files { - bodies[path] = body - } - used, err := parseCoverageHints(root, bodies) - if err != nil || used != tc.used { - t.Fatalf("optional maps = used=%v err=%v, want used=%v", used, err, tc.used) - } - }) - } - - // Empty-shell metadata files (tools: {}) remain valid when still mapped. - emptyShell := `{"version":1,"source":{"kind":"explicit"},"tools":{}}` - indexWithShell := validCoverageIndex("metadata.json", "selection.json") - if used, err := parseCoverageHints(root, map[string]string{ - "index.json": indexWithShell, - "metadata.json": emptyShell, - "selection.json": validHint, - }); err != nil || !used { - t.Fatalf("empty-shell metadata = %v, %v", used, err) - } - - indexWithProjection := `{"version":1,"format":"dws-agent-hint-index","source":{"kind":"explicit"},"coverage":{"source_tools":1},"metadata":{"sample":"metadata.json"},"selection":{"sample":"selection.json"},"reference_review":{"old path":{"status":"stale","reason":"removed"}}}` - if used, err := parseCoverageHints(root, map[string]string{"index.json": indexWithProjection, "metadata.json": validHint, "selection.json": validHint}); err != nil || !used { - t.Fatalf("index projection = %v, %v", used, err) - } -} - -func TestCrossPlatformCoverageHintFileAndRoleValidationEdges(t *testing.T) { - root := t.TempDir() - tests := []struct { - name string - body string - want string - }{ - {"invalid json", `{`, "decode Agent hint"}, - {"version", `{"version":2}`, "unsupported version"}, - {"source kind", `{"version":1,"source":{"kind":"other"}}`, "unsupported source kind"}, - {"legacy unavailable mode", `{"version":1,"source":{},"tools":{"sample.tool":{"interface_mode":"unavailable"}}}`, "legacy interface_mode"}, - {"invalid interface mode", `{"version":1,"source":{},"tools":{"sample.tool":{"interface_mode":"remote"}}}`, "unsupported interface_mode"}, - {"invalid availability", `{"version":1,"source":{},"tools":{"sample.tool":{"availability":"sometimes"}}}`, "unsupported availability"}, - {"incomplete interface ref", `{"version":1,"source":{},"tools":{"sample.tool":{"interface_ref":{"product_id":"sample"}}}}`, "incomplete interface_ref"}, - {"alias target", `{"version":1,"source":{},"reference_review":{"old":{"status":"alias","reason":"renamed"}}}`, "alias is missing target"}, - {"group target", `{"version":1,"source":{},"reference_review":{"old":{"status":"group","target":"new","reason":"group"}}}`, "cannot have target"}, - {"reference status", `{"version":1,"source":{},"reference_review":{"old":{"status":"other","reason":"bad"}}}`, "unsupported status"}, - {"reference reason", `{"version":1,"source":{},"reference_review":{"old":{"status":"stale"}}}`, "missing reason"}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - _, err := parseCoverageHints(root, map[string]string{"legacy.json": tc.body}) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } - - trueValue := true - roleTests := []struct { - name string - role hintFileRole - hint HintFile - want string - }{ - {"metadata selection fields", hintRoleMetadata, HintFile{Tools: map[string]HintTool{"sample.tool": {UseWhen: []string{"x"}}}}, "selection fields"}, - {"metadata bad gate", hintRoleMetadata, HintFile{Tools: map[string]HintTool{"sample.tool": {RuntimeGate: "other"}}}, "unsupported runtime_gate"}, - {"metadata gate confirmation", hintRoleMetadata, HintFile{Tools: map[string]HintTool{"sample.tool": {RuntimeGate: "typed_yes", Confirmation: "not_required"}}}, "requires confirmation"}, - {"metadata confirmation gate", hintRoleMetadata, HintFile{Tools: map[string]HintTool{"sample.tool": {RuntimeGate: "none", Confirmation: "user_required"}}}, "requires runtime_gate"}, - {"metadata products", hintRoleMetadata, HintFile{Products: map[string]HintProduct{"sample": {}}}, "must not carry products"}, - {"selection metadata fields", hintRoleSelection, HintFile{Tools: map[string]HintTool{"sample.tool": {Reviewed: &trueValue, Effect: "read", effectPresent: true}}}, "metadata fields"}, - } - for _, tc := range roleTests { - t.Run(tc.name, func(t *testing.T) { - err := validateHintFileRole("fixture", tc.hint, tc.role) - if err == nil || !strings.Contains(err.Error(), tc.want) { - t.Fatalf("error = %v, want %q", err, tc.want) - } - }) - } -} - -func TestCrossPlatformCoverageHintHelperRemainingBranches(t *testing.T) { - falseValue := false - if got := hintSelectionRank(HintSource{}, &falseValue); got != selectionRankUnreviewedExplicit { - t.Fatalf("unreviewed rank = %d", got) - } - if got := hintSelectionRank(HintSource{Reviewed: true}, nil); got != selectionRankReviewedExplicit { - t.Fatalf("reviewed source rank = %d", got) - } - if reason := hintCandidateReviewReason(HintSource{Kind: "explicit", Name: "fixture"}, HintTool{}); !strings.Contains(reason, "explicit") { - t.Fatalf("explicit reason = %q", reason) - } - if got := normalizeHintToolPath(" dws sample.tool "); got != "sample.tool" { - t.Fatalf("canonical hint path = %q", got) - } - if got := normalizeHintToolPath(" "); got != "" { - t.Fatalf("blank hint path = %q", got) - } - if got := hintSourceLabel(HintSource{Revision: "1234567890abcdef"}, "fallback"); got != "fallback@1234567890ab" { - t.Fatalf("fallback label = %q", got) - } -} - -func TestCrossPlatformCoverageHintParsingAndApplicationConflictEdges(t *testing.T) { - var product HintProduct - if err := product.UnmarshalJSON([]byte(`{`)); err == nil { - t.Fatal("direct invalid HintProduct succeeded") - } - var tool HintTool - if err := tool.UnmarshalJSON([]byte(`{`)); err == nil { - t.Fatal("direct invalid HintTool succeeded") - } - - root := t.TempDir() - badSelection := validCoverageIndex("metadata.json", "selection.json") - if _, err := parseCoverageHints(root, map[string]string{ - "index.json": badSelection, - "metadata.json": `{"version":1,"source":{"kind":"explicit"}}`, - "selection.json": `{"version":1,"source":{"kind":"explicit"},"tools":{"sample.tool":{"effect":"read"}}}`, - }); err == nil || !strings.Contains(err.Error(), "metadata fields") { - t.Fatalf("selection role error = %v", err) - } - if _, err := parseCoverageHints(root, map[string]string{ - "index.json": badSelection, - "metadata.json": `{"version":1,"source":{"kind":"explicit"}}`, - }); err == nil || !strings.Contains(err.Error(), "selection") { - t.Fatalf("missing selection error = %v", err) - } - - index := validCoverageIndex("metadata.json", "selection.json") - if used, err := parseCoverageHints(root, map[string]string{ - "index.json": index, - "metadata.json": `{"version":1,"source":{"kind":"explicit"}}`, - "selection.json": `{"version":1,"source":{"kind":"explicit"}}`, - "imported/z.json": `{"version":1,"source":{"kind":"imported"}}`, - "imported/a.json": `{"version":1,"source":{"kind":"imported"}}`, - }); err != nil || !used { - t.Fatalf("sorted imported hints = %v, %v", used, err) - } - - trueValue := true - apply := func(out File, incoming HintFile) error { - stats := Stats{referenceReviews: map[string]ReferenceReview{}} - return applyHintSource(&out, parsedHintSource{ - file: sourceFile{display: "hints/selection/sample.json"}, - hint: incoming, - }, &stats, sourceTracker{}) - } - reviewedSource := HintSource{Kind: "explicit", Reviewed: true} - for _, tc := range []struct { - name string - existing ProductMetadata - incoming HintProduct - }{ - {"summary", ProductMetadata{AgentSummary: "a", agentSummaryPresent: true, agentSummaryRank: selectionRankReviewedExplicit, agentSummaryOrigin: "existing"}, HintProduct{AgentSummary: "b", agentSummaryPresent: true}}, - {"use", ProductMetadata{UseWhen: []string{"a"}, useWhenPresent: true, useWhenRank: selectionRankReviewedExplicit, useWhenOrigin: "existing"}, HintProduct{UseWhen: []string{"b"}}}, - {"avoid", ProductMetadata{AvoidWhen: []string{"a"}, avoidWhenPresent: true, avoidWhenRank: selectionRankReviewedExplicit, avoidWhenOrigin: "existing"}, HintProduct{AvoidWhen: []string{"b"}}}, - } { - t.Run("product "+tc.name, func(t *testing.T) { - err := apply(File{Products: map[string]ProductMetadata{"sample": tc.existing}, Tools: map[string]ToolMetadata{}}, HintFile{ - Source: reviewedSource, Products: map[string]HintProduct{"sample": tc.incoming}, - }) - if err == nil { - t.Fatal("same-rank product conflict succeeded") - } - }) - } - - listExisting := func(field string) ToolMetadata { - metadata := ToolMetadata{} - switch field { - case "use_when": - metadata.UseWhen, metadata.useWhenPresent, metadata.useWhenRank, metadata.useWhenOrigin = []string{"a"}, true, selectionRankReviewedExplicit, "existing" - case "avoid_when": - metadata.AvoidWhen, metadata.avoidWhenPresent, metadata.avoidWhenRank, metadata.avoidWhenOrigin = []string{"a"}, true, selectionRankReviewedExplicit, "existing" - case "prerequisites": - metadata.Prerequisites, metadata.prerequisitesPresent, metadata.prerequisitesRank, metadata.prerequisitesOrigin = []string{"a"}, true, selectionRankReviewedExplicit, "existing" - case "tips": - metadata.Tips, metadata.tipsPresent, metadata.tipsRank, metadata.tipsOrigin = []string{"a"}, true, selectionRankReviewedExplicit, "existing" - case "workflow_refs": - metadata.WorkflowRefs, metadata.workflowRefsPresent, metadata.workflowRefsRank, metadata.workflowRefsOrigin = []string{"a"}, true, selectionRankReviewedExplicit, "existing" - case "examples": - metadata.Examples, metadata.examplesPresent, metadata.examplesRank, metadata.examplesOrigin = []string{"a"}, true, selectionRankReviewedExplicit, "existing" - } - return metadata - } - for _, field := range []string{"use_when", "avoid_when", "prerequisites", "tips", "workflow_refs", "examples"} { - t.Run("tool "+field, func(t *testing.T) { - incoming := HintTool{Reviewed: &trueValue} - switch field { - case "use_when": - incoming.UseWhen = []string{"b"} - case "avoid_when": - incoming.AvoidWhen = []string{"b"} - case "prerequisites": - incoming.Prerequisites = []string{"b"} - case "tips": - incoming.Tips = []string{"b"} - case "workflow_refs": - incoming.WorkflowRefs = []string{"b"} - case "examples": - incoming.Examples = []string{"b"} - } - err := apply(File{Products: map[string]ProductMetadata{}, Tools: map[string]ToolMetadata{"sample get": listExisting(field)}}, HintFile{ - Source: reviewedSource, Tools: map[string]HintTool{"sample get": incoming}, - }) - if err == nil { - t.Fatal("same-rank list conflict succeeded") - } - }) - } - - for _, tc := range []struct { - name string - existing ToolMetadata - incoming HintTool - }{ - {"effect", ToolMetadata{Effect: "read", effectPresent: true, effectRank: selectionRankReviewedExplicit, effectOrigin: "existing"}, HintTool{Effect: "write", effectPresent: true, Reviewed: &trueValue}}, - {"risk", ToolMetadata{Risk: "low", riskPresent: true, riskRank: selectionRankReviewedExplicit, riskOrigin: "existing"}, HintTool{Risk: "high", riskPresent: true, Reviewed: &trueValue}}, - {"confirmation", ToolMetadata{Confirmation: "not_required", confirmationPresent: true, confirmationRank: selectionRankReviewedExplicit, confirmationOrigin: "existing"}, HintTool{Confirmation: "user_required", confirmationPresent: true, Reviewed: &trueValue}}, - {"idempotency", ToolMetadata{Idempotency: "idempotent", idempotencyPresent: true, idempotencyRank: selectionRankReviewedExplicit, idempotencyOrigin: "existing"}, HintTool{Idempotency: "unknown", idempotencyPresent: true, Reviewed: &trueValue}}, - {"interface ref", ToolMetadata{InterfaceRef: &InterfaceRef{ProductID: "sample", RPCName: "one"}, interfaceRefPresent: true, interfaceRefRank: selectionRankReviewedExplicit, interfaceRefOrigin: "existing"}, HintTool{InterfaceRef: &InterfaceRef{ProductID: "sample", RPCName: "two"}, interfaceRefPresent: true, Reviewed: &trueValue}}, - {"interface mode", ToolMetadata{InterfaceMode: "mcp", interfaceModePresent: true, interfaceModeRank: selectionRankReviewedExplicit, interfaceModeOrigin: "existing"}, HintTool{InterfaceMode: "local", interfaceModePresent: true, Reviewed: &trueValue}}, - } { - t.Run("tool "+tc.name, func(t *testing.T) { - err := apply(File{Products: map[string]ProductMetadata{}, Tools: map[string]ToolMetadata{"sample get": tc.existing}}, HintFile{ - Source: reviewedSource, Tools: map[string]HintTool{"sample get": tc.incoming}, - }) - if err == nil { - t.Fatal("same-rank scalar conflict succeeded") - } - }) - } - - base := File{Products: map[string]ProductMetadata{}, Tools: map[string]ToolMetadata{}} - stats := Stats{referenceReviews: map[string]ReferenceReview{}} - if err := applyHintSource(&base, parsedHintSource{file: sourceFile{display: "fixture"}, hint: HintFile{ - Source: reviewedSource, - ReferenceReview: map[string]ReferenceReview{" ": {Status: "stale"}}, - Products: map[string]HintProduct{" ": {}}, - Tools: map[string]HintTool{" ": {AgentSummary: "ignored"}, "empty": {}, "sample get": {Risk: "high", riskPresent: true, Reviewed: &trueValue}}, - }}, &stats, sourceTracker{}); err != nil { - t.Fatal(err) - } - if stats.RiskRules != 1 { - t.Fatalf("risk rules = %d", stats.RiskRules) - } -} diff --git a/internal/generator/agentmetadata/interface_disposition_test.go b/internal/generator/agentmetadata/interface_disposition_test.go deleted file mode 100644 index 11d1ef53..00000000 --- a/internal/generator/agentmetadata/interface_disposition_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package agentmetadata - -import ( - "strings" - "testing" -) - -func TestValidateInterfaceDispositionsMatrix(t *testing.T) { - ref := &InterfaceRef{ProductID: "calendar", RPCName: "get_event"} - for _, test := range []struct { - name string - tool ToolMetadata - want string - }{ - {name: "mcp available", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "available", InterfaceRef: ref}}, - {name: "local available", tool: ToolMetadata{InterfaceMode: "local", Availability: "available"}}, - {name: "composite available", tool: ToolMetadata{InterfaceMode: "composite", Availability: "available", InterfaceReason: "orchestrates operations"}}, - {name: "mcp unavailable", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "unavailable", InterfaceReason: "RPC retired"}}, - {name: "local unavailable", tool: ToolMetadata{InterfaceMode: "local", Availability: "unavailable", InterfaceReason: "compatibility command frozen"}}, - {name: "composite unavailable", tool: ToolMetadata{InterfaceMode: "composite", Availability: "unavailable", InterfaceReason: "workflow retired"}}, - {name: "unavailable is not mode", tool: ToolMetadata{InterfaceMode: "unavailable", Availability: "unavailable", InterfaceReason: "retired"}, want: "legacy interface_mode=unavailable; migrate"}, - {name: "mcp available without ref", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "available"}, want: "available mcp without interface_ref"}, - {name: "local available with ref", tool: ToolMetadata{InterfaceMode: "local", Availability: "available", InterfaceRef: ref}, want: "available local but declares interface_ref"}, - {name: "composite available with ref", tool: ToolMetadata{InterfaceMode: "composite", Availability: "available", InterfaceReason: "orchestrates operations", InterfaceRef: ref}, want: "available composite but declares a single interface_ref"}, - {name: "composite available without reason", tool: ToolMetadata{InterfaceMode: "composite", Availability: "available"}, want: "available composite without interface_reason"}, - {name: "unavailable with ref", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "unavailable", InterfaceReason: "retired", InterfaceRef: ref}, want: "unavailable but declares interface_ref"}, - {name: "unavailable without reason", tool: ToolMetadata{InterfaceMode: "local", Availability: "unavailable"}, want: "unavailable without interface_reason"}, - {name: "missing availability", tool: ToolMetadata{InterfaceMode: "local"}, want: "unsupported availability"}, - } { - t.Run(test.name, func(t *testing.T) { - err := validateInterfaceDispositions(File{Tools: map[string]ToolMetadata{"calendar event get": test.tool}}) - if test.want == "" { - if err != nil { - t.Fatalf("validateInterfaceDispositions() error = %v", err) - } - return - } - if err == nil || !strings.Contains(err.Error(), test.want) { - t.Fatalf("validateInterfaceDispositions() error = %v, want %q", err, test.want) - } - }) - } -} - -func TestFinalizeUnavailableDispositionClearsRefAndRecordsNoneWinner(t *testing.T) { - file := File{Tools: map[string]ToolMetadata{ - "conference camera open": { - InterfaceMode: "mcp", - interfaceModeRank: selectionRankReviewedExplicit, - interfaceModeOrigin: "reviewed.json", - Availability: "unavailable", - availabilityRank: selectionRankReviewedExplicit, - availabilityOrigin: "reviewed.json", - InterfaceReason: "compatibility command frozen", - InterfaceRef: &InterfaceRef{ProductID: "conference", RPCName: "open_camera"}, - interfaceRefRank: selectionRankImported, - interfaceRefOrigin: "pinned-interface.json", - FieldProvenance: map[string]FieldProvenance{ - "interface_ref": { - Value: "conference.open_camera", - Source: "pinned-interface.json", - Precedence: "imported", - Resolution: "highest_precedence", - Candidates: []FieldCandidateProvenance{{ - Value: "conference.open_camera", - Source: "pinned-interface.json", - Precedence: "imported", - Selected: true, - }}, - }, - }, - }, - }} - - finalizeInterfaceDispositions(&file) - tool := file.Tools["conference camera open"] - if tool.InterfaceRef != nil { - t.Fatalf("InterfaceRef = %#v, want nil", tool.InterfaceRef) - } - provenance, ok := tool.FieldProvenance["interface_ref"] - if !ok || provenance.Value != nil || provenance.Resolution != "interface_disposition_matrix" { - t.Fatalf("interface_ref provenance = %#v", provenance) - } - if len(provenance.Candidates) != 1 || !provenance.Candidates[0].Selected || provenance.Candidates[0].Value != nil { - t.Fatalf("interface_ref candidates = %#v", provenance.Candidates) - } - if !strings.Contains(provenance.ReviewReason, "forbids Agent invocation") { - t.Fatalf("interface_ref review reason = %q", provenance.ReviewReason) - } - if len(provenance.OverriddenCandidates) != 1 || provenance.OverriddenCandidates[0].Value != "conference.open_camera" { - t.Fatalf("overridden interface_ref candidates = %#v", provenance.OverriddenCandidates) - } -} - -func TestGenerateRejectsLegacyUnavailableModeWithMigrationError(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar event get") - writeFixture(t, root, "internal/cli/schema_hints/legacy.json", hintFixture("explicit", "legacy-interface", "calendar event get", `{ - "interface_mode": "unavailable", - "availability": "unavailable", - "interface_reason": "legacy fixture" - }`)) - - _, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar event get": "calendar event get", - })) - if err == nil || !strings.Contains(err.Error(), "legacy interface_mode=unavailable; migrate") || !strings.Contains(err.Error(), "availability=unavailable") { - t.Fatalf("Generate() error = %v, want explicit interface mode migration guidance", err) - } -} diff --git a/internal/generator/agentmetadata/interface_metadata_test.go b/internal/generator/agentmetadata/interface_metadata_test.go deleted file mode 100644 index 59de3990..00000000 --- a/internal/generator/agentmetadata/interface_metadata_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 - -package agentmetadata - -import "testing" - -func TestGenerateUsesMCPDescriptionsAsUnreviewedFallback(t *testing.T) { - root := t.TempDir() - writeFixture(t, root, "skills/mono/SKILL.md", "# DWS\n## 意图判断决策树\n用户提到\"日程\" -> `calendar`\n") - writeFixture(t, root, "skills/mono/references/intent-guide.md", "# Intent guide\n") - writeFixture(t, root, "skills/mono/references/products/calendar.md", "# Calendar\n") - writeFixture(t, root, "internal/cli/schema_hints/imported/wukong.json", `{ - "version": 1, - "source": {"kind": "imported", "name": "dws-wukong", "revision": "1234567890abcdef"}, - "tools": { - "calendar.get_calendar": {"agent_summary": "读取指定日历"} - } -}`) - writeFixture(t, root, "internal/cli/schema_mcp_metadata.json", `{ - "version": 1, - "source": "mcp-tools-list+cli-registry", - "source_revision": "abcdef1234567890", - "source_hash": "sha256:interface", - "tools": { - "calendar.get_calendar": {"description": "MCP 原始读取描述。"}, - "calendar.list_calendars": { - "description": "列出当前用户可访问的日历。后续句子不应进入 summary。", - "interface_ref": {"product_id": "calendar", "rpc_name": "list_calendars"} - }, - "calendar.raw_tool": {"description": "raw_tool_name"}, - "outside.tool": {"description": "不在公开命令面"} - } -}`) - - metadata, stats, err := generateFromSources(Options{ - Root: root, - SkillPath: "skills/mono/SKILL.md", - ProductsDir: "skills/mono/references/products", - IntentGuidePath: "skills/mono/references/intent-guide.md", - HintsDir: "internal/cli/schema_hints", - InterfaceMetadataPath: "internal/cli/schema_mcp_metadata.json", - ToolPaths: map[string]string{ - "calendar.get_calendar": "calendar book get", - "calendar.list_calendars": "calendar book list", - "calendar.raw_tool": "calendar raw", - }, - ProductIDs: map[string]bool{"calendar": true}, - SurfaceToolCount: 3, - }) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - get := metadata.Tools["calendar book get"] - if get.AgentSummary != "读取指定日历" || get.AgentSummarySource != "dws-wukong@1234567890ab" { - t.Fatalf("reviewed/imported summary was not preserved: %#v", get) - } - list := metadata.Tools["calendar book list"] - if list.AgentSummary != "列出当前用户可访问的日历。" { - t.Fatalf("MCP summary = %q", list.AgentSummary) - } - if list.AgentSummarySource != "mcp-tools-list+cli-registry@abcdef123456" { - t.Fatalf("MCP summary source = %q", list.AgentSummarySource) - } - if list.InterfaceRef == nil || list.InterfaceRef.ProductID != "calendar" || list.InterfaceRef.RPCName != "list_calendars" { - t.Fatalf("MCP interface ref = %#v", list.InterfaceRef) - } - refProvenance := list.FieldProvenance["interface_ref"] - if refProvenance.Value != "calendar.list_calendars" || refProvenance.Precedence != "mcp_fallback" || refProvenance.Source == "" { - t.Fatalf("MCP interface ref provenance = %#v", refProvenance) - } - if list.Reviewed == nil || *list.Reviewed { - t.Fatalf("MCP fallback reviewed = %#v, want false", list.Reviewed) - } - raw, exists := metadata.Tools["calendar raw"] - if !exists { - t.Fatal("effective command without an eligible MCP summary must retain an Agent metadata projection") - } - if raw.AgentSummary != "" { - t.Fatalf("identifier-only MCP description became an Agent summary: %#v", raw) - } - audit := stats.InterfaceMetadata - if audit == nil || audit.SourceTools != 4 || audit.SurfaceTools != 3 || - audit.EligibleSummaries != 2 || audit.AppliedSummaries != 1 || - audit.PreservedSummaries != 1 { - t.Fatalf("interface audit = %#v", audit) - } - if len(audit.RejectedTools) != 1 || audit.RejectedTools[0] != "calendar.raw_tool" || - len(audit.OutsideSurface) != 1 || audit.OutsideSurface[0] != "outside.tool" { - t.Fatalf("interface audit paths = %#v", audit) - } -} - -func TestSummarizeInterfaceDescriptionBoundsAndRejectsIdentifiers(t *testing.T) { - if got := summarizeInterfaceDescription("search_open_platform_docs", 40); got != "" { - t.Fatalf("identifier summary = %q, want empty", got) - } - if got := summarizeInterfaceDescription("**查询日历。** 第二句不保留。", 40); got != "查询日历。" { - t.Fatalf("sentence summary = %q", got) - } - got := summarizeInterfaceDescription("这是一个没有句号而且明显超过允许长度的接口描述文本", 12) - if got != "这是一个没有句号而..." { - t.Fatalf("truncated summary = %q", got) - } -} diff --git a/internal/generator/agentmetadata/list_precedence_test.go b/internal/generator/agentmetadata/list_precedence_test.go deleted file mode 100644 index 744432ff..00000000 --- a/internal/generator/agentmetadata/list_precedence_test.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package agentmetadata - -import ( - "encoding/json" - "reflect" - "strings" - "testing" -) - -func TestSelectionListsUseWholeFieldPrecedenceOrderIndependently(t *testing.T) { - for _, field := range []string{"use_when", "avoid_when", "prerequisites", "tips", "workflow_refs", "examples"} { - t.Run(field, func(t *testing.T) { - low := toolMetadataWithList(field, []string{"low second", "low first"}, selectionRankImported, "imported.json") - high := toolMetadataWithList(field, []string{"high second", "high first"}, selectionRankReviewedExplicit, "reviewed.json") - - forward, err := mergeToolMetadata(low, high, "calendar event get") - if err != nil { - t.Fatalf("mergeToolMetadata(low, high) error = %v", err) - } - reverse, err := mergeToolMetadata(high, low, "calendar event get") - if err != nil { - t.Fatalf("mergeToolMetadata(high, low) error = %v", err) - } - want := []string{"high second", "high first"} - if got := toolMetadataList(forward, field); !reflect.DeepEqual(got, want) { - t.Fatalf("forward %s = %#v, want authored winner %#v", field, got, want) - } - if got := toolMetadataList(reverse, field); !reflect.DeepEqual(got, want) { - t.Fatalf("reverse %s = %#v, want authored winner %#v", field, got, want) - } - - provenance, ok := forward.FieldProvenance[field] - if !ok || provenance.Source != "reviewed.json" || provenance.Precedence != "reviewed_explicit" || provenance.Resolution != "highest_precedence" { - t.Fatalf("%s provenance = %#v", field, provenance) - } - if got, ok := provenance.Value.([]string); !ok || !reflect.DeepEqual(got, want) { - t.Fatalf("%s provenance winner = %#v", field, provenance.Value) - } - if !hasListCandidate(provenance.Candidates, want, "reviewed.json", true) || - !hasListCandidate(provenance.Candidates, []string{"low second", "low first"}, "imported.json", false) { - t.Fatalf("%s provenance candidates = %#v", field, provenance.Candidates) - } - }) - } -} - -func TestSelectionListsRejectSameRankContentOrOrderConflict(t *testing.T) { - for _, field := range []string{"use_when", "avoid_when", "prerequisites", "tips", "workflow_refs", "examples"} { - for _, test := range []struct { - name string - left []string - right []string - }{ - {name: "content", left: []string{"first"}, right: []string{"second"}}, - {name: "order", left: []string{"first", "second"}, right: []string{"second", "first"}}, - } { - t.Run(field+"/"+test.name, func(t *testing.T) { - left := toolMetadataWithList(field, test.left, selectionRankExplicit, "a.json") - right := toolMetadataWithList(field, test.right, selectionRankExplicit, "b.json") - for _, pair := range [][2]ToolMetadata{{left, right}, {right, left}} { - _, err := mergeToolMetadata(pair[0], pair[1], "calendar event get") - if err == nil || !strings.Contains(err.Error(), "field "+field) || !strings.Contains(err.Error(), "a.json") || !strings.Contains(err.Error(), "b.json") { - t.Fatalf("merge error = %v, want order-independent same-rank %s conflict", err, field) - } - } - }) - } - } -} - -func TestSelectionListsAllowIdenticalSameRankArraysAndAuditRefsStillUnion(t *testing.T) { - left := toolMetadataWithList("use_when", []string{"second", "first"}, selectionRankExplicit, "b.json") - left.SourceRefs = []string{"b.json"} - right := toolMetadataWithList("use_when", []string{"second", "first"}, selectionRankExplicit, "a.json") - right.SourceRefs = []string{"a.json", "b.json"} - - merged, err := mergeToolMetadata(left, right, "calendar event get") - if err != nil { - t.Fatalf("mergeToolMetadata() error = %v", err) - } - if got, want := merged.UseWhen, []string{"second", "first"}; !reflect.DeepEqual(got, want) { - t.Fatalf("use_when = %#v, want authored order %#v", got, want) - } - if got, want := merged.SourceRefs, []string{"a.json", "b.json"}; !reflect.DeepEqual(got, want) { - t.Fatalf("source_refs = %#v, want audit union %#v", got, want) - } -} - -func TestSelectionListsTreatExplicitEmptyAsAWinningValue(t *testing.T) { - for _, field := range []string{"use_when", "avoid_when", "prerequisites", "tips", "workflow_refs", "examples"} { - t.Run(field, func(t *testing.T) { - low := toolMetadataWithList(field, []string{"lower value"}, selectionRankImported, "imported.json") - high := toolMetadataWithList(field, []string{}, selectionRankReviewedExplicit, "reviewed.json") - - for direction, pair := range map[string][2]ToolMetadata{ - "forward": {low, high}, - "reverse": {high, low}, - } { - merged, err := mergeToolMetadata(pair[0], pair[1], "calendar event get") - if err != nil { - t.Fatalf("%s merge error = %v", direction, err) - } - got := toolMetadataList(merged, field) - if got == nil || len(got) != 0 { - t.Fatalf("%s %s = %#v, want selected explicit []", direction, field, got) - } - provenance := merged.FieldProvenance[field] - winner, ok := provenance.Value.([]string) - if !ok || winner == nil || len(winner) != 0 || provenance.Source != "reviewed.json" { - t.Fatalf("%s %s provenance = %#v, want reviewed [] winner", direction, field, provenance) - } - if !hasListCandidate(provenance.Candidates, []string{}, "reviewed.json", true) || - !hasListCandidate(provenance.Candidates, []string{"lower value"}, "imported.json", false) { - t.Fatalf("%s %s candidates = %#v", direction, field, provenance.Candidates) - } - encoded, err := json.Marshal(merged) - if err != nil { - t.Fatalf("json.Marshal() error = %v", err) - } - var object map[string]json.RawMessage - if err := json.Unmarshal(encoded, &object); err != nil { - t.Fatalf("json.Unmarshal() error = %v", err) - } - if string(object[field]) != "[]" { - t.Fatalf("%s JSON field = %s, full JSON = %s", field, object[field], encoded) - } - var wire struct { - FieldProvenance map[string]struct { - Value []string `json:"value"` - Candidates []struct { - Value []string `json:"value"` - Source string `json:"source"` - Selected bool `json:"selected"` - } `json:"candidates"` - } `json:"field_provenance"` - } - if err := json.Unmarshal(encoded, &wire); err != nil { - t.Fatalf("decode provenance JSON error = %v", err) - } - wireProvenance := wire.FieldProvenance[field] - if wireProvenance.Value == nil || len(wireProvenance.Value) != 0 { - t.Fatalf("%s wire provenance value = %#v, want JSON []", field, wireProvenance.Value) - } - selectedEmpty := false - for _, candidate := range wireProvenance.Candidates { - if candidate.Source == "reviewed.json" && candidate.Selected && candidate.Value != nil && len(candidate.Value) == 0 { - selectedEmpty = true - } - } - if !selectedEmpty { - t.Fatalf("%s wire candidates = %#v, want selected reviewed JSON []", field, wireProvenance.Candidates) - } - } - }) - } -} - -func TestSelectionListsRejectSameRankEmptyVersusNonEmpty(t *testing.T) { - for _, field := range []string{"use_when", "avoid_when", "prerequisites", "tips", "workflow_refs", "examples"} { - t.Run(field, func(t *testing.T) { - empty := toolMetadataWithList(field, []string{}, selectionRankExplicit, "empty.json") - nonempty := toolMetadataWithList(field, []string{"value"}, selectionRankExplicit, "nonempty.json") - for _, pair := range [][2]ToolMetadata{{empty, nonempty}, {nonempty, empty}} { - _, err := mergeToolMetadata(pair[0], pair[1], "calendar event get") - if err == nil || !strings.Contains(err.Error(), "field "+field) || !strings.Contains(err.Error(), "empty.json") || !strings.Contains(err.Error(), "nonempty.json") { - t.Fatalf("merge error = %v, want same-rank [] versus non-empty conflict", err) - } - } - }) - } -} - -func TestSelectionListsIgnoreOmittedHigherRankValue(t *testing.T) { - for _, field := range []string{"use_when", "avoid_when", "prerequisites", "tips", "workflow_refs", "examples"} { - t.Run(field, func(t *testing.T) { - low := toolMetadataWithList(field, []string{"lower value"}, selectionRankImported, "imported.json") - omitted := toolMetadataWithList(field, nil, selectionRankReviewedExplicit, "reviewed.json") - setToolMetadataListPresence(&omitted, field, false) - - for _, pair := range [][2]ToolMetadata{{low, omitted}, {omitted, low}} { - merged, err := mergeToolMetadata(pair[0], pair[1], "calendar event get") - if err != nil { - t.Fatalf("merge error = %v", err) - } - if got, want := toolMetadataList(merged, field), []string{"lower value"}; !reflect.DeepEqual(got, want) { - t.Fatalf("%s = %#v, want lower authored value %#v", field, got, want) - } - provenance := merged.FieldProvenance[field] - if provenance.Source != "imported.json" || hasListCandidate(provenance.Candidates, []string{}, "reviewed.json", false) { - t.Fatalf("%s provenance = %#v, omitted source must not participate", field, provenance) - } - } - }) - } -} - -func TestHintExplicitEmptyOverridesImportedAndOmittedDoesNotParticipate(t *testing.T) { - reviewed := true - out := File{Products: map[string]ProductMetadata{}, Tools: map[string]ToolMetadata{}} - stats := Stats{} - origins := sourceTracker{} - imported := parsedHintSource{ - file: sourceFile{display: "imported.json"}, - hint: HintFile{ - Source: HintSource{Kind: "imported", Name: "imported"}, - Tools: map[string]HintTool{ - "calendar event get": {UseWhen: []string{"lower value"}}, - }, - }, - } - explicit := parsedHintSource{ - file: sourceFile{display: "reviewed.json"}, - hint: HintFile{ - Source: HintSource{Kind: "explicit", Name: "reviewed"}, - Tools: map[string]HintTool{ - "calendar event get": {UseWhen: []string{}, Reviewed: &reviewed}, - "calendar event list": {Reviewed: &reviewed}, - }, - }, - } - if err := applyHintSource(&out, imported, &stats, origins); err != nil { - t.Fatalf("apply imported hint error = %v", err) - } - if err := applyHintSource(&out, explicit, &stats, origins); err != nil { - t.Fatalf("apply explicit hint error = %v", err) - } - normalizeFile(&out, 0) - - got := out.Tools["calendar event get"] - if got.UseWhen == nil || len(got.UseWhen) != 0 || !got.useWhenPresent { - t.Fatalf("explicit use_when = %#v, present=%v; want [] selected", got.UseWhen, got.useWhenPresent) - } - if _, exists := out.Tools["calendar event list"].FieldProvenance["use_when"]; exists { - t.Fatalf("omitted use_when unexpectedly participated: %#v", out.Tools["calendar event list"].FieldProvenance) - } -} - -func TestHintJSONDistinguishesOmittedAndExplicitEmptyList(t *testing.T) { - var omitted, explicitEmpty HintTool - if err := json.Unmarshal([]byte(`{}`), &omitted); err != nil { - t.Fatalf("decode omitted hint error = %v", err) - } - if err := json.Unmarshal([]byte(`{"use_when":[]}`), &explicitEmpty); err != nil { - t.Fatalf("decode explicit-empty hint error = %v", err) - } - if omitted.UseWhen != nil { - t.Fatalf("omitted use_when = %#v, want nil", omitted.UseWhen) - } - if explicitEmpty.UseWhen == nil || len(explicitEmpty.UseWhen) != 0 { - t.Fatalf("explicit use_when = %#v, want non-nil []", explicitEmpty.UseWhen) - } - if hasAgentHintFields(omitted) { - t.Fatal("omitted list unexpectedly counts as an authored hint") - } - if !hasAgentHintFields(explicitEmpty) { - t.Fatal("explicit [] must count as an authored hint") - } -} - -func TestProductSelectionListExplicitEmptyIsSerializedAndProvenanced(t *testing.T) { - metadata := ProductMetadata{ - UseWhen: []string{}, - useWhenPresent: true, - useWhenRank: selectionRankReviewedExplicit, - useWhenOrigin: "reviewed.json", - } - recordProductListCandidate(&metadata, "use_when", metadata.UseWhen, true, metadata.useWhenRank, metadata.useWhenOrigin) - file := File{Products: map[string]ProductMetadata{"calendar": metadata}, Tools: map[string]ToolMetadata{}} - normalizeFile(&file, 0) - product := file.Products["calendar"] - provenance := product.FieldProvenance["use_when"] - if winner, ok := provenance.Value.([]string); !ok || winner == nil || len(winner) != 0 || !hasListCandidate(provenance.Candidates, []string{}, "reviewed.json", true) { - t.Fatalf("product provenance = %#v", provenance) - } - encoded, err := json.Marshal(product) - if err != nil { - t.Fatalf("json.Marshal() error = %v", err) - } - if !strings.Contains(string(encoded), `"use_when":[]`) { - t.Fatalf("product JSON = %s, want explicit use_when:[]", encoded) - } -} - -func toolMetadataWithList(field string, values []string, rank int, origin string) ToolMetadata { - metadata := ToolMetadata{} - values = cloneStringList(values) - switch field { - case "use_when": - metadata.UseWhen, metadata.useWhenPresent, metadata.useWhenRank, metadata.useWhenOrigin = values, values != nil, rank, origin - case "avoid_when": - metadata.AvoidWhen, metadata.avoidWhenPresent, metadata.avoidWhenRank, metadata.avoidWhenOrigin = values, values != nil, rank, origin - case "prerequisites": - metadata.Prerequisites, metadata.prerequisitesPresent, metadata.prerequisitesRank, metadata.prerequisitesOrigin = values, values != nil, rank, origin - case "tips": - metadata.Tips, metadata.tipsPresent, metadata.tipsRank, metadata.tipsOrigin = values, values != nil, rank, origin - case "workflow_refs": - metadata.WorkflowRefs, metadata.workflowRefsPresent, metadata.workflowRefsRank, metadata.workflowRefsOrigin = values, values != nil, rank, origin - case "examples": - metadata.Examples, metadata.examplesPresent, metadata.examplesRank, metadata.examplesOrigin = values, values != nil, rank, origin - } - return metadata -} - -func setToolMetadataListPresence(metadata *ToolMetadata, field string, present bool) { - switch field { - case "use_when": - metadata.useWhenPresent = present - case "avoid_when": - metadata.avoidWhenPresent = present - case "prerequisites": - metadata.prerequisitesPresent = present - case "tips": - metadata.tipsPresent = present - case "workflow_refs": - metadata.workflowRefsPresent = present - case "examples": - metadata.examplesPresent = present - } -} - -func toolMetadataList(metadata ToolMetadata, field string) []string { - switch field { - case "use_when": - return metadata.UseWhen - case "avoid_when": - return metadata.AvoidWhen - case "prerequisites": - return metadata.Prerequisites - case "tips": - return metadata.Tips - case "workflow_refs": - return metadata.WorkflowRefs - case "examples": - return metadata.Examples - default: - return nil - } -} - -func hasListCandidate(candidates []FieldCandidateProvenance, values []string, source string, selected bool) bool { - for _, candidate := range candidates { - actual, ok := candidate.Value.([]string) - if ok && reflect.DeepEqual(actual, values) && candidate.Source == source && candidate.Selected == selected { - return true - } - } - return false -} diff --git a/internal/generator/agentmetadata/manual_hints.go b/internal/generator/agentmetadata/manual_hints.go index bad680d3..eb0391e2 100644 --- a/internal/generator/agentmetadata/manual_hints.go +++ b/internal/generator/agentmetadata/manual_hints.go @@ -9,8 +9,6 @@ package agentmetadata import ( "fmt" - "os" - "path/filepath" "sort" "strings" @@ -244,12 +242,7 @@ func expectedCanonicalToolSet(opts Options) map[string]bool { } func validateSelectionAuthoringContracts(opts Options) error { - selectionRoot := filepath.Join(resolvePath(opts.Root, opts.HintsDir), "selection") expectedTools := expectedCanonicalToolSet(opts) - // Declared tools carry their selection fields in the Contract final - // overlay (corecmd.SchemaDecl); they are exempt from hint-file coverage. - // Hint rows for them may still exist during migration, but are no longer - // required and never win over the declaration. for canonical := range expectedTools { bound, ok := opts.BoundCommands.ByCanonical[canonical] if ok && cli.HasRuntimeContractFinal(bound.PrimaryCommand) { @@ -257,33 +250,26 @@ func validateSelectionAuthoringContracts(opts Options) error { } } expectedProducts := selectionHintCoverageProducts(opts.ProductIDs) - info, err := os.Stat(selectionRoot) - missing := err != nil || !info.IsDir() - if missing { - if selectionHintCoverageRequired(expectedProducts, expectedTools) { - return fmt.Errorf("required Agent hint directory missing: %s", selectionRoot) - } - return nil - } - hints, err := cli.LoadAgentHintsFromSelectionForValidation(os.DirFS(selectionRoot)) - if err != nil { - return fmt.Errorf("load selection Agent hints: %w", err) - } - if err := cli.ValidateManualAgentHintSet(hints, expectedProducts, expectedTools); err != nil { - return fmt.Errorf("validate selection Agent hints: %w", err) - } - if opts.MaxExamples > 0 { - for canonical, hint := range hints.Tools { - if len(hint.Examples) > opts.MaxExamples { - return fmt.Errorf("selection Agent hint %s has %d reviewed examples, exceeding max-examples=%d", canonical, len(hint.Examples), opts.MaxExamples) + if selectionHintCoverageRequired(expectedProducts, expectedTools) { + missingProducts := make([]string, 0) + for productID, include := range expectedProducts { + if include { + missingProducts = append(missingProducts, productID) } } + missingTools := make([]string, 0) + for tool, include := range expectedTools { + if include { + missingTools = append(missingTools, tool) + } + } + sort.Strings(missingProducts) + sort.Strings(missingTools) + return fmt.Errorf("ProductDecl/ContractFinal selection coverage incomplete: missing_products=%v missing_tools=%v", missingProducts, missingTools) } - if err := cli.ValidateManualAgentHintExamples(opts.BoundCommands, hints); err != nil { - return fmt.Errorf("validate selection Agent hint examples: %w", err) + if len(opts.BoundCommands.Commands) == 0 { + return nil } - if _, err := cli.ValidateManualAgentSelectionContract(opts.BoundCommands, hints); err != nil { - return fmt.Errorf("validate selection Agent selection contract: %w", err) - } - return nil + _, err := cli.ValidateAgentSelectionContract(opts.BoundCommands) + return err } diff --git a/internal/generator/agentmetadata/manual_hints_coverage_test.go b/internal/generator/agentmetadata/manual_hints_coverage_test.go deleted file mode 100644 index 6cd63483..00000000 --- a/internal/generator/agentmetadata/manual_hints_coverage_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package agentmetadata - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" -) - -func TestCrossPlatformCoverageReviewedSelectionRetentionAndDeliveryFailures(t *testing.T) { - retainReviewedSelectionCandidates(nil) - file := File{ - Products: map[string]ProductMetadata{"sample": {}}, - Tools: map[string]ToolMetadata{"sample tool": {}}, - } - retainReviewedSelectionCandidates(&file) - if file.Products["sample"].fieldCandidates == nil || file.Tools["sample tool"].fieldCandidates == nil { - t.Fatal("candidate maps were not initialized") - } - - opts := Options{ProductIDs: map[string]bool{"sample": true}, CanonicalToolPaths: map[string]string{"sample.tool": "sample tool"}} - if err := validateReviewedSelectionDelivery(File{Products: map[string]ProductMetadata{}, Tools: map[string]ToolMetadata{}}, opts); err == nil || !strings.Contains(err.Error(), "missing product") || !strings.Contains(err.Error(), "missing tool") { - t.Fatalf("missing delivery error = %v", err) - } - - selected := []FieldCandidateProvenance{{Precedence: selectionPrecedenceReviewedExplicit, Selected: true}} - wrongProvenance := FieldProvenance{Precedence: selectionPrecedenceReviewedExplicit, Source: "metadata/sample.json", Candidates: selected} - falseValue := false - trueValue := true - file = File{ - Products: map[string]ProductMetadata{"sample": { - FieldProvenance: map[string]FieldProvenance{ - "agent_summary": wrongProvenance, - "use_when": wrongProvenance, - "avoid_when": wrongProvenance, - }, - }}, - Tools: map[string]ToolMetadata{"sample tool": { - Reviewed: &falseValue, - FieldProvenance: map[string]FieldProvenance{ - "agent_summary": wrongProvenance, - "use_when": wrongProvenance, - "avoid_when": wrongProvenance, - "examples": wrongProvenance, - }, - }}, - } - err := validateReviewedSelectionDelivery(file, opts) - if err == nil || !strings.Contains(err.Error(), "source is not selection") || !strings.Contains(err.Error(), "is not reviewed_explicit/contract_final") { - t.Fatalf("invalid delivery error = %v", err) - } - - file = File{ - Products: map[string]ProductMetadata{"sample": { - AgentSummary: "summary", agentSummaryRank: selectionRankReviewedExplicit, - UseWhen: []string{"use"}, useWhenRank: selectionRankReviewedExplicit, - AvoidWhen: []string{"avoid"}, avoidWhenRank: selectionRankReviewedExplicit, - FieldProvenance: map[string]FieldProvenance{}, - }}, - Tools: map[string]ToolMetadata{"sample tool": { - AgentSummary: "summary", agentSummaryRank: selectionRankReviewedExplicit, - UseWhen: []string{"use"}, useWhenRank: selectionRankReviewedExplicit, - AvoidWhen: []string{"avoid"}, avoidWhenRank: selectionRankReviewedExplicit, - Examples: []string{"dws sample tool"}, examplesRank: selectionRankReviewedExplicit, - Reviewed: &trueValue, reviewedRank: selectionRankReviewedExplicit, - FieldProvenance: map[string]FieldProvenance{}, - }}, - } - if err := validateReviewedSelectionDelivery(file, opts); err == nil || !strings.Contains(err.Error(), "provenance is not one") { - t.Fatalf("missing provenance error = %v", err) - } -} - -func TestValidateReviewedSelectionDeliveryAcceptsProductContractFinal(t *testing.T) { - t.Cleanup(func() { cli.ClearProductDeclForTest("sample") }) - cli.RegisterProductDecl(cli.ProductDecl{ - ID: "sample", - Selection: cli.ProductSelectionDecl{ - AgentSummary: "Manage samples", - UseWhen: []string{"target is a sample"}, - AvoidWhen: []string{"target is another product"}, - }, - }) - - selected := []FieldCandidateProvenance{{Precedence: selectionPrecedenceContractFinal, Selected: true, Source: "cli.product_decl"}} - contractFinalProvenance := FieldProvenance{ - Precedence: selectionPrecedenceContractFinal, - Source: "cli.product_decl", - Candidates: selected, - } - trueValue := true - file := File{ - Products: map[string]ProductMetadata{"sample": { - AgentSummary: "Manage samples", agentSummaryRank: selectionRankContractFinal, - UseWhen: []string{"target is a sample"}, useWhenRank: selectionRankContractFinal, - AvoidWhen: []string{"target is another product"}, avoidWhenRank: selectionRankContractFinal, - FieldProvenance: map[string]FieldProvenance{ - "agent_summary": contractFinalProvenance, - "use_when": contractFinalProvenance, - "avoid_when": contractFinalProvenance, - }, - }}, - Tools: map[string]ToolMetadata{"sample tool": { - AgentSummary: "summary", agentSummaryRank: selectionRankContractFinal, - UseWhen: []string{"use"}, useWhenRank: selectionRankContractFinal, - AvoidWhen: []string{"avoid"}, avoidWhenRank: selectionRankContractFinal, - Examples: []string{"dws sample tool"}, examplesRank: selectionRankContractFinal, - Reviewed: &trueValue, reviewedRank: selectionRankContractFinal, - FieldProvenance: map[string]FieldProvenance{ - "agent_summary": contractFinalProvenance, - "use_when": contractFinalProvenance, - "avoid_when": contractFinalProvenance, - "examples": contractFinalProvenance, - }, - }}, - } - opts := Options{ProductIDs: map[string]bool{"sample": true}, CanonicalToolPaths: map[string]string{"sample.tool": "sample tool"}} - if err := validateReviewedSelectionDelivery(file, opts); err != nil { - t.Fatalf("contract_final product delivery rejected: %v", err) - } - - // Declared products may omit selection products{} entirely. - if err := validateReviewedSelectionDelivery(File{ - Products: map[string]ProductMetadata{}, - Tools: file.Tools, - }, opts); err != nil { - t.Fatalf("ProductDecl-exempt missing product rejected: %v", err) - } -} - -func TestCrossPlatformCoverageExpectedCanonicalToolHelpersRemainingEdges(t *testing.T) { - paths := expectedCanonicalToolPaths(Options{CanonicalToolPaths: map[string]string{ - " sample.tool ": " sample item get ", - "": "blank", - "blank.tool": " ", - }}) - if len(paths) != 1 || paths["sample.tool"] != "sample item get" { - t.Fatalf("canonical paths = %#v", paths) - } - paths = expectedCanonicalToolPaths(Options{ToolPaths: map[string]string{ - "sample.tool": "sample item get", - "sample item get": "sample item get", - }}) - if len(paths) != 1 || paths["sample.tool"] != "sample item get" { - t.Fatalf("fallback canonical paths = %#v", paths) - } - - set := expectedCanonicalToolSet(Options{CanonicalToolPaths: map[string]string{" sample.tool ": "path", " ": "blank"}}) - if len(set) != 1 || !set["sample.tool"] { - t.Fatalf("canonical set = %#v", set) - } - if set := expectedCanonicalToolSet(Options{}); set != nil { - t.Fatalf("empty canonical set = %#v", set) - } - set = expectedCanonicalToolSet(Options{ToolPaths: map[string]string{"sample.tool": "path", "sample path": "path"}}) - if len(set) != 1 || !set["sample.tool"] { - t.Fatalf("fallback canonical set = %#v", set) - } -} - -func TestCrossPlatformCoverageSelectionAuthoringLoadFailure(t *testing.T) { - root := t.TempDir() - if err := os.MkdirAll(filepath.Join(root, "hints"), 0o700); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(filepath.Join(root, "hints", "selection"), 0o700); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(root, "hints", "selection", "bad.json"), []byte("{"), 0o600); err != nil { - t.Fatal(err) - } - err := validateSelectionAuthoringContracts(Options{Root: root, HintsDir: "hints"}) - if err == nil || !strings.Contains(err.Error(), "load selection Agent hints") { - t.Fatalf("selection load error = %v", err) - } -} - -func TestCrossPlatformCoverageSelectionAuthoringSelectionContractFailure(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, true, `["dws sample item search --query value"]`) - path := filepath.Join(root, "internal/cli/schema_hints/selection/sample.json") - body, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - body = []byte(strings.ReplaceAll(string(body), "A new item must be created", "An existing item must be found")) - if err := os.WriteFile(path, body, 0o600); err != nil { - t.Fatal(err) - } - err = validateSelectionAuthoringContracts(selectionFixtureOptions(root)) - if err == nil || !strings.Contains(err.Error(), "same literal positive and negative") { - t.Fatalf("selection contract error = %v", err) - } -} diff --git a/internal/generator/agentmetadata/manual_hints_test.go b/internal/generator/agentmetadata/manual_hints_test.go deleted file mode 100644 index 6c4c9762..00000000 --- a/internal/generator/agentmetadata/manual_hints_test.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); - -package agentmetadata - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" - "github.com/spf13/cobra" -) - -func TestGenerateUsesReviewedSelectionAsSingleDeliverySource(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, true, `["dws sample item search --query value"]`) - writeManualFixtureFile(t, root, "skills/mono/SKILL.md", "# Skill\n") - writeManualFixtureFile(t, root, "skills/mono/references/intent-guide.md", "# Intent\n") - writeManualFixtureFile(t, root, "skills/mono/references/products/sample.md", "# Sample\n") - writeManualFixtureFile(t, root, "internal/cli/schema_hints/imported/legacy.json", `{ - "version":1, - "source":{"kind":"imported","name":"legacy"}, - "tools":{"sample.search_items":{"agent_summary":"legacy A","use_when":["legacy A"],"avoid_when":["legacy A"],"examples":["dws sample item search --legacy-a"]}} -}`) - - selectionPath := filepath.Join(root, "internal/cli/schema_hints/selection/sample.json") - before, err := os.ReadFile(selectionPath) - if err != nil { - t.Fatal(err) - } - file, _, err := Generate(selectionFixtureOptions(root)) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - after, err := os.ReadFile(selectionPath) - if err != nil { - t.Fatal(err) - } - if string(after) != string(before) { - t.Fatal("Generate modified the reviewed selection input") - } - - tool := file.Tools["sample item search"] - if tool.AgentSummary != "Search existing sample items" || len(tool.UseWhen) != 1 || tool.UseWhen[0] != "An existing item must be found" { - t.Fatalf("selection was not delivered: %#v", tool) - } - product := file.Products["sample"] - for _, field := range []string{"agent_summary", "use_when", "avoid_when"} { - provenance := product.FieldProvenance[field] - if !isReviewedSelectionPrecedence(provenance.Precedence) || selectedCandidateCount(provenance.Candidates) != 1 { - t.Fatalf("product %s provenance = %#v", field, provenance) - } - if provenance.Precedence != selectionPrecedenceContractFinal && !strings.Contains(provenance.Source, "/selection/") { - t.Fatalf("product %s source = %q, want selection/ or contract_final", field, provenance.Source) - } - } - for _, field := range []string{"agent_summary", "use_when", "avoid_when", "examples"} { - provenance := tool.FieldProvenance[field] - if provenance.Precedence != selectionPrecedenceReviewedExplicit || selectedCandidateCount(provenance.Candidates) != 1 { - t.Fatalf("%s provenance = %#v", field, provenance) - } - for _, candidate := range provenance.Candidates { - if candidate.Precedence != selectionPrecedenceReviewedExplicit { - t.Fatalf("legacy prose remained a semantic candidate for %s: %#v", field, provenance.Candidates) - } - } - } -} - -func TestGenerateRejectsMaxExamplesThatWouldTruncateReviewedSelection(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, true, `["dws sample item search --query value","dws sample item search --query other"]`) - writeManualFixtureFile(t, root, "skills/mono/SKILL.md", "# Skill\n") - writeManualFixtureFile(t, root, "skills/mono/references/intent-guide.md", "# Intent\n") - if err := os.MkdirAll(filepath.Join(root, "skills/mono/references/products"), 0o755); err != nil { - t.Fatal(err) - } - opts := selectionFixtureOptions(root) - opts.MaxExamples = 1 - _, _, err := Generate(opts) - if err == nil || !strings.Contains(err.Error(), "exceeding max-examples=1") { - t.Fatalf("Generate() error = %v", err) - } -} - -func TestSelectionHintCoverageProductsExemptsProductDecl(t *testing.T) { - t.Cleanup(func() { cli.ClearProductDeclForTest("declared") }) - cli.RegisterProductDecl(cli.ProductDecl{ - ID: "declared", - Selection: cli.ProductSelectionDecl{ - AgentSummary: "Declared product", - UseWhen: []string{"use declared"}, - AvoidWhen: []string{"avoid declared"}, - }, - }) - got := SelectionHintCoverageProducts(RegistryProjection{ - ProductIDs: map[string]bool{"declared": true, "hinted": true}, - }) - if got["declared"] { - t.Fatal("ProductDecl product must be exempt from selection products coverage") - } - if !got["hinted"] { - t.Fatal("non-Decl product must remain required in selection products coverage") - } -} - -func TestGenerateRejectsIncompleteSelectionCoverage(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, false, `[]`) - writeManualFixtureFile(t, root, "skills/mono/SKILL.md", "# Skill\n") - writeManualFixtureFile(t, root, "skills/mono/references/intent-guide.md", "# Intent\n") - if err := os.MkdirAll(filepath.Join(root, "skills/mono/references/products"), 0o755); err != nil { - t.Fatal(err) - } - _, _, err := Generate(selectionFixtureOptions(root)) - if err == nil || !strings.Contains(err.Error(), "missing=[sample.search_items]") { - t.Fatalf("coverage error = %v", err) - } -} - -func TestGenerateAllowsEmptyAgentHintDirectory(t *testing.T) { - _, _, err := Generate(Options{}) - if err == nil || !strings.Contains(err.Error(), "complete Effective CommandRegistry projection is required") { - t.Fatalf("Generate() error = %v", err) - } -} - -func TestGenerateRequiresCompleteEffectiveCommandRegistryProjection(t *testing.T) { - _, _, err := Generate(Options{HintsDir: "hints-fixture"}) - if err == nil || !strings.Contains(err.Error(), "complete Effective CommandRegistry projection is required") { - t.Fatalf("Generate() error = %v", err) - } -} - -func TestGenerateValidatesSelectionExamplesAgainstBoundCobra(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, true, `["dws sample item search --invented value"]`) - writeManualFixtureFile(t, root, "skills/mono/SKILL.md", "# Skill\n") - writeManualFixtureFile(t, root, "skills/mono/references/intent-guide.md", "# Intent\n") - if err := os.MkdirAll(filepath.Join(root, "skills/mono/references/products"), 0o755); err != nil { - t.Fatal(err) - } - _, _, err := Generate(selectionFixtureOptions(root)) - if err == nil || !strings.Contains(err.Error(), "unknown flag --invented") { - t.Fatalf("Generate() error = %v", err) - } -} - -func selectionFixtureOptions(root string) Options { - leaf := &cobra.Command{Use: "search", Run: func(*cobra.Command, []string) {}} - leaf.Flags().String("query", "", "query") - boundSpec := cli.BoundCommandSpec{ - CommandSpec: cli.CommandSpec{CanonicalPath: "sample.search_items", PrimaryCLIPath: "sample item search"}, - PrimaryCommand: leaf, - } - return Options{ - Root: root, - SkillPath: "skills/mono/SKILL.md", - ProductsDir: "skills/mono/references/products", - IntentGuidePath: "skills/mono/references/intent-guide.md", - HintsDir: "internal/cli/schema_hints", - ToolPaths: map[string]string{"sample.search_items": "sample item search", "sample item search": "sample item search"}, - CanonicalToolPaths: map[string]string{"sample.search_items": "sample item search"}, - BoundCommands: cli.BoundCommandRegistry{ - Commands: []cli.BoundCommandSpec{boundSpec}, - ByCanonical: map[string]cli.BoundCommandSpec{"sample.search_items": boundSpec}, - ByCLIPath: map[string]cli.BoundCommandSpec{"sample item search": boundSpec}, - }, - ProductIDs: map[string]bool{"sample": true}, - SurfaceToolCount: 1, - MaxExamples: 2, - InterfaceMetadataPath: "", - MaxInterfaceSummaryRunes: 120, - } -} - -func writeSelectionFixture(t *testing.T, root string, includeTool bool, examplesJSON string) { - t.Helper() - writeManualFixtureFile(t, root, "internal/cli/schema_hints/index.json", `{ - "version":1, - "format":"dws-agent-hint-index", - "source":{"kind":"explicit","name":"fixture"}, - "metadata":{"sample":"metadata/sample.json"}, - "selection":{"sample":"selection/sample.json"} -}`) - writeManualFixtureFile(t, root, "internal/cli/schema_hints/metadata/sample.json", `{ - "version":1, - "source":{"kind":"explicit","name":"dws-tool-metadata/sample","reviewed":true}, - "tools":{ - "sample.search_items":{ - "effect":"read", - "risk":"low", - "confirmation":"not_required", - "runtime_gate":"none", - "reviewed":true, - "review_reason":"fixture metadata" - } - } -}`) - tool := "" - if includeTool { - tool = `"sample.search_items":{ - "agent_summary":"Search existing sample items", - "use_when":["An existing item must be found"], - "avoid_when":["A new item must be created"], - "examples":` + examplesJSON + `, - "reviewed":true, - "review_reason":"Reviewed selection guidance", - "source_refs":["dws sample item search --help"] - }` - } - writeManualFixtureFile(t, root, "internal/cli/schema_hints/selection/sample.json", `{ - "version":1, - "source":{"kind":"explicit","name":"dws-agent-selection/sample","reviewed":true}, - "products":{ - "sample":{ - "agent_summary":"Manage samples", - "use_when":["The target is a sample"], - "avoid_when":["The target is not a sample"], - "reviewed":true, - "review_reason":"Reviewed product routing", - "source_refs":["sample.md"] - } - }, - "tools":{`+tool+`} -}`) -} - -func writeManualFixtureFile(t *testing.T, root, relative, body string) { - t.Helper() - path := filepath.Join(root, relative) - if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(path, []byte(body), 0o644); err != nil { - t.Fatal(err) - } -} diff --git a/internal/generator/agentmetadata/metadata_helpers_coverage_test.go b/internal/generator/agentmetadata/metadata_helpers_coverage_test.go index ce298e3a..2bff82a1 100644 --- a/internal/generator/agentmetadata/metadata_helpers_coverage_test.go +++ b/internal/generator/agentmetadata/metadata_helpers_coverage_test.go @@ -40,7 +40,7 @@ func TestCrossPlatformCoverageMetadataMarshalAndProjectionEdges(t *testing.T) { t.Fatal("unexpected tool projection succeeded") } if _, _, err := Generate(Options{ - HintsDir: "hints", + HintsDir: "", CanonicalToolPaths: map[string]string{"sample": "sample get"}, ToolPaths: map[string]string{"sample": "sample get"}, ProductIDs: map[string]bool{"sample": true}, @@ -483,93 +483,6 @@ func TestCrossPlatformCoverageMetadataParserConflictEdges(t *testing.T) { } } -func TestCrossPlatformCoverageGenerateFromSourcesFailureEdges(t *testing.T) { - base := func(t *testing.T) (string, Options) { - t.Helper() - root := t.TempDir() - if err := os.MkdirAll(filepath.Join(root, "products"), 0o700); err != nil { - t.Fatal(err) - } - writeManualFixtureFile(t, root, "empty.md", "# empty") - return root, Options{Root: root, ProductsDir: "products", SkillPath: "empty.md", IntentGuidePath: "empty.md"} - } - - t.Run("danger rules", func(t *testing.T) { - root, opts := base(t) - opts.SkillPath = "skill.md" - writeManualFixtureFile(t, root, "skill.md", "## 危险操作确认\n| 产品 | 命令 | 风险 |\n|---|---|---|\n| `sample` | `get` | 删除 |\n| `sample` | `get` | 高风险写入 |") - if _, _, err := generateFromSources(opts); err == nil { - t.Fatal("conflicting danger source succeeded") - } - }) - - t.Run("examples", func(t *testing.T) { - root, opts := base(t) - opts.SkillPath = "skill.md" - writeManualFixtureFile(t, root, "skill.md", "## 危险操作确认\n| 产品 | 命令 | 风险 |\n|---|---|---|\n| `sample` | `delete` | 删除且不可逆 |") - body := "## 使用场景\n- 用户提到“delete” → `sample delete`\n\n```bash\n# 高风险需要确认\ndws sample delete\n```" - writeManualFixtureFile(t, root, "products/sample/guide.md", body) - if _, _, err := generateFromSources(opts); err == nil { - t.Fatal("conflicting example source succeeded") - } - }) - - t.Run("interface metadata", func(t *testing.T) { - root, opts := base(t) - opts.InterfaceMetadataPath = "interface.json" - writeManualFixtureFile(t, root, "interface.json", "{") - if _, _, err := generateFromSources(opts); err == nil { - t.Fatal("invalid interface metadata succeeded") - } - }) - - t.Run("projected count", func(t *testing.T) { - _, opts := base(t) - opts.ToolPaths = map[string]string{"sample.tool": "sample get"} - opts.SurfaceToolCount = 2 - if _, _, err := generateFromSources(opts); err == nil { - t.Fatal("invalid projected count succeeded") - } - }) - - t.Run("reviewed delivery", func(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, true, `["dws sample item search --query value"]`) - writeManualFixtureFile(t, root, "skills/mono/SKILL.md", "# empty") - writeManualFixtureFile(t, root, "skills/mono/references/intent-guide.md", "# empty") - if err := os.MkdirAll(filepath.Join(root, "skills/mono/references/products"), 0o700); err != nil { - t.Fatal(err) - } - opts := selectionFixtureOptions(root) - opts.ToolPaths = map[string]string{"other.tool": "other get"} - opts.SurfaceToolCount = 1 - if _, _, err := generateFromSources(opts); err == nil || !strings.Contains(err.Error(), "selection Agent delivery") { - t.Fatalf("reviewed delivery error = %v", err) - } - }) - - t.Run("interface disposition", func(t *testing.T) { - root := t.TempDir() - writeSelectionFixture(t, root, true, `["dws sample item search --query value"]`) - writeManualFixtureFile(t, root, "skills/mono/SKILL.md", "# empty") - writeManualFixtureFile(t, root, "skills/mono/references/intent-guide.md", "# empty") - if err := os.MkdirAll(filepath.Join(root, "skills/mono/references/products"), 0o700); err != nil { - t.Fatal(err) - } - path := filepath.Join(root, "internal/cli/schema_hints/metadata/sample.json") - body, err := os.ReadFile(path) - if err != nil { - t.Fatal(err) - } - body = []byte(strings.Replace(string(body), `"runtime_gate":"none",`, `"runtime_gate":"none","interface_mode":"mcp","availability":"available",`, 1)) - if err := os.WriteFile(path, body, 0o600); err != nil { - t.Fatal(err) - } - if _, _, err := generateFromSources(selectionFixtureOptions(root)); err == nil || !strings.Contains(err.Error(), "invalid final Agent interface disposition") { - t.Fatalf("interface disposition error = %v", err) - } - }) -} func TestCrossPlatformCoverageContractFinalDeclarationFailureEdges(t *testing.T) { declared := &cobra.Command{Use: "run"} diff --git a/internal/generator/agentmetadata/metadata_test.go b/internal/generator/agentmetadata/metadata_test.go index 55f7d799..6a835efe 100644 --- a/internal/generator/agentmetadata/metadata_test.go +++ b/internal/generator/agentmetadata/metadata_test.go @@ -292,93 +292,6 @@ func TestValidateEffectiveToolProjectionRejectsCountOnlyFalseGreen(t *testing.T) } } -func TestGenerateMergesVersionedHintsByCanonicalPath(t *testing.T) { - root := t.TempDir() - writeFixture(t, root, "skills/mono/SKILL.md", "# DWS\n## 意图判断决策树\n用户提到\"日程\" → `calendar`\n") - writeFixture(t, root, "skills/mono/references/intent-guide.md", "# Intent guide\n") - writeFixture(t, root, "skills/mono/references/products/calendar.md", "# Calendar\n") - writeFixture(t, root, "internal/cli/schema_hints/imported/wukong.json", `{ - "version": 1, - "source": {"kind": "imported", "name": "dws-wukong", "revision": "1234567890abcdef"}, - "products": { - "calendar": {"agent_summary": "管理日历日程"} - }, - "tools": { - "calendar.get_calendar_detail": { - "agent_summary": "获取日程详情", - "risk": "high", - "confirmation": "user_required", - "examples": ["dws calendar event get --id EVENT_ID"] - } - } -}`) - writeFixture(t, root, "internal/cli/schema_hints/calendar.json", `{ - "version": 1, - "source": {"kind": "explicit", "name": "schema-review"}, - "tools": { - "calendar.get_calendar_detail": { - "agent_summary": "读取一个日程的完整详情", - "use_when": ["已经取得 eventId,需要查看详情"], - "avoid_when": ["需要修改日程时不要使用"], - "examples": ["dws calendar event get --id reviewed"], - "interface_ref": {"product_id": "calendar", "rpc_name": "get_calendar_detail"}, - "interface_mode": "mcp", - "availability": "available", - "reviewed": true - }, - "aitable.base_copy": { - "interface_ref": {"product_id": "aitable", "rpc_name": "copy_base"}, - "interface_mode": "mcp", - "availability": "available" - } - } -}`) - - metadata, stats, err := generateFromSources(Options{ - Root: root, - SkillPath: "skills/mono/SKILL.md", - ProductsDir: "skills/mono/references/products", - IntentGuidePath: "skills/mono/references/intent-guide.md", - HintsDir: "internal/cli/schema_hints", - ToolPaths: map[string]string{ - "calendar.get_calendar_detail": "calendar event get", - "calendar event get": "calendar event get", - "aitable.base_copy": "aitable base copy", - }, - ProductIDs: map[string]bool{"calendar": true, "aitable": true}, - SurfaceToolCount: 2, - }) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - product := metadata.Products["calendar"] - if product.AgentSummary != "管理日历日程" || product.AgentSummarySource != "dws-wukong@1234567890ab" { - t.Fatalf("product metadata = %#v", product) - } - tool := metadata.Tools["calendar event get"] - if tool.AgentSummary != "读取一个日程的完整详情" || tool.AgentSummarySource != "schema-review" { - t.Fatalf("tool summary = %#v", tool) - } - if len(tool.Examples) != 1 || tool.Examples[0] != "dws calendar event get --id reviewed" || len(tool.UseWhen) != 1 || len(tool.AvoidWhen) != 1 || tool.Risk != "high" || tool.Confirmation != "user_required" { - t.Fatalf("merged tool metadata = %#v", tool) - } - if tool.InterfaceMode != "mcp" || tool.Availability != "available" { - t.Fatalf("interface disposition = %#v", tool) - } - if tool.Reviewed == nil || !*tool.Reviewed { - t.Fatalf("reviewed = %#v", tool.Reviewed) - } - if stats.HintFiles != 2 || stats.HintTools != 3 || metadata.Coverage.ToolsWithSummary != 1 { - t.Fatalf("hint stats = %#v coverage=%#v", stats, metadata.Coverage) - } - interfaceOnly, exists := metadata.Tools["aitable base copy"] - if !exists || interfaceOnly.InterfaceRef == nil { - t.Fatalf("interface-only hint was not retained: %#v", interfaceOnly) - } - if interfaceOnly.InterfaceRef.ProductID != "aitable" || interfaceOnly.InterfaceRef.RPCName != "copy_base" { - t.Fatalf("interface ref = %#v", interfaceOnly.InterfaceRef) - } -} func TestMergeToolMetadataUsesSourcePrecedenceAcrossAliases(t *testing.T) { merged, err := mergeToolMetadata( @@ -406,49 +319,6 @@ func TestMergeToolMetadataUsesSourcePrecedenceAcrossAliases(t *testing.T) { } } -func TestGenerateAppliesReviewedSkillReferenceDispositions(t *testing.T) { - root := t.TempDir() - writeFixture(t, root, "skills/mono/SKILL.md", "# DWS\n") - writeFixture(t, root, "skills/mono/references/intent-guide.md", "# Intent guide\n") - writeFixture(t, root, "skills/mono/references/products/sheet.md", "# Sheet\n"+ - "dws sheet range old --node NODE\n"+ - "dws sheet range removed --node NODE\n") - writeFixture(t, root, "internal/cli/schema_hints/reference-review.json", `{ - "version": 1, - "source": {"kind": "explicit", "name": "reference-review"}, - "reference_review": { - "sheet range old": {"status": "alias", "target": "sheet range read", "reason": "renamed"}, - "sheet range removed": {"status": "stale", "reason": "removed from the public surface"} - } -}`) - - metadata, stats, err := generateFromSources(Options{ - Root: root, - SkillPath: "skills/mono/SKILL.md", - ProductsDir: "skills/mono/references/products", - IntentGuidePath: "skills/mono/references/intent-guide.md", - HintsDir: "internal/cli/schema_hints", - ToolPaths: map[string]string{ - "sheet.range_read": "sheet range read", - "sheet range read": "sheet range read", - }, - ProductIDs: map[string]bool{"sheet": true}, - SurfaceToolCount: 1, - }) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - if _, ok := metadata.Tools["sheet range read"]; !ok { - t.Fatalf("reviewed alias was not merged: %#v", metadata.Tools) - } - if stats.UnmatchedTools != 1 || stats.unreviewedSkillTools != 0 || len(stats.UnmatchedReferences) != 1 { - t.Fatalf("reference review stats = %#v", stats) - } - reference := stats.UnmatchedReferences[0] - if reference.ToolPath != "sheet range removed" || reference.Review == nil || reference.Review.Status != "stale" { - t.Fatalf("reviewed unmatched reference = %#v", reference) - } -} func TestClassifyEffectPathUsesActionSegment(t *testing.T) { tests := map[string]string{ diff --git a/internal/generator/agentmetadata/pipeline.go b/internal/generator/agentmetadata/pipeline.go index 31e66f07..5b03bbd4 100644 --- a/internal/generator/agentmetadata/pipeline.go +++ b/internal/generator/agentmetadata/pipeline.go @@ -15,8 +15,8 @@ package agentmetadata import ( "fmt" - "os" "path/filepath" + "sort" "strings" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" @@ -91,41 +91,36 @@ func SelectionHintCoverageProducts(projection RegistryProjection) map[string]boo return selectionHintCoverageProducts(projection.ProductIDs) } -// ValidateSelectionHints validates optional residual selection/ HintFiles -// against the projected registry. Production no longer requires -// schema_hints/: when every projected product has ProductDecl and every -// projected tool has ContractFinal, an empty or absent HintsDir is valid. -// When residual coverage remains, selection/ must exist under HintsDir. -func ValidateSelectionHints(rootPath, hintsDir string, projection RegistryProjection) error { +// ValidateSelectionCoverage requires every projected product to have +// ProductDecl and every projected tool to have ContractFinal. Residual +// schema_hints/ HintFiles are not accepted. +func ValidateSelectionCoverage(projection RegistryProjection) error { expectedTools := SelectionHintCoverageTools(projection) expectedProducts := SelectionHintCoverageProducts(projection) if !selectionHintCoverageRequired(expectedProducts, expectedTools) { return nil } - if strings.TrimSpace(hintsDir) == "" { - return fmt.Errorf("Agent selection HintFiles required for undeclared tools/products, but HintsDir is empty; declare ProductDecl/ContractFinal or provide residual selection/") + missingProducts := make([]string, 0) + for productID, include := range expectedProducts { + if include { + missingProducts = append(missingProducts, productID) + } } - hintsRoot := resolvePipelineRootPath(rootPath, hintsDir) - selectionRoot := filepath.Join(hintsRoot, "selection") - info, err := os.Stat(selectionRoot) - missing := err != nil || !info.IsDir() - if missing { - return fmt.Errorf("required Agent hint directory missing: %s", selectionRoot) + missingTools := make([]string, 0) + for tool, include := range expectedTools { + if include { + missingTools = append(missingTools, tool) + } } - agentHints, err := cli.LoadAgentHintsFromSelectionForValidation(os.DirFS(selectionRoot)) - if err != nil { - return fmt.Errorf("load selection Agent hints: %w", err) - } - if err := cli.ValidateManualAgentHintSet(agentHints, expectedProducts, expectedTools); err != nil { - return fmt.Errorf("validate selection Agent hints: %w", err) - } - if err := cli.ValidateManualAgentHintExamples(projection.Bound, agentHints); err != nil { - return fmt.Errorf("validate selection Agent hint examples: %w", err) - } - if _, err := cli.ValidateManualAgentSelectionContract(projection.Bound, agentHints); err != nil { - return fmt.Errorf("validate selection Agent selection contract: %w", err) - } - return nil + sort.Strings(missingProducts) + sort.Strings(missingTools) + return fmt.Errorf("ProductDecl/ContractFinal selection coverage incomplete: missing_products=%v missing_tools=%v", missingProducts, missingTools) +} + +// ValidateSelectionHints is a compatibility wrapper that ignores HintsDir and +// enforces ContractFinal/ProductDecl coverage only. +func ValidateSelectionHints(_ string, _ string, projection RegistryProjection) error { + return ValidateSelectionCoverage(projection) } func selectionHintCoverageRequired(expectedProducts, expectedTools map[string]bool) bool { @@ -143,10 +138,9 @@ func selectionHintCoverageRequired(expectedProducts, expectedTools map[string]bo } // GenerateFromCommandRoot is the Catalog in-memory Agent-metadata pipeline. -// It applies reviewed manual Schema overlays, binds the EffectiveCommandRegistry, -// validates that ProductDecl/ContractFinal cover selection (residual HintFiles -// optional), and Generate()s metadata without reading or writing -// schema_agent_metadata/. Production leaves HintsDir empty. +// It binds the EffectiveCommandRegistry, validates that ProductDecl/ContractFinal +// cover selection, and Generate()s metadata without reading or writing +// schema_agent_metadata/ or schema_hints/. func GenerateFromCommandRoot(rootPath string, commandRoot *cobra.Command, opts Options) (File, Stats, RegistryProjection, error) { if commandRoot == nil { return File{}, Stats{}, RegistryProjection{}, fmt.Errorf("schema source root is nil") @@ -155,9 +149,6 @@ func GenerateFromCommandRoot(rootPath string, commandRoot *cobra.Command, opts O if rootPath == "" { rootPath = "." } - if _, err := cli.ApplyEmbeddedManualSchemaHints(commandRoot); err != nil { - return File{}, Stats{}, RegistryProjection{}, fmt.Errorf("apply reviewed manual Schema hints for Agent metadata: %w", err) - } effective, err := cli.BuildEffectiveCommandRegistry(commandRoot) if err != nil { return File{}, Stats{}, RegistryProjection{}, fmt.Errorf("build effective CommandRegistry for Agent metadata: %w", err) @@ -179,8 +170,7 @@ func GenerateFromCommandRoot(rootPath string, commandRoot *cobra.Command, opts O if strings.TrimSpace(opts.IntentGuidePath) == "" { opts.IntentGuidePath = "skills/mono/references/intent-guide.md" } - // HintsDir defaults empty: schema_hints/ is retired. Fixture tests may still - // pass a temporary HintsDir to exercise residual HintFile parsing. + // HintsDir must stay empty: schema_hints/ is retired and non-empty values fail closed. if strings.TrimSpace(opts.InterfaceMetadataPath) == "" { opts.InterfaceMetadataPath = "internal/cli/schema_mcp_metadata.json" } @@ -197,7 +187,7 @@ func GenerateFromCommandRoot(rootPath string, commandRoot *cobra.Command, opts O opts.SurfaceHash = projection.Hash opts.SurfaceToolCount = projection.ToolCount - if err := ValidateSelectionHints(rootPath, opts.HintsDir, projection); err != nil { + if err := ValidateSelectionCoverage(projection); err != nil { return File{}, Stats{}, RegistryProjection{}, err } metadata, stats, err := Generate(opts) diff --git a/internal/generator/agentmetadata/precedence_test.go b/internal/generator/agentmetadata/precedence_test.go deleted file mode 100644 index a0ae1960..00000000 --- a/internal/generator/agentmetadata/precedence_test.go +++ /dev/null @@ -1,527 +0,0 @@ -// Copyright 2026 Alibaba Group -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package agentmetadata - -import ( - "encoding/json" - "fmt" - "strings" - "testing" -) - -func TestAgentMetadataScalarSourcePrecedenceMatrix(t *testing.T) { - tests := []struct { - rank int - precedence string - }{ - {selectionRankReviewedManual, selectionPrecedenceReviewedManual}, - {selectionRankReviewedExplicit, selectionPrecedenceReviewedExplicit}, - {selectionRankExplicit, selectionPrecedenceExplicit}, - {selectionRankImported, selectionPrecedenceImported}, - {selectionRankUnreviewedExplicit, selectionPrecedenceUnreviewedExplicit}, - {selectionRankSkill, selectionPrecedenceSkill}, - {selectionRankMCPFallback, selectionPrecedenceMCPFallback}, - {selectionRankDefault, selectionPrecedenceDefault}, - } - for index, test := range tests { - if got := selectionPrecedence(test.rank); got != test.precedence { - t.Fatalf("rank %d precedence = %q, want %q", test.rank, got, test.precedence) - } - if got := precedenceRank(test.precedence); got != test.rank { - t.Fatalf("precedence %q rank = %d, want %d", test.precedence, got, test.rank) - } - if index > 0 && tests[index-1].rank <= test.rank { - t.Fatalf("scalar source precedence is not strictly descending at %q > %q", tests[index-1].precedence, test.precedence) - } - } -} - -func TestReviewedHintSourceRanksEveryAuthoredFieldWithoutPerToolDuplication(t *testing.T) { - if got := hintSelectionRank(HintSource{Kind: "explicit", Name: "reviewed-baseline", Reviewed: true}, nil); got != selectionRankReviewedExplicit { - t.Fatalf("reviewed source rank = %d, want %d", got, selectionRankReviewedExplicit) - } - unreviewed := false - if got := hintSelectionRank(HintSource{Kind: "explicit", Reviewed: true}, &unreviewed); got != selectionRankUnreviewedExplicit { - t.Fatalf("per-tool reviewed=false rank = %d, want %d", got, selectionRankUnreviewedExplicit) - } - reason := hintCandidateReviewReason(HintSource{Kind: "explicit", Name: "reviewed-baseline", Reviewed: true}, HintTool{}) - if !strings.Contains(reason, "reviewed Agent hint source") { - t.Fatalf("source-level review reason = %q", reason) - } -} - -func TestGenerateUsesSourcePrecedenceForSelectionAndSafety(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/10-imported.json", hintFixture("imported", "imported-review", "calendar attendee update", `{ - "agent_summary": "imported summary", - "risk": "high", - "confirmation": "user_required" - }`)) - writeFixture(t, root, "internal/cli/schema_hints/20-unreviewed.json", hintFixture("explicit", "generated-baseline", "calendar attendee update", `{ - "agent_summary": "unreviewed baseline summary", - "risk": "medium", - "confirmation": "not_required", - "reviewed": false - }`)) - writeFixture(t, root, "internal/cli/schema_hints/30-reviewed.json", hintFixture("explicit", "reviewed-selection", "calendar attendee update", `{ - "agent_summary": "reviewed explicit summary", - "risk": "low", - "confirmation": "not_required", - "reviewed": true - }`)) - - metadata, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - })) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - tool := metadata.Tools["calendar attendee update"] - if tool.AgentSummary != "reviewed explicit summary" || tool.AgentSummarySource != "reviewed-selection" { - t.Fatalf("selection winner = %q from %q", tool.AgentSummary, tool.AgentSummarySource) - } - if tool.Risk != "low" || tool.Confirmation != "not_required" { - t.Fatalf("reviewed explicit safety did not override imported values: %s/%s", tool.Risk, tool.Confirmation) - } - riskProvenance := tool.FieldProvenance["risk"] - if riskProvenance.Value != "low" || riskProvenance.Source != "internal/cli/schema_hints/30-reviewed.json" || riskProvenance.Precedence != "reviewed_explicit" || riskProvenance.Resolution != "highest_precedence" { - t.Fatalf("risk provenance = %#v", riskProvenance) - } - if riskProvenance.ReviewReason == "" { - t.Fatalf("risk provenance has no review reason: %#v", riskProvenance) - } - if !hasCandidate(riskProvenance.Candidates, "high", "internal/cli/schema_hints/10-imported.json", "imported", false) { - t.Fatalf("risk provenance lost imported candidate: %#v", riskProvenance) - } - if !hasCandidate(riskProvenance.Candidates, "low", "internal/cli/schema_hints/30-reviewed.json", "reviewed_explicit", true) { - t.Fatalf("risk provenance did not mark its winner: %#v", riskProvenance) - } - confirmationProvenance := tool.FieldProvenance["confirmation"] - if confirmationProvenance.Source != "internal/cli/schema_hints/30-reviewed.json" || confirmationProvenance.Precedence != "reviewed_explicit" { - t.Fatalf("confirmation provenance = %#v", confirmationProvenance) - } -} - -func TestGenerateRejectsSamePrecedenceScalarConflict(t *testing.T) { - tests := []struct { - name string - first string - second string - wantField string - }{ - {name: "selection", first: `{"agent_summary":"first summary"}`, second: `{"agent_summary":"second summary"}`, wantField: "agent_summary"}, - {name: "selection empty versus nonempty", first: `{"agent_summary":""}`, second: `{"agent_summary":"second summary"}`, wantField: "agent_summary"}, - {name: "safety", first: `{"risk":"high"}`, second: `{"risk":"low"}`, wantField: "risk"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/a.json", hintFixture("explicit", "review-a", "calendar attendee update", test.first)) - writeFixture(t, root, "internal/cli/schema_hints/b.json", hintFixture("explicit", "review-b", "calendar attendee update", test.second)) - - _, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - })) - if err == nil || !strings.Contains(err.Error(), "field "+test.wantField) || !strings.Contains(err.Error(), "a.json") || !strings.Contains(err.Error(), "b.json") { - t.Fatalf("Generate() error = %v, want source-aware same-precedence conflict", err) - } - }) - } -} - -func TestGenerateRejectsLowerPrecedenceConflictHiddenByHigherWinner(t *testing.T) { - tests := []struct { - name string - left string - right string - }{ - {name: "lower values in path order", left: "high", right: "low"}, - {name: "lower values reverse path order", left: "low", right: "high"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/10-import-a.json", hintFixture( - "imported", "import-a", "calendar.attendee_update_a", fmt.Sprintf(`{"risk":%q}`, test.left), - )) - writeFixture(t, root, "internal/cli/schema_hints/20-import-b.json", hintFixture( - "imported", "import-b", "calendar.attendee_update_b", fmt.Sprintf(`{"risk":%q}`, test.right), - )) - writeFixture(t, root, "internal/cli/schema_hints/30-reviewed.json", `{ - "version": 1, - "source": {"kind": "explicit", "name": "reviewed-winner"}, - "tools": { - "calendar.attendee_update_a": {"risk": "medium", "reviewed": true}, - "calendar.attendee_update_b": {"risk": "medium", "reviewed": true} - } -}`) - - _, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - "calendar.attendee_update_a": "calendar attendee update", - "calendar.attendee_update_b": "calendar attendee update", - })) - if err == nil || !strings.Contains(err.Error(), "field risk") || - !strings.Contains(err.Error(), "10-import-a.json") || !strings.Contains(err.Error(), "20-import-b.json") || - !strings.Contains(err.Error(), "precedence 200") { - t.Fatalf("Generate() error = %v, want masked imported-rank conflict", err) - } - }) - } -} - -func TestGenerateRejectsSamePrecedenceInterfaceConflictAfterAliasReconciliation(t *testing.T) { - tests := []struct { - name string - first string - second string - wantField string - }{ - { - name: "interface ref", - first: `{"interface_ref":{"product_id":"calendar","rpc_name":"update_attendee"}}`, - second: `{"interface_ref":{"product_id":"calendar","rpc_name":"replace_attendee"}}`, - wantField: "interface_ref", - }, - { - name: "interface disposition", - first: `{"interface_mode":"local"}`, - second: `{"interface_mode":"mcp"}`, - wantField: "interface_mode", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/aliases.json", fmt.Sprintf(`{ - "version": 1, - "source": {"kind": "explicit", "name": "alias-review"}, - "tools": { - "calendar.attendee_update_old": %s, - "calendar.attendee_update_new": %s - } -}`, test.first, test.second)) - - _, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - "calendar.attendee_update_old": "calendar attendee update", - "calendar.attendee_update_new": "calendar attendee update", - })) - if err == nil || !strings.Contains(err.Error(), "field "+test.wantField) || !strings.Contains(err.Error(), "calendar attendee update") { - t.Fatalf("Generate() error = %v, want %s alias conflict", err, test.wantField) - } - }) - } -} - -func TestReviewedLocalDispositionClearsLowerPrecedenceMCPRef(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/10-imported.json", hintFixture("imported", "imported-interface", "calendar attendee update", `{ - "interface_ref": {"product_id": "calendar", "rpc_name": "update_attendee"}, - "interface_mode": "mcp", - "availability": "available" - }`)) - writeFixture(t, root, "internal/cli/schema_hints/20-reviewed.json", hintFixture("explicit", "reviewed-interface", "calendar attendee update", `{ - "interface_mode": "local", - "interface_reason": "The CLI wrapper is executable but has no pinned MCP operation.", - "availability": "available", - "reviewed": true - }`)) - - metadata, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - })) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - tool := metadata.Tools["calendar attendee update"] - if tool.InterfaceMode != "local" || tool.InterfaceRef != nil { - t.Fatalf("interface = mode %q ref %#v, want reviewed local without direct ref", tool.InterfaceMode, tool.InterfaceRef) - } - refProvenance, exists := tool.FieldProvenance["interface_ref"] - if !exists || refProvenance.Value != nil || refProvenance.Resolution != "interface_disposition_matrix" || len(refProvenance.OverriddenCandidates) == 0 { - t.Fatalf("interface_ref disposition provenance = %#v", refProvenance) - } - provenance := tool.FieldProvenance["interface_mode"] - if provenance.Value != "local" || provenance.Source != "internal/cli/schema_hints/20-reviewed.json" || provenance.Precedence != "reviewed_explicit" { - t.Fatalf("interface_mode provenance = %#v", provenance) - } -} - -func TestInterfaceDispositionResolutionSurvivesAliasReconciliation(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/10-imported.json", hintFixture("imported", "legacy-local", "calendar.update_legacy", `{ - "interface_ref": {"product_id": "calendar", "rpc_name": "update_attendee"}, - "interface_mode": "local", - "availability": "available", - "interface_reason": "legacy wrapper" - }`)) - writeFixture(t, root, "internal/cli/schema_hints/20-reviewed.json", hintFixture("explicit", "reviewed-mcp", "calendar.update_current", `{ - "interface_mode": "mcp", - "availability": "available", - "reviewed": true - }`)) - - metadata, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - "calendar.update_legacy": "calendar attendee update", - "calendar.update_current": "calendar attendee update", - })) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - tool := metadata.Tools["calendar attendee update"] - if tool.InterfaceMode != "mcp" || tool.InterfaceRef == nil || tool.InterfaceRef.ProductID != "calendar" || tool.InterfaceRef.RPCName != "update_attendee" { - t.Fatalf("reconciled interface = mode %q ref %#v", tool.InterfaceMode, tool.InterfaceRef) - } - if provenance := tool.FieldProvenance["interface_ref"]; provenance.Value != "calendar.update_attendee" || provenance.Precedence != "imported" { - t.Fatalf("reconciled ref provenance = %#v", provenance) - } -} - -func TestSafetyPrecedenceAllowsExplicitDowngrade(t *testing.T) { - tests := []struct { - name string - left ToolMetadata - right ToolMetadata - wantRisk string - wantConfirm string - }{ - { - name: "reviewed explicit low overrides imported high", - left: ToolMetadata{ - Risk: "high", - riskRank: selectionRankImported, - riskOrigin: "imported.json", - Confirmation: "user_required", - confirmationRank: selectionRankImported, - confirmationOrigin: "imported.json", - }, - right: ToolMetadata{ - Risk: "low", - riskRank: selectionRankReviewedExplicit, - riskOrigin: "reviewed.json", - Confirmation: "not_required", - confirmationRank: selectionRankReviewedExplicit, - confirmationOrigin: "reviewed.json", - }, - wantRisk: "low", - wantConfirm: "not_required", - }, - { - name: "explicit low overrides imported high", - left: ToolMetadata{ - Risk: "high", - riskRank: selectionRankImported, - riskOrigin: "imported.json", - Confirmation: "user_required", - confirmationRank: selectionRankImported, - confirmationOrigin: "imported.json", - }, - right: ToolMetadata{ - Risk: "low", - riskRank: selectionRankExplicit, - riskOrigin: "explicit.json", - Confirmation: "not_required", - confirmationRank: selectionRankExplicit, - confirmationOrigin: "explicit.json", - }, - wantRisk: "low", - wantConfirm: "not_required", - }, - { - name: "explicit high remains above imported low", - left: ToolMetadata{ - Risk: "high", - riskRank: selectionRankExplicit, - riskOrigin: "explicit.json", - Confirmation: "user_required", - confirmationRank: selectionRankExplicit, - confirmationOrigin: "explicit.json", - }, - right: ToolMetadata{ - Risk: "low", - riskRank: selectionRankImported, - riskOrigin: "imported.json", - Confirmation: "not_required", - confirmationRank: selectionRankImported, - confirmationOrigin: "imported.json", - }, - wantRisk: "high", - wantConfirm: "user_required", - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - merged, err := mergeToolMetadata(test.left, test.right, "calendar attendee update") - if err != nil { - t.Fatalf("mergeToolMetadata() error = %v", err) - } - if merged.Risk != test.wantRisk || merged.Confirmation != test.wantConfirm { - t.Fatalf("safety = %s/%s, want %s/%s", merged.Risk, merged.Confirmation, test.wantRisk, test.wantConfirm) - } - }) - } -} - -func TestReviewedAuthoredEmptyScalarsOverrideLowerValues(t *testing.T) { - root := t.TempDir() - writePrecedenceFixture(t, root, "calendar attendee update") - writeFixture(t, root, "internal/cli/schema_hints/10-imported.json", hintFixture("imported", "legacy", "calendar attendee update", `{ - "agent_summary": "legacy summary", - "effect": "write", - "risk": "high", - "confirmation": "user_required", - "idempotency": "unknown", - "interface_mode": "local", - "availability": "available", - "interface_reason": "legacy wrapper" - }`)) - writeFixture(t, root, "internal/cli/schema_hints/20-reviewed.json", hintFixture("explicit", "reviewed-empty", "calendar attendee update", `{ - "agent_summary": "", - "effect": "", - "risk": "", - "confirmation": "", - "idempotency": "", - "interface_reason": "", - "reviewed": true, - "review_reason": "reviewed clearing of stale scalar metadata" - }`)) - - metadata, _, err := generateFromSources(precedenceOptions(root, map[string]string{ - "calendar attendee update": "calendar attendee update", - })) - if err != nil { - t.Fatalf("Generate() error = %v", err) - } - tool := metadata.Tools["calendar attendee update"] - values := map[string]string{ - "agent_summary": tool.AgentSummary, - "effect": tool.Effect, - "risk": tool.Risk, - "confirmation": tool.Confirmation, - "idempotency": tool.Idempotency, - "interface_reason": tool.InterfaceReason, - } - for field, value := range values { - if value != "" { - t.Errorf("%s = %q, want authored empty", field, value) - } - provenance := tool.FieldProvenance[field] - if provenance.Value != "" || provenance.Source != "internal/cli/schema_hints/20-reviewed.json" || provenance.Precedence != "reviewed_explicit" { - t.Errorf("%s provenance = %#v", field, provenance) - continue - } - selected := 0 - for _, candidate := range provenance.Candidates { - if candidate.Selected { - selected++ - if candidate.Value != "" { - t.Errorf("%s selected candidate = %#v", field, candidate) - } - } - } - if selected != 1 { - t.Errorf("%s selected candidates = %d, provenance %#v", field, selected, provenance) - } - } -} - -func TestHintToolPreservesExplicitEmptyAndNullPresence(t *testing.T) { - var hint HintTool - if err := json.Unmarshal([]byte(`{ - "agent_summary": "", - "effect": "", - "risk": "", - "confirmation": "", - "idempotency": "", - "interface_ref": null, - "interface_mode": "", - "availability": "", - "interface_reason": "" -}`), &hint); err != nil { - t.Fatal(err) - } - if !hint.agentSummaryPresent || !hint.effectPresent || !hint.riskPresent || - !hint.confirmationPresent || !hint.idempotencyPresent || !hint.interfaceRefPresent || - !hint.interfaceModePresent || !hint.availabilityPresent || !hint.interfaceReasonPresent { - t.Fatalf("decoded hint lost authored presence: %#v", hint) - } - - left := ToolMetadata{ - InterfaceRef: &InterfaceRef{ProductID: "calendar", RPCName: "update_attendee"}, - interfaceRefPresent: true, - interfaceRefRank: selectionRankImported, - interfaceRefOrigin: "imported.json", - } - right := ToolMetadata{ - interfaceRefPresent: true, - interfaceRefRank: selectionRankReviewedExplicit, - interfaceRefOrigin: "reviewed.json", - } - merged, err := mergeToolMetadata(left, right, "calendar attendee update") - if err != nil { - t.Fatalf("mergeToolMetadata() error = %v", err) - } - if merged.InterfaceRef != nil || !merged.interfaceRefPresent { - t.Fatalf("merged interface ref = %#v present=%v, want authored null", merged.InterfaceRef, merged.interfaceRefPresent) - } - provenance := merged.FieldProvenance["interface_ref"] - if provenance.Value != nil || provenance.Source != "reviewed.json" || provenance.Precedence != "reviewed_explicit" { - t.Fatalf("null interface_ref provenance = %#v", provenance) - } -} - -func hasCandidate(candidates []FieldCandidateProvenance, value, source, precedence string, selected bool) bool { - for _, candidate := range candidates { - if candidate.Value == value && candidate.Source == source && candidate.Precedence == precedence && candidate.Selected == selected { - return true - } - } - return false -} - -func writePrecedenceFixture(t *testing.T, root, commandPath string) { - t.Helper() - writeFixture(t, root, "skills/mono/SKILL.md", "# DWS\n") - writeFixture(t, root, "skills/mono/references/intent-guide.md", "# Intent guide\n") - writeFixture(t, root, "skills/mono/references/products/calendar.md", "# Calendar\n\n```bash\ndws "+commandPath+" --id example\n```\n") -} - -func precedenceOptions(root string, toolPaths map[string]string) Options { - return Options{ - Root: root, - SkillPath: "skills/mono/SKILL.md", - ProductsDir: "skills/mono/references/products", - IntentGuidePath: "skills/mono/references/intent-guide.md", - HintsDir: "internal/cli/schema_hints", - ToolPaths: toolPaths, - ProductIDs: map[string]bool{"calendar": true}, - SurfaceToolCount: 1, - } -} - -func hintFixture(kind, name, path, body string) string { - return fmt.Sprintf(`{ - "version": 1, - "source": {"kind": %q, "name": %q}, - "tools": {%q: %s} -}`, kind, name, path, body) -} diff --git a/internal/generator/cmd_schema_agent_metadata/coverage_more_test.go b/internal/generator/cmd_schema_agent_metadata/coverage_more_test.go index 64c477ee..2ee694ac 100644 --- a/internal/generator/cmd_schema_agent_metadata/coverage_more_test.go +++ b/internal/generator/cmd_schema_agent_metadata/coverage_more_test.go @@ -269,18 +269,8 @@ func TestCrossPlatformCoverageMetadataRegistryAndSelectionFailureEdges(t *testin root := t.TempDir() registry := commandRegistryProjection{CanonicalToolPaths: map[string]string{"sample.run": "sample run"}} - if err := validateSelectionHintInput(root, "hints", registry); err == nil || !strings.Contains(err.Error(), "required Agent hint directory missing") { - t.Fatalf("missing selection/ error = %v", err) - } - selection := filepath.Join(root, "hints", "selection") - if err := os.MkdirAll(selection, 0o755); err != nil { - t.Fatal(err) - } - if err := os.WriteFile(filepath.Join(selection, "sample.json"), []byte("{"), 0o644); err != nil { - t.Fatal(err) - } - if err := validateSelectionHintInput(root, "hints", registry); err == nil || !strings.Contains(err.Error(), "load selection") { - t.Fatalf("selection load error = %v", err) + if err := validateSelectionHintInput(root, "", registry); err == nil || !strings.Contains(err.Error(), "ProductDecl/ContractFinal selection coverage incomplete") { + t.Fatalf("missing ContractFinal coverage error = %v", err) } abs := filepath.Join(t.TempDir(), "absolute") diff --git a/internal/generator/cmd_schema_agent_metadata/main_test.go b/internal/generator/cmd_schema_agent_metadata/main_test.go index 60ac04fb..cff7953d 100644 --- a/internal/generator/cmd_schema_agent_metadata/main_test.go +++ b/internal/generator/cmd_schema_agent_metadata/main_test.go @@ -255,18 +255,14 @@ func TestValidateSelectionHintInputAllowsMissingWhenCoverageEmpty(t *testing.T) } } -func TestValidateSelectionHintInputRequiresHintDirsWhenCoverageRemains(t *testing.T) { +func TestValidateSelectionHintInputRequiresContractFinalWhenCoverageRemains(t *testing.T) { root := t.TempDir() registry := commandRegistryProjection{ CanonicalToolPaths: map[string]string{"sample.run": "sample run"}, ProductIDs: map[string]bool{"sample": true}, } err := validateSelectionHintInput(root, "", registry) - if err == nil || !strings.Contains(err.Error(), "HintsDir is empty") { - t.Fatalf("validateSelectionHintInput() error = %v", err) - } - err = validateSelectionHintInput(root, "internal/cli/schema_hints", registry) - if err == nil || !strings.Contains(err.Error(), "required Agent hint directory missing") { + if err == nil || !strings.Contains(err.Error(), "ProductDecl/ContractFinal selection coverage incomplete") { t.Fatalf("validateSelectionHintInput() error = %v", err) } }