refactor(cmdcore): split Safety into its own enum tier with Risk default

Risk (runtime confirmation) and Safety (schema metadata) are two
independent enums composed at embed time: explicit SafetyDecl fields >
CommandSpec.Safety tier > Risk.SafetyDefault(). The tier fill now also
covers idempotency, so an enum-only declaration is self-sufficient and
validateSchemaDecl no longer needs safety completeness checks.

devapp reclassifies its write leaves by reversibility: reversible
mutations declare LeafSafetyWrite (risk high->medium), create/robot
submit/version create declare LeafSafetyHighWrite, and delete/version
publish declare LeafSafetyDestructive (publish effect
write->destructive). Shared hand-written safety constants are deleted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
玉澜
2026-07-30 20:37:40 +08:00
co-authored by Claude Opus 4.6 Cursor
parent da93fbcc47
commit ce0501b93e
9 changed files with 316 additions and 222 deletions
+1 -1
View File
@@ -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 | 身份解析到活契约 |
+45 -45
View File
@@ -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"
],
+16 -16
View File
@@ -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 或白名单时"
+45 -45
View File
@@ -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": [
+105 -34
View File
@@ -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,
+8 -14
View File
@@ -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)")
+45 -11
View File
@@ -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",
+30 -56
View File
@@ -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 <unifiedAppId> --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 <unifiedAppId> --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 <unifiedAppId> " +
"--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 不发起任何远端读。
+21
View File
@@ -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,