diff --git a/AGENTS.md b/AGENTS.md index 6e55f7f2..2adafe31 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,7 +49,7 @@ organizational family, separate authority; do not merge with - Today: `helpers.LeafSpec` / `shortcut.Shortcut` → `corecmd.Spec` (+ optional `Contract`) → `corecmd.New` - **Declare = final Schema source**: `Flags` / `Constraints` / `Safety` / `ConstParams` / `Contract` (`corecmd.ContractDecl`; nested fields are `contract.*`) - Naming: `ContractDecl` is the authoring leaf declaration. "Schema" means Catalog / `ToolSpec` delivery — do not reintroduce `SchemaDecl`. -- `Safety` uses `contract.SafetySpec` (`internal/corecmd/contract` only — no `cli.*` type alias). Its `confirmation` drives the runtime gate; `effect` / `risk` / `idempotency` are published unchanged. When `Contract` is set, convert once via `contractfinal.RegisterRuntimeContractFinal` (framework) or `cli.RegisterRuntimeContractFinal` (product re-export); assembly **pass-throughs** Final. +- `Safety` uses `contract.SafetySpec` (`internal/corecmd/contract` only — no `cli.*` type alias). Its `confirmation` drives the runtime gate; `effect` / `risk` / `idempotency` are published unchanged. When `Contract` is set, convert once via `contractfinal.RegisterRuntimeContractFinal` (all callers — `corecmd.New` registers internally); assembly **pass-throughs** Final. - Package seam: - types / ProductDecl → `corecmd/contract` (DTO only; **no** Cobra-keyed ContractFinal store) - AnnotateRuntime* writers → `internal/corecmd/runtimeannotate` (framework-owned) diff --git a/docs/rfc-command-framework-convergence.md b/docs/rfc-command-framework-convergence.md index bf85ebe2..262b7a06 100644 --- a/docs/rfc-command-framework-convergence.md +++ b/docs/rfc-command-framework-convergence.md @@ -274,7 +274,7 @@ Definition(仅声明;不可编译) | 层 | 含义 | 今日落点 | |---|---|---| | **声明(declare)** | `corecmd.Spec` / `LeafSpec` / `ContractDecl` **数据字段**(声明证据;交付见下) | `Flags`/`Constraints`/`Risk`/`ConstParams`/`Contract`;类型真身在 `corecmd/contract`(DTO:`SafetySpec`/`ParamDecl`/`ProductDecl`/`ContractFinalPayload`;**无** Cobra store) | -| **框架转换** | 类型转换并注册(**禁止** JSON 注解桥) | `embedContractDecl` → `corecmd/contractfinal.RegisterRuntimeContractFinal`(annotate + store;产品代码经 `cli.RegisterRuntimeContractFinal` 薄 re-export) | +| **框架转换** | 类型转换并注册(**禁止** JSON 注解桥) | `embedContractDecl` → `corecmd/contractfinal.RegisterRuntimeContractFinal`(annotate + store;全部调用方直调,`corecmd.New` 内部注册) | | **注解 seam** | Cobra `dws.schema.*` 写入 | `internal/corecmd/runtimeannotate.AnnotateRuntime*`(框架侧;`cli` 根经 `runtime_schema_seam.go` 包内别名访问;`cli/runtimeannotate` 垫片包已删,一律直引 corecmd) | | **Schema 透传** / 交付 | 组装读取注册表,原样投影为 `ToolSpec`;`RegisterSchemaSourceRoot` → `ResolveSchemaBuild`(`ResolveMeta` 自同一组装投影);go:embed 仅限 reviewed 输入(registry / MCP meta / bindings 等),不得 embed Catalog | `internal/cli` 根(交付边界);ContractFinal store 在 `corecmd/contractfinal`(`cli` 根经 `runtime_schema_seam.go` 包内别名访问;`cli/contractfinal` 垫片包已删) | | **执行(execute)** | 钩子不发明表面 | `Validate` / `Call` / `RunE` / `PostMount` | diff --git a/internal/cli/runtime_schema_coverage_more_test.go b/internal/cli/runtime_schema_coverage_more_test.go index f3933cab..fb452c2a 100644 --- a/internal/cli/runtime_schema_coverage_more_test.go +++ b/internal/cli/runtime_schema_coverage_more_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contractfinal" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/runtimeannotate" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/testseam" "github.com/spf13/cobra" @@ -86,7 +87,7 @@ func TestCrossPlatformCoverageRuntimeSchemaMetadataLookupEdges(t *testing.T) { } leaf := &cobra.Command{Use: "reply"} - RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "sample", Name: "run", CanonicalPath: "sample.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", diff --git a/internal/cli/runtime_schema_seam.go b/internal/cli/runtime_schema_seam.go index 260d5954..4bb09b8d 100644 --- a/internal/cli/runtime_schema_seam.go +++ b/internal/cli/runtime_schema_seam.go @@ -58,13 +58,6 @@ var ( ApplyParamDecls = contractfinal.ApplyParamDecls ) -// RegisterRuntimeContractFinal is the sole production registration entry for -// product / helper / shortcut code (annotate + store). Framework code calls -// corecmd/contractfinal.RegisterRuntimeContractFinal directly. -func RegisterRuntimeContractFinal(cmd *cobra.Command, payload contract.ContractFinalPayload) { - contractfinal.RegisterRuntimeContractFinal(cmd, payload) -} - func resolvedFieldProvenance(value any, source, sourceRef, precedence, resolution, reviewReason string) contract.FieldProvenance { return contract.ResolvedFieldProvenance(value, source, sourceRef, precedence, resolution, reviewReason) } diff --git a/internal/cli/schema_agent_metadata.go b/internal/cli/schema_agent_metadata.go index ad0f88f0..e2006a83 100644 --- a/internal/cli/schema_agent_metadata.go +++ b/internal/cli/schema_agent_metadata.go @@ -34,11 +34,7 @@ import ( const ( ProvenanceEmbeddedSkillMetadata = "embedded-skill-metadata" ProvenanceEmbeddedMCPMetadata = "embedded-mcp-metadata" - // ProvenanceEmbeddedCommandCatalog is a legacy Catalog envelope source - // label retained for wire/compat recognition; runtime assembly stamps - // SchemaSourceRuntimeAssembled instead. - ProvenanceEmbeddedCommandCatalog = "embedded-command-catalog" - ProvenanceReviewedManual = "reviewed_manual" + ProvenanceReviewedManual = "reviewed_manual" ) type agentMetadata struct { diff --git a/internal/cli/schema_cobra_binding_coverage_more_test.go b/internal/cli/schema_cobra_binding_coverage_more_test.go index 5ba3ee4e..a9675f74 100644 --- a/internal/cli/schema_cobra_binding_coverage_more_test.go +++ b/internal/cli/schema_cobra_binding_coverage_more_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract" + "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contractfinal" "github.com/spf13/cobra" ) @@ -363,13 +364,13 @@ func TestCrossPlatformCoverageCompatibilityPathAndHelperRemainingEdges(t *testin // ContractFinal without Identity fails closed once a Contract is declared. noIdentity := &cobra.Command{Use: "run"} - RegisterRuntimeContractFinal(noIdentity, contract.ContractFinalPayload{Description: "x"}) + contractfinal.RegisterRuntimeContractFinal(noIdentity, contract.ContractFinalPayload{Description: "x"}) t.Cleanup(func() { ClearRuntimeContractFinalForTest(noIdentity) }) if err := validateCommandRegistryAnnotation(noIdentity, "sample run", spec); err == nil || !strings.Contains(err.Error(), "without Identity") { t.Fatalf("missing Identity error = %v", err) } mismatch := &cobra.Command{Use: "run"} - RegisterRuntimeContractFinal(mismatch, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(mismatch, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "other", Name: "run", CanonicalPath: "other.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", @@ -380,7 +381,7 @@ func TestCrossPlatformCoverageCompatibilityPathAndHelperRemainingEdges(t *testin t.Fatalf("Identity mismatch error = %v", err) } aligned := &cobra.Command{Use: "run"} - RegisterRuntimeContractFinal(aligned, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(aligned, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "sample", Name: "run", CanonicalPath: "sample.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", diff --git a/internal/cli/schema_cross_platform_coverage_remaining_test.go b/internal/cli/schema_cross_platform_coverage_remaining_test.go index 8596a0f4..ac9fb5ef 100644 --- a/internal/cli/schema_cross_platform_coverage_remaining_test.go +++ b/internal/cli/schema_cross_platform_coverage_remaining_test.go @@ -51,7 +51,7 @@ func TestCrossPlatformCoverageAgentExampleRemainingBranches(t *testing.T) { leaf := &cobra.Command{Use: "run", Run: func(*cobra.Command, []string) {}} leaf.Flags().String("name", "", "name") AttachRuntimeSchema(leaf, "sample", "run", "test") - RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "sample", Name: "run", CanonicalPath: "sample.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", @@ -515,7 +515,7 @@ func crossPlatformAgentSelectionBound(t *testing.T, mutate func(*cobra.Command, if mutate != nil { mutate(leaf, &payload) } - RegisterRuntimeContractFinal(leaf, payload) + contractfinal.RegisterRuntimeContractFinal(leaf, payload) t.Cleanup(func() { contractfinal.ClearRuntimeContractFinalForTest(leaf) contract.ClearProductDeclForTest("sample") @@ -721,7 +721,7 @@ func TestCrossPlatformCoverageSchemaRuntimeRegistryRemainingBranches(t *testing. }}) t.Cleanup(func() { contract.ClearProductDeclForTest("doc") }) create := root.Commands()[0].Commands()[0] - RegisterRuntimeContractFinal(create, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(create, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "doc", Name: "create_document", CanonicalPath: "doc.create_document", CLIPath: "doc create", PrimaryCLIPath: "doc create", @@ -872,7 +872,7 @@ func TestCrossPlatformCoverageAgentExamplePlanAndTokenizerRemaining(t *testing.T leaf := &cobra.Command{Use: "run", Run: func(*cobra.Command, []string) {}} leaf.Flags().String("name", "", "name") AttachRuntimeSchema(leaf, "sample", "run", "test") - RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "sample", Name: "run", CanonicalPath: "sample.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", diff --git a/internal/cli/schema_cross_platform_coverage_test.go b/internal/cli/schema_cross_platform_coverage_test.go index d3e5dcc2..3c971c53 100644 --- a/internal/cli/schema_cross_platform_coverage_test.go +++ b/internal/cli/schema_cross_platform_coverage_test.go @@ -321,7 +321,7 @@ func crossPlatformAgentExampleFixture(t *testing.T, mutate func(*cobra.Command, if mutate != nil { mutate(leaf, &payload) } - RegisterRuntimeContractFinal(leaf, payload) + contractfinal.RegisterRuntimeContractFinal(leaf, payload) t.Cleanup(func() { contractfinal.ClearRuntimeContractFinalForTest(leaf) contract.ClearProductDeclForTest("sample") diff --git a/internal/cli/schema_runtime_registry_contract_test.go b/internal/cli/schema_runtime_registry_contract_test.go index 1129b5e6..d453e317 100644 --- a/internal/cli/schema_runtime_registry_contract_test.go +++ b/internal/cli/schema_runtime_registry_contract_test.go @@ -138,7 +138,7 @@ func TestCrossPlatformCoverageAssembleSchemaRegistryFailClosedMissingProductDecl root := &cobra.Command{Use: "dws"} leaf := &cobra.Command{Use: "run", Short: "Run", Run: func(*cobra.Command, []string) {}} AttachRuntimeSchema(leaf, "orphan", "run", "test") - RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "orphan", Name: "run", CanonicalPath: "orphan.run", CLIPath: "orphan run", PrimaryCLIPath: "orphan run", @@ -193,7 +193,7 @@ func TestAssembleSchemaRegistryRequiresContractFinalAndProductDecl(t *testing.T) root := &cobra.Command{Use: "dws"} leaf := &cobra.Command{Use: "run", Short: "Run sample", Long: "Run the sample tool", Run: func(*cobra.Command, []string) {}} AttachRuntimeSchema(leaf, "sample", "run", "test") - RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "sample", Name: "run", CanonicalPath: "sample.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", @@ -328,7 +328,7 @@ func assembleContractFinalTextTool(t *testing.T, short, long, declaredDescriptio Run: func(*cobra.Command, []string) {}, } AttachRuntimeSchema(leaf, "sample", "run", "test") - RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(leaf, contract.ContractFinalPayload{ Identity: &contract.ToolIdentitySpec{ ProductID: "sample", Name: "run", CanonicalPath: "sample.run", CLIPath: "sample run", PrimaryCLIPath: "sample run", diff --git a/internal/corecmd/contract/doc.go b/internal/corecmd/contract/doc.go index 95e6e042..f6c0e542 100644 --- a/internal/corecmd/contract/doc.go +++ b/internal/corecmd/contract/doc.go @@ -31,8 +31,7 @@ // - AnnotateRuntime* writers → internal/corecmd/runtimeannotate // (framework-owned; cli may thin re-export; corecmd must not import cli). // - ContractFinal cobra store + Register seam → internal/corecmd/contractfinal -// (framework calls RegisterRuntimeContractFinal here; product code uses -// cli.RegisterRuntimeContractFinal re-export). +// (all callers register here directly; corecmd.New registers internally). // - Catalog assembly / ResolveMeta (`RegisterSchemaSourceRoot` → // `ResolveSchemaBuild`); go:embed only for reviewed inputs → internal/cli // (delivery root). diff --git a/internal/corecmd/contract/final.go b/internal/corecmd/contract/final.go index b86ab01e..afa44364 100644 --- a/internal/corecmd/contract/final.go +++ b/internal/corecmd/contract/final.go @@ -20,9 +20,9 @@ package contract // // The Cobra-keyed runtime store and Register live in // internal/corecmd/contractfinal (not this DTO package). AnnotateRuntime* -// writers live in internal/corecmd/runtimeannotate. Production product code -// registers through cli.RegisterRuntimeContractFinal (thin re-export); -// framework code calls contractfinal.RegisterRuntimeContractFinal directly. +// writers live in internal/corecmd/runtimeannotate. All callers register via +// contractfinal.RegisterRuntimeContractFinal directly (corecmd.New registers +// internally). type ContractFinalPayload struct { Title string Description string diff --git a/internal/corecmd/contract_register_seam_test.go b/internal/corecmd/contract_register_seam_test.go index a19c9dd8..bb4930a8 100644 --- a/internal/corecmd/contract_register_seam_test.go +++ b/internal/corecmd/contract_register_seam_test.go @@ -38,9 +38,8 @@ func TestAttachContractUsesContractFinalRegisterSeam(t *testing.T) { if !strings.Contains(body, "contractfinal.RegisterRuntimeContractFinal(") { t.Fatal("AttachContract/New must call contractfinal.RegisterRuntimeContractFinal") } - if strings.Contains(body, "cli.RegisterRuntimeContractFinal(") { - t.Fatal("corecmd must not call cli.RegisterRuntimeContractFinal; use contractfinal seam") - } + // Registration goes through contractfinal directly; no cli-root wrapper + // exists anymore, and the import-prefix check below forbids corecmd → cli. // Build the forbidden import prefix without embedding it as a contiguous // literal in this test file (the walker below must not self-match). forbidden := strings.Join([]string{ diff --git a/internal/corecmd/contractfinal/doc.go b/internal/corecmd/contractfinal/doc.go index 9389774e..90fced83 100644 --- a/internal/corecmd/contractfinal/doc.go +++ b/internal/corecmd/contractfinal/doc.go @@ -14,10 +14,9 @@ // Package contractfinal owns the Cobra-keyed ContractFinal runtime store and // the annotate+store registration seam. // -// Production product / helper / shortcut code should call -// cli.RegisterRuntimeContractFinal (thin re-export). Framework code -// (corecmd.AttachContract) calls RegisterRuntimeContractFinal here directly -// so internal/corecmd never imports any internal/cli package. +// All registration — products, helpers, shortcuts, and framework code +// (corecmd.AttachContract) — calls RegisterRuntimeContractFinal here directly. +// internal/corecmd never imports any internal/cli package. // // Types remain in internal/corecmd/contract (DTO only — no cobra store). // Annotate writers live in internal/corecmd/runtimeannotate. diff --git a/internal/corecmd/contractfinal/store.go b/internal/corecmd/contractfinal/store.go index fe31376f..b297aff4 100644 --- a/internal/corecmd/contractfinal/store.go +++ b/internal/corecmd/contractfinal/store.go @@ -27,9 +27,8 @@ var contractFinalByCommand sync.Map // *cobra.Command → *contract.ContractFina // RegisterRuntimeContractFinal annotates dws.schema.contract then stores the // typed final Schema overlay. This is the atomic annotate+store implementation. // -// Ownership lives under the command framework (this package). Product code -// should call cli.RegisterRuntimeContractFinal (thin re-export); -// corecmd.AttachContract calls this function directly. +// Ownership lives under the command framework (this package). All callers — +// products and corecmd.AttachContract alike — call this function directly. func RegisterRuntimeContractFinal(cmd *cobra.Command, payload contract.ContractFinalPayload) { if cmd == nil { return diff --git a/internal/corecmd/corecmd.go b/internal/corecmd/corecmd.go index 1f0dadcd..5594a637 100644 --- a/internal/corecmd/corecmd.go +++ b/internal/corecmd/corecmd.go @@ -1211,8 +1211,7 @@ func embedContractDecl(cmd *cobra.Command, spec Spec) { // // Production registration always goes through contractfinal.RegisterRuntimeContractFinal // (annotate + store). Do not call the store/Register APIs except via -// contractfinal (framework) or the cli.RegisterRuntimeContractFinal re-export -// (product code). +// contractfinal — no cli-root wrapper exists. // // Title/Description stored on the payload are the declared Contract values // only. Catalog assembly may prefer Cobra Long for delivered description diff --git a/internal/generator/agentmetadata/contract_final_coverage_test.go b/internal/generator/agentmetadata/contract_final_coverage_test.go index b1ab24be..5847425e 100644 --- a/internal/generator/agentmetadata/contract_final_coverage_test.go +++ b/internal/generator/agentmetadata/contract_final_coverage_test.go @@ -44,7 +44,7 @@ func TestCrossPlatformCoverageContractFinalSkipsWithoutOverlay(t *testing.T) { func TestCrossPlatformCoverageContractFinalMissingCanonicalProjection(t *testing.T) { declared := &cobra.Command{Use: "run"} - cli.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{ Selection: &contract.SelectionSpec{ AgentSummary: "Declared", UseWhen: []string{"use"}, @@ -142,7 +142,7 @@ func TestCrossPlatformCoverageContractFinalProductDeclMergeConflicts(t *testing. func TestCrossPlatformCoverageContractFinalToolMergeConflict(t *testing.T) { declared := &cobra.Command{Use: "run"} - cli.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{ Selection: &contract.SelectionSpec{ AgentSummary: "Declared summary", UseWhen: []string{"use declared"}, diff --git a/internal/generator/agentmetadata/metadata_helpers_coverage_test.go b/internal/generator/agentmetadata/metadata_helpers_coverage_test.go index dc2121f6..24904233 100644 --- a/internal/generator/agentmetadata/metadata_helpers_coverage_test.go +++ b/internal/generator/agentmetadata/metadata_helpers_coverage_test.go @@ -577,7 +577,7 @@ func TestCrossPlatformCoverageMetadataParserConflictEdges(t *testing.T) { func TestCrossPlatformCoverageContractFinalDeclarationFailureEdges(t *testing.T) { declared := &cobra.Command{Use: "run"} - cli.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{ + contractfinal.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{ Selection: &contract.SelectionSpec{AgentSummary: "declared summary"}, }) t.Cleanup(func() { contractfinal.ClearRuntimeContractFinalForTest(declared) }) 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 6d0a97d0..32e077c3 100644 --- a/internal/generator/cmd_schema_agent_metadata/coverage_more_test.go +++ b/internal/generator/cmd_schema_agent_metadata/coverage_more_test.go @@ -283,7 +283,7 @@ func TestCrossPlatformCoverageMetadataRegistryAndSelectionFailureEdges(t *testin func TestCrossPlatformCoverageSelectionInputExemptsDeclaredTools(t *testing.T) { declared := &cobra.Command{Use: "run"} - cli.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{}) + contractfinal.RegisterRuntimeContractFinal(declared, contract.ContractFinalPayload{}) t.Cleanup(func() { contractfinal.ClearRuntimeContractFinalForTest(declared) }) registry := commandRegistryProjection{