diff --git a/docs/rfc-command-framework-convergence.md b/docs/rfc-command-framework-convergence.md index ea6e1224..44263932 100644 --- a/docs/rfc-command-framework-convergence.md +++ b/docs/rfc-command-framework-convergence.md @@ -1146,7 +1146,7 @@ cmd.RunE = func(cmd *cobra.Command, args []string) error { | 非 Shortcut 受管定义的 Cobra 命令 Hidden | 可执行 Contract | 挂载的命令可见性与声明匹配 | | Shortcut 列表成员资格与语义 disposition | 经评审的 Shortcut 可见性解析器 | public/all 列表成员资格与经评审决策匹配 | | Runtime Schema / Agent 暴露 | 经评审的 CommandRegistry 加精确排除 | 每个暴露叶子解析到活 Contract;排除显式且不重叠 | -| 运行时 Risk 与确认 | 可执行 Contract(非空 `Risk`)或显式 annotate(如 `runtime_gate`);见 §5.0 | 不得靠推断;空 Risk 不嵌入 schema risk;写命令须 Risk 或 gate;Safety 组装优先级 **`Schema.Safety`(Final)> `Risk` > `runtime_gate`**,高优先命中即忽略低优先 | +| 运行时 Risk 与确认 | 可执行 Contract(非空 `Risk`)或显式 annotate(如 `runtime_gate`);见 §5.0 | 不得靠推断;空 Risk 不嵌入 schema risk;写命令须 Risk 或 gate;Safety 组装优先级 **`Schema.Safety`(Final 显式字段)> `CommandSpec.Safety` 枚举 tier > `Risk.SafetyDefault()` > `runtime_gate`**,高优先命中即忽略低优先;tier 填充含 idempotency(读 idempotent / 写 unknown),故仅声明 Risk/Safety 枚举即自足 | | 后端 product/tool/载荷绑定 | mcpbind + 后端元数据 | 每个绑定引用真实的 flag/属性 | | Agent 选择文案(`use_when`、`avoid_when`、摘要) | 经评审的 hints/catalog | 身份解析到活契约 | diff --git a/internal/cli/schema_agent_metadata/dev.json b/internal/cli/schema_agent_metadata/dev.json index a8f6ede1..c60a18d4 100644 --- a/internal/cli/schema_agent_metadata/dev.json +++ b/internal/cli/schema_agent_metadata/dev.json @@ -957,13 +957,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -999,7 +999,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -1205,13 +1205,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -1247,7 +1247,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -1701,13 +1701,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -1743,7 +1743,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -1949,13 +1949,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -1991,7 +1991,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -2678,13 +2678,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -2714,7 +2714,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl" ], @@ -3124,13 +3124,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -3160,7 +3160,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl" ], @@ -3365,13 +3365,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -3407,7 +3407,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -3861,13 +3861,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -3903,7 +3903,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -4109,13 +4109,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -4151,7 +4151,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -4357,13 +4357,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -4399,7 +4399,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -4605,13 +4605,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -4647,7 +4647,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -5579,13 +5579,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -5615,7 +5615,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl" ], @@ -5820,13 +5820,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -5862,7 +5862,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl", "skills/mono/references/products/dev.md" @@ -6574,7 +6574,7 @@ "含高敏权限但尚未确认时不要正式发布" ], "confirmation": "user_required", - "effect": "write", + "effect": "destructive", "effect_source": "cmdcore.contract", "examples": [ "dws dev app version publish --unified-app-id \u003cunifiedAppId\u003e --version-id \u003cversionId\u003e --dry-run --format json" @@ -6649,13 +6649,13 @@ ] }, "effect": { - "value": "write", + "value": "destructive", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "write", + "value": "destructive", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -7243,13 +7243,13 @@ ] }, "risk": { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "resolution": "highest_precedence", "candidates": [ { - "value": "high", + "value": "medium", "source": "cmdcore.contract", "precedence": "contract_final", "selected": true @@ -7279,7 +7279,7 @@ "interface_mode": "composite", "interface_reason": "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command.", "reviewed": true, - "risk": "high", + "risk": "medium", "source_refs": [ "cmdcore.SchemaDecl" ], diff --git a/internal/cli/schema_catalog/catalog.json b/internal/cli/schema_catalog/catalog.json index 01a85e56..b75e4bc4 100644 --- a/internal/cli/schema_catalog/catalog.json +++ b/internal/cli/schema_catalog/catalog.json @@ -1,7 +1,7 @@ { "version": 1, "surface_hash": "sha256:29d4f5f43688cc01bc2277a4528b597de4fbab89a7b2f13fe2e56df85d1c66c4", - "source_hash": "sha256:26c7461cde3019fd9ec7378606e59a5725ca78a3d465ac8a865f9f70ecc28c3a", + "source_hash": "sha256:3ab6ab1717cdfaa6f115487c838ecd422f9a931bab28725e7af1cedd3d31a366", "catalog": { "agent_metadata": { "products_with_metadata": 26, @@ -12441,7 +12441,7 @@ "name": "add_dev_app_members", "primary_cli_path": "dev app member add", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "添加开放平台应用成员", "use_when": [ "需要授予指定用户应用协作角色时" @@ -12472,7 +12472,7 @@ "name": "apply_dev_app_permissions", "primary_cli_path": "dev app permission add", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "申请开放平台应用权限点", "use_when": [ "已确认 scopeValue 并需要新增权限时" @@ -12652,7 +12652,7 @@ "name": "disable_dev_app", "primary_cli_path": "dev app disable", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "停用开放平台企业内部应用", "use_when": [ "需要让应用暂时不可用但保留配置时" @@ -12683,7 +12683,7 @@ "name": "disable_dev_app_robot", "primary_cli_path": "dev app robot disable", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "停用现有应用的机器人能力", "use_when": [ "需要暂时让应用机器人离线时" @@ -12714,7 +12714,7 @@ "name": "enable_dev_app", "primary_cli_path": "dev app enable", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "启用开放平台企业内部应用", "use_when": [ "需要恢复一个已停用应用时" @@ -12745,7 +12745,7 @@ "name": "enable_dev_app_robot", "primary_cli_path": "dev app robot enable", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "启用现有应用机器人能力(纯启用,无需配置字段)", "use_when": [ "机器人配置存在但处于 OFFLINE 时" @@ -13109,7 +13109,7 @@ "dry_run": { "preview_kind": "invocation" }, - "effect": "write", + "effect": "destructive", "group": "app.version", "idempotency": "unknown", "interface_mode": "composite", @@ -13180,7 +13180,7 @@ "name": "remove_dev_app_members", "primary_cli_path": "dev app member remove", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "移除开放平台应用成员", "use_when": [ "需要撤销指定用户的应用协作角色时" @@ -13211,7 +13211,7 @@ "name": "remove_dev_app_permissions", "primary_cli_path": "dev app permission remove", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "取消开放平台应用权限点", "use_when": [ "需要撤销已知 scopeValue 的权限时" @@ -13273,7 +13273,7 @@ "name": "set_extension_robot_config", "primary_cli_path": "dev app robot config", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "创建或更新现有应用的机器人配置(upsert)", "use_when": [ "已有 unifiedAppId 并需要配置机器人名称、回调或技能时" @@ -13304,7 +13304,7 @@ "name": "set_extension_webapp_config", "primary_cli_path": "dev app webapp config", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "配置网页应用能力", "use_when": [ "需要设置 H5、PC 首页或 OMP 地址时" @@ -13366,7 +13366,7 @@ "name": "subscribe_dev_app_events", "primary_cli_path": "dev app event subscribe", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "订阅应用事件回调", "use_when": [ "已确认事件码并需要新增事件订阅时" @@ -13397,7 +13397,7 @@ "name": "unsubscribe_dev_app_events", "primary_cli_path": "dev app event unsubscribe", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "取消订阅应用事件", "use_when": [ "需要停止接收一个或多个已订阅事件时" @@ -13428,7 +13428,7 @@ "name": "update_dev_app", "primary_cli_path": "dev app update", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "修改开放平台企业内部应用基础信息", "use_when": [ "已有 unifiedAppId,需要修改应用基础信息" @@ -13459,7 +13459,7 @@ "name": "update_dev_app_security_config", "primary_cli_path": "dev app security config", "reviewed": true, - "risk": "high", + "risk": "medium", "title": "更新开放平台应用安全配置", "use_when": [ "需要调整应用安全相关 URL 或白名单时" diff --git a/internal/cli/schema_catalog/tools/dev.json b/internal/cli/schema_catalog/tools/dev.json index 2ce544ed..05446c5a 100644 --- a/internal/cli/schema_catalog/tools/dev.json +++ b/internal/cli/schema_catalog/tools/dev.json @@ -317,7 +317,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -325,7 +325,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -728,7 +728,7 @@ "primary_cli_path": "dev app member add", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "添加开放平台应用成员", "use_when": [ @@ -1051,7 +1051,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -1059,7 +1059,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -1347,7 +1347,7 @@ "primary_cli_path": "dev app permission add", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "申请开放平台应用权限点", "use_when": [ @@ -4843,7 +4843,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -4851,7 +4851,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -5024,7 +5024,7 @@ "primary_cli_path": "dev app disable", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "停用开放平台企业内部应用", "use_when": [ @@ -5347,7 +5347,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -5355,7 +5355,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -5528,7 +5528,7 @@ "primary_cli_path": "dev app robot disable", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "停用现有应用的机器人能力", "use_when": [ @@ -5851,7 +5851,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -5859,7 +5859,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -6032,7 +6032,7 @@ "primary_cli_path": "dev app enable", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "启用开放平台企业内部应用", "use_when": [ @@ -6355,7 +6355,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -6363,7 +6363,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -6536,7 +6536,7 @@ "primary_cli_path": "dev app robot enable", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "启用现有应用机器人能力(纯启用,无需配置字段)", "use_when": [ @@ -14747,7 +14747,7 @@ "dry_run": { "preview_kind": "invocation" }, - "effect": "write", + "effect": "destructive", "effect_source": "cmdcore.contract", "examples": [ "dws dev app version publish --unified-app-id \u003cunifiedAppId\u003e --version-id \u003cversionId\u003e --dry-run --format json" @@ -14897,7 +14897,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "write" + "value": "destructive" } ], "precedence": "contract_final", @@ -14905,7 +14905,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "write" + "value": "destructive" }, "examples": { "candidates": [ @@ -16386,7 +16386,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -16394,7 +16394,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -16797,7 +16797,7 @@ "primary_cli_path": "dev app member remove", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "移除开放平台应用成员", "use_when": [ @@ -17120,7 +17120,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -17128,7 +17128,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -17416,7 +17416,7 @@ "primary_cli_path": "dev app permission remove", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "取消开放平台应用权限点", "use_when": [ @@ -18442,7 +18442,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -18450,7 +18450,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -20040,7 +20040,7 @@ "primary_cli_path": "dev app robot config", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "创建或更新现有应用的机器人配置(upsert)", "use_when": [ @@ -20373,7 +20373,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -20381,7 +20381,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -21014,7 +21014,7 @@ "primary_cli_path": "dev app webapp config", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "配置网页应用能力", "use_when": [ @@ -22404,7 +22404,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -22412,7 +22412,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -22700,7 +22700,7 @@ "primary_cli_path": "dev app event subscribe", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "订阅应用事件回调", "use_when": [ @@ -23023,7 +23023,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -23031,7 +23031,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -23319,7 +23319,7 @@ "primary_cli_path": "dev app event unsubscribe", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "取消订阅应用事件", "use_when": [ @@ -23651,7 +23651,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -23659,7 +23659,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -24177,7 +24177,7 @@ "primary_cli_path": "dev app update", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "修改开放平台企业内部应用基础信息", "use_when": [ @@ -24509,7 +24509,7 @@ "selected": true, "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" } ], "precedence": "contract_final", @@ -24517,7 +24517,7 @@ "review_reason": "Contract final Schema pass-through", "source": "cmdcore.contract", "source_ref": "cmdcore.SchemaDecl", - "value": "high" + "value": "medium" }, "title": { "candidates": [ @@ -25035,7 +25035,7 @@ "primary_cli_path": "dev app security config", "product_id": "dev", "reviewed": true, - "risk": "high", + "risk": "medium", "source": "reviewed_command_registry", "title": "更新开放平台应用安全配置", "use_when": [ diff --git a/internal/cmdcore/cmdcore.go b/internal/cmdcore/cmdcore.go index 5f033ef8..97f9c613 100644 --- a/internal/cmdcore/cmdcore.go +++ b/internal/cmdcore/cmdcore.go @@ -155,6 +155,44 @@ func (r Risk) Effective() Risk { return r } +// SafetyDefault returns the Safety tier this Risk level implies when no +// explicit Safety is declared. It is the composition seam: Risk drives +// runtime confirmation, Safety drives Schema metadata, and SafetyDefault +// bridges the two when only Risk is declared. +func (r Risk) SafetyDefault() Safety { + switch r.Effective() { + case RiskWrite: + return SafetyWrite + case RiskHighWrite: + return SafetyDestructive + default: + return SafetyRead + } +} + +// Safety declares the Schema safety metadata tier, projected into +// agent-facing Schema independently of the runtime Risk. Empty == SafetyRead. +type Safety string + +const ( + // SafetyRead is a read-only operation: read/low/not_required/idempotent. + SafetyRead Safety = "read" + // SafetyWrite is a reversible mutation: write/medium/user_required/unknown. + SafetyWrite Safety = "write" + // SafetyHighWrite is a hard-to-reverse mutation: write/high/user_required/unknown. + SafetyHighWrite Safety = "high-write" + // SafetyDestructive is a destructive operation: destructive/high/user_required/unknown. + SafetyDestructive Safety = "destructive" +) + +// Effective returns the effective safety tier, defaulting empty to read. +func (s Safety) Effective() Safety { + if s == "" { + return SafetyRead + } + return s +} + // ConstraintKind is the type of a cross-flag relationship constraint. Values // match the shortcut framework's ConstraintKind verbatim. type ConstraintKind string @@ -218,6 +256,10 @@ type CommandSpec struct { Flags []FlagSpec Constraints []Constraint Risk Risk + // Safety declares the Schema safety tier independently of the runtime + // Risk. Empty falls back to Risk.SafetyDefault(); explicit SafetyDecl + // string fields always win over the tier fill. + Safety Safety // ConfirmFirst runs the Risk write-confirmation before required/constraint/ // Validate checks instead of after them. Use it where the legacy semantics // were guard-first (a write without --yes fails fast with @@ -898,7 +940,7 @@ func embedSchemaDecl(cmd *cobra.Command, spec CommandSpec) { payload := cli.ContractFinalPayload{ Title: firstNonEmpty(schema.Title, spec.Short), Description: firstNonEmpty(schema.Description, spec.Long), - Safety: schemaSafetyFromDecl(spec.Risk, schema.Safety), + Safety: schemaSafetyFromDecl(spec.Risk, spec.Safety, schema.Safety), } if n := len(schema.Positionals); n > 0 { payload.Positionals = make([]cli.RuntimeSchemaPositional, n) @@ -959,45 +1001,74 @@ func firstNonEmpty(values ...string) string { return "" } -func schemaSafetyFromDecl(risk Risk, decl SafetyDecl) *cli.SafetySpec { +// schemaSafetyFromDecl composes the Schema safety metadata from three +// sources, in precedence order: explicit SafetyDecl string fields (highest) +// > the declared Safety tier enum > Risk.SafetyDefault() (lowest). The tier +// fill also covers Idempotency (idempotent for reads, unknown for writes), +// so a plain Risk/Safety enum declaration is self-sufficient. +func schemaSafetyFromDecl(risk Risk, safety Safety, decl SafetyDecl) *cli.SafetySpec { out := cli.SafetySpec{ Effect: strings.TrimSpace(decl.Effect), Risk: strings.TrimSpace(decl.Risk), Confirmation: strings.TrimSpace(decl.Confirmation), Idempotency: strings.TrimSpace(decl.Idempotency), } - if strings.TrimSpace(string(risk)) != "" { - switch risk.Effective() { - case RiskWrite: - if out.Effect == "" { - out.Effect = "write" - } - if out.Risk == "" { - out.Risk = "medium" - } - if out.Confirmation == "" { - out.Confirmation = "user_required" - } - case RiskHighWrite: - if out.Effect == "" { - out.Effect = "destructive" - } - if out.Risk == "" { - out.Risk = "high" - } - if out.Confirmation == "" { - out.Confirmation = "user_required" - } - case RiskRead: - if out.Effect == "" { - out.Effect = "read" - } - if out.Risk == "" { - out.Risk = "low" - } - if out.Confirmation == "" { - out.Confirmation = "not_required" - } + tier := safety + if tier == "" { + tier = risk.SafetyDefault() + } + switch tier.Effective() { + case SafetyWrite: + if out.Effect == "" { + out.Effect = "write" + } + if out.Risk == "" { + out.Risk = "medium" + } + if out.Confirmation == "" { + out.Confirmation = "user_required" + } + if out.Idempotency == "" { + out.Idempotency = "unknown" + } + case SafetyHighWrite: + if out.Effect == "" { + out.Effect = "write" + } + if out.Risk == "" { + out.Risk = "high" + } + if out.Confirmation == "" { + out.Confirmation = "user_required" + } + if out.Idempotency == "" { + out.Idempotency = "unknown" + } + case SafetyDestructive: + if out.Effect == "" { + out.Effect = "destructive" + } + if out.Risk == "" { + out.Risk = "high" + } + if out.Confirmation == "" { + out.Confirmation = "user_required" + } + if out.Idempotency == "" { + out.Idempotency = "unknown" + } + default: // SafetyRead + if out.Effect == "" { + out.Effect = "read" + } + if out.Risk == "" { + out.Risk = "low" + } + if out.Confirmation == "" { + out.Confirmation = "not_required" + } + if out.Idempotency == "" { + out.Idempotency = "idempotent" } } // effect_source is assembly-derived for every Contract-declared safety, diff --git a/internal/cmdcore/schema_decl.go b/internal/cmdcore/schema_decl.go index 0101a2de..2b2a041b 100644 --- a/internal/cmdcore/schema_decl.go +++ b/internal/cmdcore/schema_decl.go @@ -48,8 +48,9 @@ type PositionalDecl struct { Index int } -// SafetyDecl is the final Schema Safety payload. Empty Effect/Risk/Confirmation -// may be filled from CommandSpec.Risk at embed time; Idempotency is declaration-only. +// SafetyDecl is the final Schema Safety payload. Any field left empty is +// filled from the CommandSpec.Safety tier (or Risk.SafetyDefault() when +// Safety is unset) at embed time, including Idempotency. type SafetyDecl struct { Effect string Risk string @@ -126,18 +127,11 @@ func validateSchemaDecl(spec CommandSpec) { if len(spec.Schema.Selection.Examples) == 0 { missing = append(missing, "Schema.Selection.Examples") } - // Safety/interface are unconditional catalog required keys for declared - // tools (no hints fallback). effect/risk/confirmation may come from the - // Risk shorthand; Idempotency is declaration-only. - safety := spec.Schema.Safety - safetyCoreFilled := strings.TrimSpace(safety.Effect) != "" && - strings.TrimSpace(safety.Risk) != "" && strings.TrimSpace(safety.Confirmation) != "" - if !safetyCoreFilled && strings.TrimSpace(string(spec.Risk)) == "" { - missing = append(missing, "Schema.Safety (effect/risk/confirmation, or CommandSpec.Risk)") - } - if strings.TrimSpace(safety.Idempotency) == "" { - missing = append(missing, "Schema.Safety.Idempotency") - } + // Interface is an unconditional catalog required key for declared tools + // (no hints fallback). Safety needs no completeness check: the enum tier + // (CommandSpec.Safety, falling back to Risk.SafetyDefault()) fills every + // Safety field including Idempotency, so any declared Schema projects a + // complete safety block by construction. if iface := spec.Schema.Interface; iface == nil || strings.TrimSpace(iface.Mode) == "" || strings.TrimSpace(iface.Availability) == "" { missing = append(missing, "Schema.Interface (mode/availability)") diff --git a/internal/cmdcore/schema_decl_test.go b/internal/cmdcore/schema_decl_test.go index 7505cee5..8e4a99ff 100644 --- a/internal/cmdcore/schema_decl_test.go +++ b/internal/cmdcore/schema_decl_test.go @@ -124,17 +124,6 @@ func TestNewCommandPanicsOnPartialSchemaDecl(t *testing.T) { Interface: &InterfaceDecl{Mode: "mcp", Availability: "available", ProductID: "dev", RPCName: "get_thing"}, Selection: SelectionDecl{AgentSummary: "s", UseWhen: []string{"u"}, AvoidWhen: []string{"a"}}, }, "Schema.Selection.Examples"}, - {"missing safety", SchemaDecl{ - Description: "d", - Interface: &InterfaceDecl{Mode: "mcp", Availability: "available", ProductID: "dev", RPCName: "get_thing"}, - Selection: SelectionDecl{AgentSummary: "s", UseWhen: []string{"u"}, AvoidWhen: []string{"a"}, Examples: []string{"dws x"}}, - }, "Schema.Safety"}, - {"missing idempotency", SchemaDecl{ - Description: "d", - Safety: SafetyDecl{Effect: "read", Risk: "low", Confirmation: "not_required"}, - Interface: &InterfaceDecl{Mode: "mcp", Availability: "available", ProductID: "dev", RPCName: "get_thing"}, - Selection: SelectionDecl{AgentSummary: "s", UseWhen: []string{"u"}, AvoidWhen: []string{"a"}, Examples: []string{"dws x"}}, - }, "Schema.Safety.Idempotency"}, {"missing interface", SchemaDecl{ Description: "d", Safety: SafetyDecl{Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent"}, @@ -203,6 +192,51 @@ func TestNewCommandDerivesHelpExampleFromDeclaredSelection(t *testing.T) { } } +func TestNewCommandSafetyTierComposition(t *testing.T) { + schema := func() SchemaDecl { + return SchemaDecl{ + Description: "desc", + Interface: &InterfaceDecl{Mode: "mcp", Availability: "available", ProductID: "dev", RPCName: "op"}, + Selection: SelectionDecl{ + AgentSummary: "s", UseWhen: []string{"u"}, AvoidWhen: []string{"a"}, Examples: []string{"dws x"}, + }, + } + } + build := func(spec CommandSpec) *cli.SafetySpec { + cmd := NewCommand(spec) + final, ok := cli.RuntimeContractFinal(cmd) + if !ok || final.Safety == nil { + t.Fatalf("expected declared safety, final=%#v ok=%v", final, ok) + } + return final.Safety + } + + // Risk-only: tier inferred from Risk.SafetyDefault(), including idempotency. + if got := build(CommandSpec{Use: "w", Short: "w", Risk: RiskWrite, Schema: schema(), + Invoke: func(*Ctx, map[string]any) error { return nil }}); got.Effect != "write" || got.Risk != "medium" || + got.Confirmation != "user_required" || got.Idempotency != "unknown" { + t.Fatalf("RiskWrite default tier = %#v", got) + } + // Safety enum overrides the Risk default (high-write over destructive). + if got := build(CommandSpec{Use: "w", Short: "w", Risk: RiskHighWrite, Safety: SafetyHighWrite, Schema: schema(), + Invoke: func(*Ctx, map[string]any) error { return nil }}); got.Effect != "write" || got.Risk != "high" { + t.Fatalf("explicit Safety tier must win over Risk default, = %#v", got) + } + // Explicit SafetyDecl string fields win over the tier fill. + s := schema() + s.Safety = SafetyDecl{Effect: "destructive"} + if got := build(CommandSpec{Use: "w", Short: "w", Risk: RiskWrite, Schema: s, + Invoke: func(*Ctx, map[string]any) error { return nil }}); got.Effect != "destructive" || got.Risk != "medium" { + t.Fatalf("SafetyDecl field must win over tier fill, = %#v", got) + } + // Empty Risk + empty Safety infers the read tier. + if got := build(CommandSpec{Use: "r", Short: "r", Schema: schema(), + Invoke: func(*Ctx, map[string]any) error { return nil }}); got.Effect != "read" || got.Risk != "low" || + got.Confirmation != "not_required" || got.Idempotency != "idempotent" { + t.Fatalf("empty Risk/Safety must infer read tier, = %#v", got) + } +} + func TestSchemaDeclEmptySkipsFinal(t *testing.T) { cmd := NewCommand(CommandSpec{ Use: "get", diff --git a/internal/helpers/devapp.go b/internal/helpers/devapp.go index 76739371..4dc208cd 100644 --- a/internal/helpers/devapp.go +++ b/internal/helpers/devapp.go @@ -266,7 +266,6 @@ func newDevAppEventListCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询应用已订阅的事件列表", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -288,6 +287,7 @@ func newDevAppEventSubscribeCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app event subscribe --unified-app-id UNIFIED_APP_ID --event-codes bpms_task_change --dry-run --format json", Tool: devAppEventSubscribeTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -296,7 +296,6 @@ func newDevAppEventSubscribeCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "订阅应用事件回调", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -318,6 +317,7 @@ func newDevAppEventUnsubscribeCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app event unsubscribe --unified-app-id UNIFIED_APP_ID --event-codes bpms_task_change --dry-run --format json", Tool: devAppEventUnsubscribeTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -326,7 +326,6 @@ func newDevAppEventUnsubscribeCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "取消订阅应用事件", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -360,7 +359,6 @@ func newDevAppListCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询开放平台企业内部应用列表", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -390,7 +388,6 @@ func newDevAppGetCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询开放平台企业内部应用详情", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -414,7 +411,8 @@ func newDevAppCreateCommand(runner executor.Runner) *cobra.Command { Short: "创建开放平台企业内部应用", Example: " dws dev app create --name DemoApp --desc 内部应用 --dry-run --format json", Tool: devAppCreateTool, - Risk: LeafRiskWrite, + Risk: LeafRiskHighWrite, + Safety: LeafSafetyHighWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -424,7 +422,6 @@ func newDevAppCreateCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "创建开放平台企业内部应用", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -447,6 +444,7 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app update --unified-app-id UNIFIED_APP_ID --name DemoApp2 --dry-run --format json", Tool: devAppUpdateTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -460,7 +458,6 @@ func newDevAppUpdateCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "修改开放平台企业内部应用基础信息", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -486,7 +483,6 @@ func newDevAppCredentialsGetCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "读取开放平台应用凭证", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -503,10 +499,11 @@ func newDevAppCredentialsGetCommand(runner executor.Runner) *cobra.Command { func newDevAppLifecycleCommand(runner executor.Runner, use, short, tool string, selection LeafSelectionDecl) *cobra.Command { return NewLeafCommand(LeafSpec{ - Use: use, - Short: short, - Tool: tool, - Risk: LeafRiskWrite, + Use: use, + Short: short, + Tool: tool, + Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -514,7 +511,6 @@ func newDevAppLifecycleCommand(runner executor.Runner, use, short, tool string, }, Schema: LeafSchema{ Description: short, - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: selection, @@ -542,13 +538,13 @@ func newDevAppDeleteCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app delete --unified-app-id UNIFIED_APP_ID --confirm-name 应用名 --yes --format json", Tool: devAppDeleteTool, Risk: LeafRiskHighWrite, + Safety: LeafSafetyDestructive, Flags: []LeafFlag{ {Name: "unified-app-id", Usage: "开放平台统一应用 ID(必填)", Bind: "unifiedAppId", Trim: true, Required: true, RequiredHint: "--unified-app-id 为必填"}, {Name: "confirm-name", Usage: "二次确认:必须与被删应用的名称一致(不可逆操作的防误删)", Bind: "confirmName", Trim: true, OmitEmpty: true}, }, Schema: LeafSchema{ Description: "删除开放平台企业内部应用(不可逆,需 --confirm-name 二次确认)", - Safety: devAppDeleteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -645,7 +641,6 @@ func newDevAppWebappGetCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询网页应用配置", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -667,6 +662,7 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app webapp config --unified-app-id UNIFIED_APP_ID --homepage-url https://example.com --dry-run --format json", Tool: devAppWebappSetTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -681,7 +677,6 @@ func newDevAppWebappConfigCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "配置网页应用能力", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -713,7 +708,6 @@ func newDevAppPermissionListCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询开放平台应用权限列表", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -738,6 +732,7 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app permission add --unified-app-id UNIFIED_APP_ID --scope-values Contact.User.mobile --dry-run --format json", Tool: devAppPermissionAddTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -746,7 +741,6 @@ func newDevAppPermissionAddCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "申请开放平台应用权限点", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -768,6 +762,7 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app permission remove --unified-app-id UNIFIED_APP_ID --scope-values Contact.User.mobile,qyapi_robot_sendmsg --dry-run --format json", Tool: devAppPermissionRmTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -776,7 +771,6 @@ func newDevAppPermissionRemoveCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "取消开放平台应用权限点", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -802,7 +796,6 @@ func newDevAppMemberListCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询开放平台应用成员", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -824,6 +817,7 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app member add --unified-app-id --user-ids userId1,userId2 --member-type DEVELOPER --dry-run", Tool: devAppMemberAddTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -841,7 +835,6 @@ func newDevAppMemberAddCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "添加开放平台应用成员", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -863,6 +856,7 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app member remove --unified-app-id --user-ids userId1,userId2 --member-type DEVELOPER --dry-run", Tool: devAppMemberRemoveTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -880,7 +874,6 @@ func newDevAppMemberRemoveCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "移除开放平台应用成员", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -901,8 +894,9 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { Short: "更新开放平台应用安全配置", Example: " dws dev app security config --unified-app-id " + "--ip-whitelist 192.0.2.10 --redirect-urls https://callback.example.invalid/callback --sso-urls https://sso.example.invalid/sso --dry-run", - Tool: devAppSecurityConfigTool, - Risk: LeafRiskWrite, + Tool: devAppSecurityConfigTool, + Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -916,7 +910,6 @@ func newDevAppSecurityConfigCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "更新开放平台应用安全配置", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -945,7 +938,8 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { Short: "异步提交钉钉智能体机器人创建任务(支持失败重试)", Example: " dws dev app robot submit --name 我的智能体 --robot-name 小助手 --desc \"处理审批问答\" --dry-run --format json", Tool: devAppRobotSubmitTool, - Risk: LeafRiskWrite, + Risk: LeafRiskHighWrite, + Safety: LeafSafetyHighWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -958,7 +952,6 @@ func newDevAppRobotSubmitCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "异步提交钉钉智能体机器人创建任务(支持失败重试)", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1002,7 +995,6 @@ func newDevAppRobotResultCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询机器人异步创建任务结果", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1028,7 +1020,6 @@ func newDevAppRobotConfigGetCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询现有应用的机器人配置", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1059,6 +1050,7 @@ func newDevAppRobotConfigCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app robot config --unified-app-id UNIFIED_APP_ID --name 小助手 --brief 审批助手 --dry-run --format json", Tool: devAppRobotConfigUpsertTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -1079,7 +1071,6 @@ func newDevAppRobotConfigCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "创建或更新现有应用的机器人配置(upsert)", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1119,6 +1110,7 @@ func newDevAppRobotEnableCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app robot enable --unified-app-id UNIFIED_APP_ID --dry-run --format json", Tool: devAppRobotEnableTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -1126,7 +1118,6 @@ func newDevAppRobotEnableCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "启用现有应用机器人能力(纯启用,无需配置字段)", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1148,6 +1139,7 @@ func newDevAppRobotOfflineCommand(runner executor.Runner) *cobra.Command { Example: " dws dev app robot disable --unified-app-id UNIFIED_APP_ID --dry-run --format json", Tool: devAppRobotOfflineTool, Risk: LeafRiskWrite, + Safety: LeafSafetyWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -1155,7 +1147,6 @@ func newDevAppRobotOfflineCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "停用现有应用的机器人能力", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1261,7 +1252,8 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { Short: "基于当前配置创建应用新版本", Example: " dws dev app version create --unified-app-id UNIFIED_APP_ID --desc \"新增机器人能力\" --dry-run --format json", Tool: devAppVersionCreateTool, - Risk: LeafRiskWrite, + Risk: LeafRiskHighWrite, + Safety: LeafSafetyHighWrite, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -1271,7 +1263,6 @@ func newDevAppVersionCreateCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "基于当前配置创建应用新版本", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1297,7 +1288,6 @@ func newDevAppVersionListCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "分页查询应用版本列表", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1324,7 +1314,6 @@ func newDevAppVersionGetCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询指定版本详情", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1361,7 +1350,8 @@ func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { Short: "发布指定版本(含高敏权限需 --confirmed-sensitive)", Example: " dws dev app version publish --unified-app-id UNIFIED_APP_ID --version-id VERSION_ID --dry-run --format json", Tool: devAppVersionPublishTool, - Risk: LeafRiskWrite, + Risk: LeafRiskHighWrite, + Safety: LeafSafetyDestructive, // devapp 旧版写守卫为 guard-first:确认门先于参数校验。 ConfirmFirst: true, Flags: []LeafFlag{ @@ -1373,7 +1363,6 @@ func newDevAppVersionPublishCommand(runner executor.Runner) *cobra.Command { ConstParams: map[string]any{"precheckOnly": false}, Schema: LeafSchema{ Description: "发布指定版本(含高敏权限需 --confirmed-sensitive)", - Safety: devAppWriteSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1400,7 +1389,6 @@ func newDevAppVersionStatusCommand(runner executor.Runner) *cobra.Command { }, Schema: LeafSchema{ Description: "查询版本发布/审批状态", - Safety: devAppReadSafety, DryRun: devAppDryRun, Interface: devAppCompositeInterface(), Selection: LeafSelectionDecl{ @@ -1464,22 +1452,8 @@ func devAppMeta(tool string) func(*cobra.Command) { // InterfaceDecl.Reason 的最终发布值。 const devAppCompositeInterfaceReason = "Reviewed unpinned remote adapter: this executable CLI wrapper calls a remote helper that is absent from the pinned MCP metadata snapshot; no single pinned semantically equivalent interface_ref can represent the command." -// devAppWriteSafety 是 devapp 普通写叶的 Safety 最终声明:锁评审发布值 -// (write/high/user_required);运行时确认由 LeafRiskWrite 驱动 ConfirmRisk。 -var devAppWriteSafety = LeafSafetyDecl{ - Effect: "write", Risk: "high", Confirmation: "user_required", Idempotency: "unknown", -} - -// devAppReadSafety 是 devapp 读叶的 Safety 最终声明(read/low/not_required/ -// idempotent),锁评审发布值;读叶无确认门。 -var devAppReadSafety = LeafSafetyDecl{ - Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", -} - -// devAppDeleteSafety 是 delete 的 Safety 最终声明:不可逆删除发布 destructive。 -var devAppDeleteSafety = LeafSafetyDecl{ - Effect: "destructive", Risk: "high", Confirmation: "user_required", Idempotency: "unknown", -} +// devapp 不写共享 Safety 常量:Safety 元数据由各叶的 LeafSafety 枚举驱动 +// (读叶留空由框架按 LeafRiskRead 推导),字段值全部来自 cmdcore 的 tier 填充。 // devAppDryRun 是 devapp 全树共用的 dry_run 最终声明:本地拼装调用预览 // (invocation preview),dry-run 不发起任何远端读。 diff --git a/internal/helpers/leaf.go b/internal/helpers/leaf.go index f145500b..c84c2ac6 100644 --- a/internal/helpers/leaf.go +++ b/internal/helpers/leaf.go @@ -75,6 +75,21 @@ const ( LeafRiskHighWrite = cmdcore.RiskHighWrite ) +// LeafSafety 声明 Schema 安全元数据等级(cmdcore.Safety 的别名)。独立于 +// Risk(运行时确认),描述操作对系统状态的影响程度;空值时框架从 Risk 推导。 +type LeafSafety = cmdcore.Safety + +const ( + // LeafSafetyRead 只读操作:read/low/not_required/idempotent。 + LeafSafetyRead = cmdcore.SafetyRead + // LeafSafetyWrite 可逆写操作:write/medium/user_required/unknown。 + LeafSafetyWrite = cmdcore.SafetyWrite + // LeafSafetyHighWrite 难撤回写操作:write/high/user_required/unknown。 + LeafSafetyHighWrite = cmdcore.SafetyHighWrite + // LeafSafetyDestructive 破坏性操作:destructive/high/user_required/unknown。 + LeafSafetyDestructive = cmdcore.SafetyDestructive +) + // LeafConstraintKind 是跨 flag 关系约束的类型(cmdcore.ConstraintKind 的 // 别名)。取值与 shortcut 框架的 ConstraintKind 逐字一致。 type LeafConstraintKind = cmdcore.ConstraintKind @@ -134,6 +149,11 @@ type LeafSpec struct { // dws.schema.risk 并合成 Schema.Safety。 Risk LeafRisk + // Safety 声明 Schema 安全元数据等级,独立于运行时 Risk。为空时框架从 + // Risk 推导(Risk.SafetyDefault);显式设置时 Safety 驱动 Schema 元数据, + // Risk 仍驱动运行时确认行为。 + Safety LeafSafety + // ConfirmFirst 为 true 时确认门先于 required/约束/Validate 校验执行 //(devapp 旧版写守卫语义:写命令未带 --yes 时快速失败 // confirmation_required,与参数完整性无关)。默认 false 保持 shortcut @@ -186,6 +206,7 @@ func FromLeafSpec(spec LeafSpec) cmdcore.CommandSpec { Flags: spec.Flags, Constraints: spec.Constraints, Risk: spec.Risk, + Safety: spec.Safety, ConfirmFirst: spec.ConfirmFirst, ConstParams: spec.ConstParams, Schema: spec.Schema,