Break the remaining corecmd→cli reverse dependency by owning runtimeannotate and contractfinal on the framework side, with cli keeping thin re-exports. Document the three authoring tiers and that Shortcut may use DeclareLeafMetadata. Co-authored-by: Cursor <cursoragent@cursor.com>
24 KiB
Repository Agent Guide
This file applies to the entire repository. Keep changes scoped, preserve
unrelated work, and use gofmt for every modified Go file.
Build and test
- Build:
go build ./cmd - Full test suite:
DWS_PACKAGE_VERSION=0.0.0-test go test ./... - Generate Schema assets:
go generate ./internal/cli(entry point:internal/cli/gen.go) - Refresh pinned MCP metadata:
make fetch-mcp-metadata(requiresdws auth login) - Check generated drift:
./scripts/policy/check-generated-drift.sh - Check the Schema contract:
./scripts/policy/check-schema-catalog.sh
Generated Schema JSON is committed under schema_catalog/ (ToolSpec wire) and
schema_meta_index.json (CommandMeta summary for ResolveMeta), both
go:embed. Change source inputs and generators, then regenerate; do not
hand-edit generated Catalog or meta-index files. schema_agent_metadata/ is retired: if that directory
(or schema_agent_metadata_audit.json) is present, policy fails.
internal/cli/schema_command_registry.json is different: it is a reviewed
CommandRegistry source, not a generated snapshot. It is the single reviewed
source of stable canonical identity,
primary paths, aliases, and navigation. Edit it only when reviewed exposure,
identity, primary path, or aliases change; parameter, Skill, and metadata-only
changes must not rewrite it mechanically.
Command framework declaration
- Framework definition:
docs/rfc-command-framework-convergence.md§5.0 - Today:
helpers.LeafSpec/shortcut.Shortcut→corecmd.Spec(+ optionalContract) →corecmd.New - Declare = final Schema source:
Flags/Constraints/Safety/ConstParams/Contract(corecmd.ContractDecl; nested fields arecontract.*) - Naming:
ContractDeclis the authoring leaf declaration. "Schema" means Catalog /ToolSpecdelivery — do not reintroduceSchemaDecl. Safetyusescontract.SafetySpec(internal/corecmd/contractonly — nocli.*type alias). Itsconfirmationdrives the runtime gate;effect/risk/idempotencyare published unchanged. WhenContractis set, convert once viacontractfinal.RegisterRuntimeContractFinal(framework) orcli.RegisterRuntimeContractFinal(product re-export); assembly pass-throughs Final.- Package seam:
- types / ProductDecl →
corecmd/contract(DTO only; no Cobra-keyed ContractFinal store) - AnnotateRuntime* writers →
internal/corecmd/runtimeannotate(framework-owned) - ContractFinal cobra store + Register →
internal/corecmd/contractfinal(framework-owned) - homology gates →
internal/cli/homology - Catalog /
ResolveMeta/ go:embed →internal/cliroot (thin re-exports of annotate/store APIs;cli/runtimeannotate+cli/contractfinalare thin re-exports only) - Hard rule:
internal/corecmd(and its subpackages) must not import anyinternal/clipackage
- types / ProductDecl →
- Authoring tiers (current, not aspirational):
- Tier1 —
corecmd.New/NewLeafCommand(fully managed declare + execute) - Tier2 —
DeclareLeafMetadata(helpers migration; Shortcut may also use this path — acceptable) - Tier3 — bare Cobra (should shrink over time; reviewed exclusions where needed)
- Long-term outlook only: broader mcpbind / fewer hand-written
Executebodies. Not a current hard requirement to deleteShortcut.Executeor force mcpbind.
- Tier1 —
- Description declare vs delivery: construction requires
ContractDecl.Description(evidence). Catalog delivery prefers Cobra Long → provenancecobra_help; without Long, declared text →contract_final. Title: declared first, then Short, then MCP. Do not read this as "declare = wire final" or dual authority. - Execute = hooks (
Validate/Call/RunE/PostMount) — not a second surface authority - Declaration path has no reviewed parallel fields; migration-only
runtime_gateannotate untilSafetyis declared
flag / help / schema homology
- Decision (path A — Contract/LeafSpec is CLI-surface authority and must embed into Schema):
docs/flag-help-schema-homology.md - Hard rule: every help/Schema fact is declared or annotated; never inference-only (§1.1–§1.3; framework §5.0).
- Embed path:
corecmd.New→dws.schema.*annotations → Schema catalog assembly - MCP metadata must not create CLI flags; optional 1:1 passthrough is a gated subset only.
- Gate IDs:
HOM-P*,HOM-S*,HOM-I1,HOM-D1(see that doc §3–§4).HOM-P1/HOM-D1/HOM-S1/HOM-S2are on thecheck-schema-catalog.shpolicy whitelist; remaining IDs land incrementally.
Agent Schema contract
The Schema data flow is one way:
1. app.NewRootCommand()
└─ builds the real Cobra command tree and flags
└─ leaf Safety / Contract / contract.ParamDecl declare ContractFinal (declare-or-annotate)
2. schema_command_registry.json
└─ forms EffectiveCommandRegistry
└─ binds exactly to real Cobra leaves and aliases
3. Parameter resolution
Cobra flags
+ schema_parameter_bindings.json
+ contract.ParamDecl / native annotations from leaf declarations
└─ produces ParameterSpec and constraints
4. Agent and interface semantics
ProductDecl + leaf ContractFinal Selection / Safety / Interface
+ pinned MCP metadata
└─ resolves Agent metadata by source precedence
Markdown is evidence only; it is not concatenated into final prose
└─ schema_hints/ is fully retired (must not reappear); not a leaf-fact source
5. One typed hub
BoundCommandRegistry
+ ParameterSpec
+ Agent metadata
+ Interface metadata
└─ resolves every command exactly once into ToolSpec
└─ aggregates SchemaRegistry + SchemaIndex
6. One-way publication
SchemaRegistry
└─ internal/cli/schema_catalog/
(catalog.json + tools/<product>.json; split per product so
concurrent feature PRs only rewrite their own shard)
└─ internal/cli/schema_meta_index.json
(CommandMeta summary for ResolveMeta / leaf --help Safety)
└─ dws schema list/product/group/leaf/--all
7. Runtime consumption (unified API)
ResolveMeta(cliPath) → CommandMeta{Identity, Safety, Selection}
└─ internal/cli/command_meta.go
└─ help / agent / skill-gen call this one function
└─ backed by embedded schema_meta_index.json (sync.Once, O(1))
└─ full ToolSpec / dws schema still use embedded schema_catalog/
After binding there is no second identity source and no identity precedence winner. The binder must reject a missing/non-runnable Cobra path, an alias collision, and any native identity annotation that disagrees with the effective registry. A missing native identity annotation is allowed because annotations are implementation-side assertions, not identity fallbacks.
The assembler resolves every bound command exactly once into one ToolSpec.
Build-time gates and the snapshot serializer consume that source-resolved typed
registry/index. Runtime projections and delivery gates consume the typed
registry/index returned by the production snapshot loader. Neither path may
reopen annotations, merge source records, or use a previous Catalog or other
generated JSON as a source. schema_catalog/ (catalog.json + per-product
tools/.json shards) is output-only in the
generation graph. The production loader decoding the embedded published
snapshot is a delivery boundary, not source resolution; it must never create or
repair a Cobra command, flag, registry entry, or later Catalog generation.
Generation vs consumption separation
The Schema system has two physically separated processes:
Generation (build-time, slow, reviewed, one-way):
- Entry point:
internal/cli/gen.go(all//go:generatepragmas isolated here, not in business code). - Tools:
cmd_schema_catalog+cmd_param_aliases(standalone Go mains via//go:generate). Catalog generation injects Agent metadata in-memory (agent_metadata_inject.go/InstallBuildTimeAgentMetadataJSON); it does not writeschema_agent_metadata/.cmd_schema_agent_metadatamay remain as a non-delivery helper/test binary and is not ago:generateentry. - Inputs: authored source groups (registry + ProductDecl/ContractFinal + MCP
metadata + parameter bindings + reviewed parameter concepts + cobra tree).
schema_hints/is fully retired and must not reappear. - Output (delivery):
schema_catalog/(per-product shards,go:embed) +schema_meta_index.json(CommandMeta summary) +param_aliases_generated.go.schema_agent_metadata/must not reappear. - Refresh MCP metadata:
make fetch-mcp-metadata(iterates 26 MCP server endpoints, merges with previous data for cross-server interface_ref). - Gates:
make generate-schema(byte guards on inputs),check-generated-drift.sh,check-command-surface.sh(catalog structure).
Consumption (runtime, fast, read-only, unified API):
- Entry point:
ResolveMeta(cliPath) → CommandMeta{Identity, Safety, Selection}ininternal/cli/command_meta.go. - Backed by embedded
schema_meta_index.json(sync.Once, O(1) map). FullembeddedSchemaCatalog()loads only fordws schema/--all/ ToolSpec. - Consumers:
--help(Safety annotation viaRenderSafetyAnnotation), agent selection, future skill generation;dws schemauses the Catalog. SafetyForCLIPathdelegates toResolveMeta(backward compatible).
This split is architecturally isomorphic to Lark's typed metadata registry, navigation catalog, and schema renderer. DWS intentionally preserves its existing flat JSON wire contract for compatibility; do not treat architectural alignment as permission to make an unversioned wire-format change.
The reviewed CommandRegistry is the sole source of stable command identity
and navigation. The executable Cobra tree remains the source of truth for
whether a CLI path exists, is runnable, and which flags it accepts. Schema
coverage is bidirectional:
- Every final
SchemaRegistrytool, including its serialized Catalog projection, must resolve to an executable Cobra command. - Every public runnable Cobra leaf must either resolve to Schema or appear as
an exact, reviewed exclusion with a non-empty reason in
internal/cli/schema_command_exclusions.json.
Do not use prefix or wildcard exclusions: they can silently hide future commands. Remove an exclusion when its command enters Schema; stale, invalid, or duplicate exclusions must fail generation and CI.
When adding or changing an Agent-visible command, review all relevant inputs:
internal/cli/schema_command_registry.jsonfor the reviewedCommandRegistry: canonical identity, primary CLI path, aliases, and stable navigation. It is the identity source and is not a generated artifact.internal/cli/schema_command_registry.schema.jsonis its closed, machine-readable editing contract. Preserve the local$schemareference; unknown fields, invalid visibility values, stale paths, and collisions fail Go validation and policy.- Leaf
Safety/Contract(corecmd.ContractDecl) /contract.ParamDecl(helpersLeafSpecor shortcutContract) for parameter facts, interface disposition, safety, and Agent selection prose. Delivered provenance iscontract_finalfromcorecmd.contract(description may stampcobra_helpwhen Cobra Long wins). Product routing usesProductDecl(internal/corecmd/contract; provenance label remainscli.product_decl). internal/cli/schema_hints/is fully retired. Do not reintroduce HintFiles, audit JSON, orimported/baselines; declare on ProductDecl / the owning leaf instead.- Native Runtime Schema identity annotations, when present, as consistency
assertions against
EffectiveCommandRegistry. They must agree exactly and must never materialize, infer, or override registry identity. - Flag-to-interface property mappings and required/default semantics.
- Generated delivery under
internal/cli/schema_catalog/(catalog.json + tools/.json) after running generation. Do not expect or commitschema_agent_metadata/.
Run the reverse-completeness tests whenever the Cobra tree changes. A command
that works through dws <path> but cannot be found through the matching
dws schema lookup is a contract failure unless it has a reviewed exact
exclusion.
RegisterSchemaHints / ToolSchemaHint overlays are fully removed. Parameter
and selection facts must be declared on the owning leaf (contract.ParamDecl /
Contract) or via ProductDecl; do not reintroduce overlay registries.
For Agent-authored selection edits:
- Confirm the exact command and flag names in the current Cobra tree.
- Declare selection prose on the owning leaf (
Contract.Selection/DeclareLeafMetadata) and product routing viaProductDecl; declare safety / parameters / interface on the same leaf. - Do not copy generated Catalog fields into source inputs.
- Run generation, drift, Schema policy, and the focused CLI tests before proposing the change.
Agent curation workflow
Use this workflow when refreshing Agent selection prose and confirmation alignment. Prefer agent-authored review over bulk merge scripts that dump Skill Markdown into Catalog fields.
Human-authored inputs:
| Block | Path | Owns |
|---|---|---|
| declaration | helpers / shortcut Safety + Contract / contract.ParamDecl + ProductDecl |
effect / risk / confirmation / idempotency / interface_* / parameter facts / selection prose (contract_final) |
schema_hints/ is fully retired. Do not reintroduce HintFiles or audit JSON.
Goals
- Selection prose is decision-oriented (Feishu/Lark style): trigger intent,
sibling-command routing, and outcome shape — not a restatement of the
summary. Delivered Catalog provenance is
contract_finalfrom leafContract.Selection/ProductDecl. - Safety follows Runtime:
confirmation=user_requiredwhen the leaf Contract/Safety (or remainingruntime_gateannotate) requires a user gate (for exampleconfirm_delete,typed_yes,confirm_dangerous). - Parameter facts are declared on the leaf (
contract.ParamDecl/Contract.Parameters/ FlagSpec). Do not reintroduce HintFile orRegisterSchemaHintsoverlays.
Authoring
For every curated tool:
- Declare safety/interface/parameters/selection on the owning leaf
(
DeclareLeafMetadata/Shortcut.Contract/contract.ParamDecl) and product routing viaProductDeclwhen needed. - Run
make generate-schema. Do not hand-edit generatedschema_catalog/.
Pull live MCP descriptions (personal token)
Pinned internal/cli/schema_mcp_metadata.json is a sanitized baseline. Prefer
live Schema from a logged-in personal session:
dws auth status # token_valid should be true
dws cache refresh # refresh discovery / tools cache
dws schema <mcp-canonical> -f json
# or CLI path: dws schema --cli-path "drive copy" -f json
Resolve MCP identity via interface_ref when CLI canonical ≠ MCP path
(example: CLI drive.copy_document → live doc.copy_document). On pull
failure, fall back to Skill + Cobra Help + pinned MCP, and record evidence
(for example live-dws-schema:<path>#FAILED). Never print or commit tokens.
Precedence when sources disagree: Runtime/Cobra > live MCP > pinned MCP > Skill (evidence only).
Parallel product agents
Split work by product groups. Each agent must:
- Read Skill, Cobra/
--help, Runtime confirmation sites, and livedws schemafor its tools. - Hand-write selection prose and leaf Contract / ProductDecl declarations; forbid wholesale JSON merges from review dumps.
- Edit only its product’s leaf declarations (and
ProductDeclwhen needed). - Never
git checkoutunrelated product files to “clean scope”.
Regenerate and gates
make generate-schema
./scripts/policy/check-runtime-confirmation-truth.sh
go test ./internal/app -run '^TestSheetFinalSchemaConfirmationMatchesRuntimeGuards$' -count=1
check-runtime-confirmation-truth.sh compares Catalog
confirmation=user_required to Contract SafetySpec only.
schema_hints/ must stay absent.
Example rules (fail generation otherwise):
- At most two examples per tool; no
--yesin stored examples. - Examples must match live Cobra argv (path, flags, required groups).
- No shell comments in examples.
After generation, spot-check Catalog: selection and safety/interface
provenance are contract_final from ProductDecl / leaf declarations
(user_required must match Runtime confirmation gates).
make generate-schema is a full deterministic snapshot rebuild, not an
incremental patch over the previous Catalog. It rereads every reviewed input,
removes stale generated product metadata, and rewrites the exact metadata and
Catalog projections. Incremental work happens only when an Agent or human
edits selected leaf / ProductDecl declarations; the next publication
still recomputes all outputs. Generated files must never be read back as merge
input, and byte guards fail generation if it changes the hint inputs or
CommandRegistry.
Selection prose may choose a more or less restrictive recommendation. It cannot
create a Cobra command or flag, change parameter facts, invent an
RPC/interface, alter safety metadata, or bypass command completeness. Examples
must use an executable primary/alias path and flags accepted by the live Cobra
command; never add --yes to stored examples.
Every example is always checked against its real BoundCommand: exact path,
accepted flags, Cobra required flags/positionals, and the effective
require_one_of, require_together, and mutually_exclusive constraints must
all pass before execution eligibility is considered. A missing required value,
constraint failure, runtime error, or MCP resolution error is a contract bug;
none is a valid reason to skip an example.
Example execution defaults to contract validation only. Runtime execution is
opt-in: an example enters dry_run only when its final ToolSpec publishes an
explicit reviewed dry-run capability. The test never injects --yes, and
risk/confirmation values do not manufacture preview support. A narrow
runtime precondition that cannot be derived from the typed contract may use an
exact zero-based example_dispositions entry with mode=contract_only,
reviewed=true, one of the schema-enumerated reason codes, and a concrete
non-empty reason. Such a disposition may only narrow an explicit dry-run
capability; it cannot turn an ordinary contract-only example into a skip.
Duplicate, missing, and out-of-range indexes fail validation. Never catch a
dry-run failure and dynamically downgrade it to contract_only.
Normal Go tests run the exhaustive contract gate. Run
make test-schema-agent-examples to additionally execute the eligible subset
through the real Cobra --dry-run path with isolated HOME and blocked proxies.
The test reports stable total, contract, dry_run, contract_only,
reviewed_manual, and per-reason counts; changing those counts requires a
review of the corresponding typed dry-run capability or manual disposition.
This target is also part of make policy.
Treat every tool use_when entry as a reviewed positive selection scenario
whose expected result is that tool's canonical path, and every avoid_when
entry as a reviewed negative scenario that must not choose that tool. The
deterministic gate derives a typed evaluation fixture from these same fields;
it requires exact tool coverage, a real runnable BoundCommandRegistry
primary command, at least one positive and negative assertion per tool, and no
literal contradictory expectations. It does not claim that string matching
proves natural-language understanding.
Semantic selection is an explicit opt-in live-model check. Run the smoke set
(one positive and one negative scenario per product) with
DWS_AGENT_SELECTION_LIVE=1 ARK_API_KEY=... ARK_BASE_URL=... ARK_MODEL=... go test ./internal/app -run TestManualAgentSelectionArkLive -count=1.
Add DWS_AGENT_SELECTION_FULL=1 to evaluate every committed tool scenario, or
set DWS_AGENT_SELECTION_CASES to comma-separated fixture case IDs. Normal CI
never calls a model; its blockers remain the reproducible fixture, binding,
example, provenance, and final-delivery facts.
The live evaluator sends only case IDs/scenarios plus one same-product
candidate table; expected/forbidden assertions stay local and must never be
included in the model prompt. Built-in Ark HTTPS bases are allowlisted. A
different HTTPS provider requires its exact base in
DWS_AGENT_SELECTION_ALLOWED_BASE_URLS; plaintext HTTP is accepted only for a
loopback test server so API credentials are never sent to an arbitrary clear
text endpoint.
Safety metadata
Parameter and safety resolution is mostly source-precedence based and
value-neutral: do not choose a winner because one value looks stricter. A
higher-priority reviewed metadata/explicit source may intentionally raise or
lower description, mapping, effect, risk, confirmation, or idempotency.
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 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-level description: declare required, delivery Long may win.
ContractDecl.Description is mandatory at construction (declaration evidence).
Catalog delivery prefers Cobra Long when present (provenance cobra_help,
resolution cobra_help_preferred); without Long, the declared Description is
delivered as contract_final. Title keeps declared ContractDecl /
ContractFinal first, then Cobra Short, then MCP metadata. This is one authority
chain with an explicit delivery preference — not two competing sources.
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,
count, presence, or hash is not a sufficient final-delivery invariant.
The same resolved ToolSpec must drive every projection. The full leaf payload
must equal the corresponding tool in schema --all and the full Catalog tool.
Overview/product/group summaries and Catalog summaries must equal
ToolSpec.ToSummaryPayload(). An alias lookup may change only the view fields
cli_path and is_alias; it must not re-resolve or mutate the command
contract.
This build-time rule is distinct from runtime drift handling. If shipped Help and leaf Schema disagree, pass only flags accepted by Cobra. For conflicting safety information, do not silently take the less restrictive behavior: use the safer interpretation or stop and report the contract drift.
Do not infer one safety field from another. In particular, effect=destructive
or risk=high does not mechanically rewrite confirmation; the final
precedence winner for each field is authoritative. When
confirmation=user_required, obtain confirmation before adding --yes.
Keep CLI confirmation behavior and Schema metadata consistent, and add a
semantic regression test through the final embedded loader/query delivery
path; a generator unit test or JSON count alone is insufficient.
Current Schema boundaries
schema listremains a progressive overview.schema --allis the stable full-export contract: every finalSchemaIndextool must contain its complete leaf parameters, constraints, and safety semantics, including an emptyparametersobject for commands without flags. Keep it suitable for the #602 compatibility baseline and fail rather than silently emitting a partial export.schema --allis not normal command discovery. Use overview -> product/group -> leaf for routine Agent work.--compactis supported for context-saving projections, but a compact full export is not a complete compatibility baseline.dws <path> --helpdefines whether Cobra exposes a path and which flags the executable accepts. A leaf Schema defines Agent selection, parameter mapping and constraints, and safety/confirmation semantics. A conflict is contract drift, not permission to guess.- Schema and Help describe commands; neither returns DingTalk business data. After discovery, execute the real read/search/list command to obtain data.