Files
dingtalk-workspace-cli/docs/flag-help-schema-homology.md
玉澜 bf2c0653ed docs: record Input in the flag/help/schema homology field table
Fourth-review fix: the FlagSpec sub-field table in the homology doc is
the named authority for "what each field does and whether it reaches
Schema parameters", and RFC §5.0.2 asserts declaration fields embed into
dws.schema.*. Input satisfied neither entry, leaving its deliberate
non-projection indistinguishable from an oversight. Add the table row and
the §5.0.2 exception note so the capability stays a declared fact (Usage
prose) rather than inviting an invented annotation.
2026-08-15 12:29:26 +08:00

271 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# flag / help / schema 同源
- **状态**:已决策(路径 A + Contract 嵌入 Schema)
- **相关**:[`rfc-command-framework-convergence.md`](rfc-command-framework-convergence.md)、[`schema-dynamic-endpoint-design.md`](schema-dynamic-endpoint-design.md)
- **实现门面**:`LeafSpec` → `corecmd.Spec` → `corecmd.New`
## 1. 决策
采用 **路径 A:Contract / LeafSpec 为 CLI 表面权威**,并且 **Contract 必须嵌入进 Schema**。
「同源」的含义是:同一份 Contract(今日经 LeafSpec / `corecmd.Spec` 表达)同时决定——且门禁能证明——
1. cobra 实际注册的 flags / required / defaults;
2. `--help` 的 Flags 与「参数约束」段;
3. 运行时**组装后的** Schema 交付中的 parameters、关系约束和 SafetySpec:
- **SchemaRegistry / Catalog ToolSpec wire** 供 `dws schema` / `--all` / 完整 leaf 载荷(lazy;首次 `dws schema` 触发完整装配)。
- **`ResolveMeta` / `SafetyForCLIPath` / leaf `--help` Safety** 与装配同源:`deliverySchemaCatalog` sync.Once 装配后同步物化 `map[cli_path]CommandMeta`;稳态为 O(1) map lookup,不为 Meta 单独重做全量 ToolSpec/wire 投影。
Agent metadata 在组装期间经内存 inject,不落盘、不 embed;`schema_agent_metadata/` 已退役,若存在则 policy 失败。
嵌入机制(已落地):
```text
corecmd.Spec
→ RegisterFlags + embedContractIntoSchema
→ cobra annotations:
dws.schema.contract=command
dws.schema.property / type / required (per flag)
dws.schema.constraints
→ RegisterRuntimeContractFinal(SafetySpec + ContractDecl)
→ Schema 组装透传 Contract Final
(组装时内存 inject Agent metadata)
→ RegisterSchemaSourceRoot → ResolveSchemaBuild
→ SchemaRegistry (+ Meta cache map) / dws schema wire projection
```
command/Leaf 不再写 `dws.schema.risk`;SafetySpec 走类型化 Final 载荷,不使用字符串枚举注解。
### 1.1 硬规则:声明 = 最终数据源(Schema 透传)
受管命令进入 Schema 的叶子数据由 **Contract 声明**定义最终值;框架(`corecmd.New`)做**类型转换**并注册,Schema 组装**透传**,不得:
- 把声明序列化成 JSON 注解再解析;
- 在声明体系里再挂「评审字段」并行权威;
- 用 hints/registry 盖写已声明字段。
**declare-vs-delivery 例外(Title / Description)**:构造期 `ContractDecl.Description` **必填**(声明证据),但这不是「declare = wire 最终值」。Catalog **交付**时:
- **description**:有 Cobra Long → 交付 Long(provenance `cobra_help` / `cobra_help_preferred`);无 Long → 交付声明(`contract_final`)。**Short 不进入 description**。
- **title**:声明 `ContractDecl.Title` 优先,否则 Cobra Short,再 MCP;组装 stamp 真实 winner。
这是一条权威链上的显式交付偏好,不是双权威(见 RFC §5.0.4)。
迁移期未迁完的叶子可暂走旧组装路径;**新声明面不含 review_reason / reviewed 字段**。写命令未设 `Safety` 时,过渡期仍可用 `runtime_gate` annotate(`HOM-S2`)。
**不采用**路径 B(以 MCP meta / 已退役的 `schema_mcp_metadata` 生成全部 CLI flag/help/schema)作为主权威。钉钉 MCP meta 不是飞书 OAPI:粒度与 CLI 特有语义(二选一、OmitEmpty、ConstParams、write guard)无法从裸 meta 推出;强行生成会违反「Schema 描述 CLI,不制造 CLI」。
路径 B 仅允许作为 **可选的 1:1 MCP 透传叶子通道**(见 §5),不得覆盖 LeafSpec / Shortcut 主路径。
对外叙事:与飞书 **分层单权威** 同构——API/透传叶可用平台事实,产品 CLI / Shortcut 用手写契约 + 执行体——而不是「全家只有平台 meta」。
### 1.2 声明(declare):写什么、写在哪、投影到哪
**定义**:声明 = 在 Contract 结构体的**数据字段**上写出事实;`corecmd.New` 据此注册 cobra、渲染 help、写入 `dws.schema.*`。钩子闭包(`Validate` / `Call` / `PostMount` / `RunE`)里的逻辑**不算**声明——即使行为正确,也不能单靠钩子让 Schema/help「猜出」该事实。
命令框架边界与今日/目标对应见 RFC [`rfc-command-framework-convergence.md`](rfc-command-framework-convergence.md) **§5.0**(框架上的「声明」定义);本节给字段级表与示例。
**唯一写入面**(三选一,语义相同):
| 入口 | 类型 | 归一 |
|---|---|---|
| Leaf 命令 | `helpers.LeafSpec` | `FromLeafSpec` → `corecmd.Spec` |
| Shortcut | Shortcut 声明(经 `FromShortcut`) | → `corecmd.Spec` |
| 直接基座 | `corecmd.Spec` | `corecmd.New` |
今日产品 CLI 叶子以 **`LeafSpec` 为声明门面**;字段与 `corecmd.Spec` 契约面一一对应。
#### 1.2.1 契约字段(算声明)
| 字段 | 声明什么 | 运行时 | 嵌入 Schema / help |
|---|---|---|---|
| `Flags[]`(`FlagSpec` / `LeafFlag`) | 用户可见参数面:名、类型、默认、必填、usage | 注册 cobra flag;装配 toolArgs | `dws.schema.property` / `type` / `required`;`--help` Flags |
| `Constraints[]` | 跨 flag 关系:`at_least_one` / `exactly_one` / `mutually_exclusive`;`custom` 记录钩子校验 | 通用关系由 `ValidateConstraints` 执行;`custom` 由 `Validate` 执行 | `dws.schema.constraints`;`--help`「参数约束」 |
| `Safety`(`contract.SafetySpec`) | effect/risk/confirmation/idempotency 四个独立事实 | `confirmation=user_required` 时 `ConfirmSafety`;`--yes` / `--dry-run` 跳过 | 同一个 SafetySpec 原样进入 Contract Final(`HOM-S1`) |
| `ConstParams` | 固定载荷(不上 flag 表) | 并入 toolArgs;不满足 Required | **不**投影为用户 parameter |
| `Use` / `Short` / `Long` / `Example` | 命令身份文案与示例 | cobra 自身 | help;identity 以 collector 收集的 `ContractFinal.Identity` 声明为准(reviewed registry 已退役) |
`FlagSpec` 子字段(声明细节):
| 子字段 | 作用 | 是否进 Schema parameters |
|---|---|---|
| `Name`, `Usage`, `Kind`, `Default` | 注册名/说明/类型/DefValue | 是(name/type;default 与 cobra 对齐) |
| `Required` / `MarkRequired` | 非空校验 / cobra 硬必填 | 是(`required`) |
| `RequiredHint`, `Aliases`, `EnvVar` | 校验提示、隐藏别名、环境回退 | 否(执行细节;别名不上主 parameter 表) |
| `ArgDefault`, `Bind`, `OmitEmpty`, `Trim`, `Transform` | toolArgs 装配语义 | 否(载荷细节;`Bind` 可进 property 映射,但不另造 flag) |
| `Input` | 额外取值来源:`@path` 读文件 / `-` 读 stdin,在 required/enum/约束/`Validate` 之前原地解析 | 否(今日:能力由作者写进 `Usage` / `SchemaDescription` 文案,是已声明事实而非推断;不另造 flag。目标形态收敛为类型化投影字段,见 RFC §5.3) |
#### 1.2.2 编排 / 执行字段(不算声明)
| 字段 | 角色 | 禁止用来「冒充」的声明 |
|---|---|---|
| `Validate` | 条件式/领域校验钩子 | 不得在此 `Flags().String(...)` 注册业务 flag;不得只靠钩子表达「必填/互斥」而不写 `Flags`/`Constraints` |
| `Call` / `Invoke` / `Orchestrate` | 执行体 | 不得 `params[k]=…` 装配业务参数(应在 `Flags`/`ConstParams`) |
| `PostMount` | 挂载后收尾(annotate、领域工具注入) | 不得注册业务 flag;分页等横切由领域工具注入并可走 annotate |
| `RunE` | 逃生舱(整段手写) | 表面事实仍须 Flags 声明;框架仍按 Safety 执行确认 |
| `Server` / `Tool` | MCP 路由 | 不构成 CLI parameter 声明 |
#### 1.2.3 最小声明示例
```go
// 读:Safety 四字段显式对齐 Schema
NewLeafCommand(LeafSpec{
Use: "get", Short: "…", Tool: "…",
Safety: contract.SafetySpec{
Effect: "read", Risk: "low",
Confirmation: "not_required", Idempotency: "idempotent",
},
Flags: []LeafFlag{
{Name: "unified-app-id", Usage: "…", Bind: "unifiedAppId", Trim: true, Required: true},
},
Call: devAppCall(runner), PostMount: devAppMeta(tool),
})
// 写:同一个 SafetySpec 同时驱动确认与 Schema
NewLeafCommand(LeafSpec{
Use: "publish", Short: "…", Tool: "…",
Flags: []LeafFlag{ /* … */ },
Safety: contract.SafetySpec{
Effect: "write", Risk: "medium",
Confirmation: "user_required", Idempotency: "unknown",
},
Call: devAppCall(runner), PostMount: devAppMeta(tool),
})
// 迁移期旧写命令:确认走 annotate,见 §1.3 —— 不是新声明面
NewLeafCommand(LeafSpec{
Use: "create", /* Flags… */,
Validate: func(cmd *cobra.Command, _ []string) error {
return devAppRequireWriteGuard(cmd, "create") // 执行守卫,不是 Contract 声明
},
Call: devAppCall(runner),
PostMount: devAppMetaWrite(tool), // 人工标注 runtime_gate
})
```
**空 `Safety` 的含义**:command 为兼容旧只读叶保留 `read/low/not_required/idempotent` 默认。因此「会改状态却留空 Safety」**不是**合法声明;新 Leaf 必须写完整 SafetySpec,未迁移旧路径则按 §1.3 标注 gate。
### 1.3 人工标注(annotate):声明的补充通道
当事实无法或不愿放进 Contract 字段时,必须**显式**落注解 / 评审源,禁止组装期推断:
| 标注手段 | 典型值 | 何时用 |
|---|---|---|
| `cli.AnnotateRuntimeGate` / `devAppMetaWrite` | `dws.schema.runtime_gate=devAppRequireWriteGuard` | 尚未迁移到 SafetySpec 的旧写命令(`HOM-S2`) |
| `cli.AnnotateRuntimeRisk` | `dws.schema.risk=…` | Shortcut 暂存的旧兼容路径;command/Leaf 禁止新增 |
| `cli.AnnotateRuntimeFlag` / Constraints | 与 embed 同形 | 手写 cobra 叶补齐表面(长期应迁入 Contract) |
| reviewed `schema_hints/metadata` Safety(已退役) | effect/risk/confirmation | 已删:`schema_hints/` 不得重现;受管命令以 Contract.Safety / gate 为准 |
标注与声明冲突时:**Contract 声明胜**(路径 A)。标注不得发明未注册的 CLI flag。
### 1.4 Schema 全覆盖(`ToolSpec` 无空洞)
`dws schema` 叶子模型是 `cli.ToolSpec`。命令框架必须为**每一个字段组**指定权威;完整矩阵在 RFC **§5.0.4**,摘要:
| ToolSpec 组 | 权威类 | 框架声明字段 / 其它源 |
|---|---|---|
| Identity | 声明源(identity collector 收集 `ContractFinal.Identity`;reviewed `schema_command_registry` 已退役) | `ContractDecl.Identity` 声明 |
| Display / Title / Description | 声明证据 + 交付偏好(非双权威) | **title**:ContractDecl 优先,否则 Cobra Short,再 MCP;**description**:构造期 Description 必填;Catalog 交付 Long→`cobra_help`,无 Long→`contract_final`;**Short 不进 description**(RFC §5.0.4) |
| Parameters.`name/type/required/default/property` | **声明**(或同形 annotate) | `Flags` / `Bind` |
| Parameters.`description` | 声明 usage(`FlagSpec.Usage` / ParamDecl) | `schema_hints/` 已退役;不得用 overlay 改 type/required/default |
| Parameters.`interface_*` | 评审源 | MCP meta / bindings;**不造 flag** |
| Constraints | **声明** | `Constraints` |
| Positionals | **声明** 或显式 annotate | 目标 `Args`;禁止推断 |
| Safety.`effect/risk/confirmation/idempotency` | **声明**完整 `Safety`,或迁移期 `runtime_gate` / reviewed Safety | 四字段独立;不得互相推导 |
| DryRun | 评审源 | dry-run capabilities registry |
| Interface | 评审源 | MCP + 内存 inject 的 Agent metadata |
| Selection | 声明(ContractFinal / ProductDecl) | `ContractDecl.Selection` / `ProductDecl` |
| FieldProvenance / Extensions | 组装派生或评审扩展 | 组装器;与 delivered value 一致 |
| (非 Schema parameter)ConstParams | **声明** | 载荷;不上 parameters 表 |
验收:新增 Schema 字段必须同步改 RFC §5.0.4 + 本表;受管写命令 Safety 不得无主。
## 2. 字段归属(每一类恰好一个写入者)
| 字段类 | 权威 | 投影到 |
|---|---|---|
| flags / defaults / required / enum / 关系约束 / 运行时 Risk | Contract(LeafSpec / `corecmd.Spec` 门面) | cobra、`--help`、Schema `parameters` / constraints / confirmation |
| ConstParams、Bind、OmitEmpty、Transform | 同上(载荷声明,不上 flag 表) | toolArgs;Schema 不把 ConstParams 伪装成用户 flag |
| canonical path / aliases / navigation / exposure | identity collector 收集的 `ContractFinal.Identity` 声明(reviewed `schema_command_registry` 已退役) | Schema identity |
| use_when / avoid_when / examples / agent_summary 文案 | `ContractDecl.Selection` / `ProductDecl` | Schema selection |
| RPC tool 形状、`interface_ref`、interface 描述 | leaf `Contract.Interface` + `ParamDecl`(`schema_parameter_mapping_ledger.go` 仅 mapping_exclusions / removals;`schema_mcp_metadata` 已退役) | Schema `interface_*` 字段;**不得创建 flag** |
| 参数描述 overlay(可选) | 生产 metadata 壳为空;参数事实走 ParamDecl / FlagSpec | **Contract/cobra 胜** |
| 遗留 Safety 文案(迁移期) | 生产 metadata 壳为空;Safety 走 Contract | 以 Contract.Safety / runtime_gate 为准(见 §4) |
| dry-run 正能力 | reviewed dry-run registry | Schema `dry_run` |
| positionals | Contract Args / 显式 annotate | Schema `positionals` |
| FieldProvenance | 组装派生 | Schema provenance(与值一致) |
Selection **刻意不**由单命令 Contract 取代(RFC 决策 8 / schema 设计硬规则);identity 的历史决策是不进 Contract、归 reviewed `CommandRegistry`,该 registry 已退役,现由 identity collector 收集 `ContractFinal.Identity` 声明(声明即 identity)。**完整无空洞表见 RFC §5.0.4。**
## 3. 当前缺口与目标闭环
已具备:
- Flags / ConstParams / Constraints → 注册、校验与 `ConstraintHelp`;SafetySpec → 运行时 `ConfirmSafety`(command);
- Call / Execute 作为执行体;业务参数不得在 Call 内装配(helpers 门禁);
- **Contract → Schema 嵌入**:参数/约束写原生 annotation,SafetySpec 与 ContractDecl 注册为类型化 Contract Final 并由 Schema 组装透传;
- Selection 权威为 `ContractDecl.Selection` / `ProductDecl`(`contract_final`);`schema_hints/` 已退役。
已进 CI(`make policy` → `check-schema-catalog.sh` / `check-runtime-confirmation-truth.sh`):
| Gate ID | CI 入口(`-run` 白名单 / 脚本) |
|---|---|
| `HOM-P1` / `HOM-D1` | `./internal/app`:`TestFinalSchemaParametersMatchExecutableHelpFlags`、`TestDeliverySchemaParametersMatchExecutableHelpFlags` |
| `HOM-P2`(参数映射/bindings 子集) | `./internal/cli`:`TestSchemaParameterBindingsMatchReviewedBaselineAndDeliveryCatalog`、`TestDeliveryCatalogMCPParameterMappingsAreComplete` 等 bindings 门禁 |
| `HOM-S1` / `HOM-S2`(confirmation 同源) | `./internal/cli/homology`:`TestUserRequiredSafetyHomologyWithRuntimeGate` + `check-runtime-confirmation-truth.sh`;`./internal/app`:`TestSheetFinalSchemaConfirmationMatchesRuntimeGuards` |
| 词汇/决策钉扎 | `./internal/cli/homology`:`TestHomologyDecisionDocPinsPathAAndGateIDs`、`TestMCPPassthroughAdmissionExcludesLeafAndShortcut`、`TestHomologyCIEntrypointsPinned` |
仍缺(未宣称全量 CI 覆盖):
1. 独立可执行的 `HOM-P3`(constraints ≡ AnnotateConstraints)与 `HOM-S3`(read 不得误投影 user_required)全量 gate;
2. `HOM-I1` 作为单独 gate ID 的显式用例(MCP bindings ⊆ Contract flags 已有映射审计子集,但未钉 `HOM-I1` 标签)。
已落地(写命令确认语义,`HOM-S2`):
- `AnnotateRuntimeGate` / `dws.schema.runtime_gate`;Leaf `PostMount: devAppMetaWrite`;手写 delete/robot 等同路径显式标注;
- Schema 组装在无 Contract Safety 但有 gate 时 overlay `confirmation=user_required`(`applyContractGateToSafety`);
- AST/mount 测试:新 Leaf 须声明完整 SafetySpec;尚未迁移的旧路径须有 runtime_gate。
## 4. Schema 投影与 Safety 门禁规划
以下门禁 ID 稳定,便于 CI 认领。§3 表标明哪些已挂入 `check-schema-catalog.sh`。
| Gate ID | 断言 | 范围 | CI |
|---|---|---|---|
| `HOM-P1` | 受管 leaf 的 schema `parameters[].name` 集合 ≡ cobra 本地 flag 名集合(排除全局 persistent) | LeafSpec / Contract 编译命令 | **已进**(app help↔schema) |
| `HOM-P2` | schema parameter `type` / `required` / `default` 与 cobra DefValue / MarkFlagRequired / FlagSpec 一致;不得用已退役 hints overlay 改写这三项 | 同上 | **部分**(bindings/mapping 门禁) |
| `HOM-P3` | schema 关系约束(require_one_of / mutually_exclusive)≡ Contract/Leaf `Constraints` 投影(与 `AnnotateConstraints` 同构) | 声明了 Constraints 的命令 | 规划 |
| `HOM-S1` | Contract/Leaf `user_required` Safety 与运行时 Confirm/gate 同源,且 help Safety 行同语义 | 受管写/破坏性命令 | **已进** |
| `HOM-S2` | 若命令走显式 write guard(如 `devAppRequireWriteGuard`)而非完整 SafetySpec,则必须人工标注 `dws.schema.runtime_gate`;Schema 不得呈 `confirmation=not_required`;符合 §1.1 declare OR annotate | 今日 devapp 写命令 | **已进**(同源测试含 gate 路径) |
| `HOM-S3` | `Risk=read`(或空→read)不得投影为 `user_required`,除非有 reviewed exclusion reason | 受管读命令 | 规划 |
| `HOM-I1` | `interface_ref` 存在时,bindings 覆盖的 CLI flag ⊆ Contract flags;MCP meta **不**引入额外 CLI flag | 有 MCP 绑定的命令 | **部分**(mapping 审计) |
| `HOM-D1` | `dws <path> --help` Flags 段与 schema leaf parameters 零未解释增量 | 受管公开 leaf | **已进**(与 HOM-P1 同测) |
落地顺序建议:
1. 保持 `HOM-P1`/`HOM-D1`/`HOM-S1`/`HOM-S2` 在 `check-schema-catalog.sh` 白名单中(勿再只靠词汇钉扎);
2. 补独立 `HOM-P3` / `HOM-S3` 可执行 gate,并把 `HOM-P2`/`HOM-I1` 从「部分」升到全量标签;
3. 新 Leaf 继续走 Contract 嵌入;禁止 `schema_hints/` 回潮。
## 5. 路径 B 子通道:1:1 MCP 透传叶(可选,非主路径)
仅当同时满足以下条件时,才允许「从 MCP meta 生成 flag/help/schema 参数」:
1. CLI path ↔ 单一 MCP tool **严格 1:1**,无多步、无按名解析、无本地 effect;
2. 无不在 MCP input schema 中的 CLI 特有 flag(含 guard 专用语义 flag 除外的全局 `--yes`/`--dry-run`);
3. 无 ConstParams / Transform / 跨 flag 约束 / Call 内业务逻辑;
4. Risk/confirmation 在 meta 或并列 reviewed Safety 中有显式来源,不靠生成器猜测;
5. 在 identity 声明面标记 `surface_kind=mcp_passthrough`(名称可调整;原计划标在 reviewed registry,该 registry 已退役),且 **不得**与 LeafSpec/Shortcut 手写定义双注册同一 `cli_path`;
6. 文档与门禁写明:该通道是子集优化,失败时回退/禁止扩张到产品 CLI。
显式排除(永远走路径 A / Shortcut):
- 全部 `LeafSpec` 命令(含 `dws dev app …`);
- 全部 `+shortcut` 与 smart 编排;
- 任何需要 `devAppRequireWriteGuard`、cursor 工具注入、或响应投影的命令。
## 6. 非目标
- ~~不把 canonical identity / 导航塞进 Contract(仍归 reviewed `CommandRegistry`)~~ —— 该非目标已被后续演进取代:reviewed `CommandRegistry` 已退役,identity 现由 collector 收集 `ContractFinal.Identity` 声明(声明即 identity,不再是独立评审文件)。selection 文案已由 `ContractDecl.Selection` / `ProductDecl` 声明(非 hints)。
- 不要求删除 LeafSpec 门面。
- 不把「生成 catalog 字节一致」当作运行时同源的充分条件(仍需 `HOM-*` 与差分门禁)。