From 3738f0bea2d807a09c0870f8871cbf81f9887912 Mon Sep 17 00:00:00 2001 From: chendelian <116870791@qq.com> Date: Sun, 16 Aug 2026 14:26:56 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B2=89=E6=B7=80=20Action=20=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=20visibleWhen=20=E6=9D=A1=E4=BB=B6=E6=98=BE=E9=9A=90?= =?UTF-8?q?=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++ SKILL.md | 2 +- assets/examples/README.md | 1 + .../examples/visible-when-action.example.json | 72 +++++++++++++++++++ assets/schemas/skill-actions.schema.json | 42 +++++++++++ development/CHECKLIST.md | 1 + development/SKILL_ACTION_RUNTIME.md | 4 +- references/ACTIONS.md | 47 +++++++++++- references/POLICY.md | 7 +- scripts/util/constants.py | 2 +- tests/test_actions_manifest.py | 34 +++++++++ tests/test_docs_standards.py | 25 +++++++ 12 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 assets/examples/visible-when-action.example.json diff --git a/CHANGELOG.md b/CHANGELOG.md index c876eb0..1629067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.84 + +- 沉淀 Action 参数表单字段条件显隐契约 `visibleWhen`(先选模式再出对应参数) +- 文档入口:`ACTIONS.md` / `SKILL_ACTION_RUNTIME.md` / `CHECKLIST` A4-7;与 Policy L3 行态 `visibleWhen` 消歧 +- 新增示例 `assets/examples/visible-when-action.example.json`;Schema 已支持该字段 + ## 1.0.83 - 沉淀跨表 Lookup 契约:Action 可用内联 `lookup` 或绑列下拉,禁止让用户手填内部编号 diff --git a/SKILL.md b/SKILL.md index 14f4801..65c25a4 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.83 +version: 1.0.84 author: 深圳匠厂科技有限公司 metadata: openclaw: diff --git a/assets/examples/README.md b/assets/examples/README.md index eae6f0b..c7d5812 100644 --- a/assets/examples/README.md +++ b/assets/examples/README.md @@ -5,3 +5,4 @@ - `skill-cell-presentation.example.json`:数据管理单元格呈现文档形状参考(见 `references/CELL_PRESENTATION.md`)。 - `render-html.action.example.json`:HTML 报告「重新生成」Action 形状参考(固定 id `render-html`;见 `references/ACTIONS.md`)。 - `lookup-action.example.json`:Action 内联 Lookup 下拉形状参考(跨表引用勿填编号;见 `references/ACTIONS.md`「跨表引用:Lookup」)。 +- `visible-when-action.example.json`:Action 参数字段条件显隐 `visibleWhen` 形状参考(互斥模式联动;见 `references/ACTIONS.md`「字段条件显隐」;≠ Policy L3)。 diff --git a/assets/examples/visible-when-action.example.json b/assets/examples/visible-when-action.example.json new file mode 100644 index 0000000..8b5de4f --- /dev/null +++ b/assets/examples/visible-when-action.example.json @@ -0,0 +1,72 @@ +{ + "_comment": "形状参考:互斥模式用 inputSchema.properties.*.visibleWhen 联动显隐,勿只靠 description 灰字。完整契约见 references/ACTIONS.md「字段条件显隐」。勿与 POLICY.md L3 行态 visibleWhen 混淆。", + "schemaVersion": 1, + "skill": "your-skill-slug", + "actions": [ + { + "id": "scrape-links-demo", + "label": "采集链接(示例)", + "description": "演示按采集方式切换参数:关键词路径 vs 作者主页路径。", + "placements": ["toolbar"], + "executionProfile": "async", + "interaction": "form", + "bind": { + "tables": ["demo_items"] + }, + "confirmation": { + "message": "将按所选方式采集链接(示例确认文案)。是否继续?" + }, + "entrypoint": { + "type": "cli", + "command": "run", + "args": [ + "--collect-mode", + "{{collectMode}}", + "--keyword", + "{{keyword}}", + "--author-url", + "{{authorUrl}}", + "--max-count", + "{{maxCount}}" + ] + }, + "inputSchema": { + "type": "object", + "required": ["collectMode"], + "properties": { + "collectMode": { + "type": "string", + "title": "采集方式", + "description": "先选方式,下方只出现对应参数。", + "enum": ["按关键词采集", "按作者主页采集"], + "default": "按关键词采集" + }, + "keyword": { + "type": "string", + "title": "关键词", + "description": "例如 rpa。", + "visibleWhen": { + "field": "collectMode", + "equals": "按关键词采集" + } + }, + "authorUrl": { + "type": "string", + "title": "作者主页链接", + "description": "例如 https://example.com/author/...", + "display_type": "url", + "visibleWhen": { + "collectMode": "按作者主页采集" + } + }, + "maxCount": { + "type": "number", + "title": "数量上限", + "default": 50, + "minimum": 1 + } + } + } + } + ] +} diff --git a/assets/schemas/skill-actions.schema.json b/assets/schemas/skill-actions.schema.json index 809beca..d0dd02a 100644 --- a/assets/schemas/skill-actions.schema.json +++ b/assets/schemas/skill-actions.schema.json @@ -131,6 +131,48 @@ "lookup": { "$ref": "#/$defs/lookupConfig", "description": "可选。内联同库表关联 Lookup(与列 options_json lookup 对象同构)。用于当前表没有对应列时;推荐同时设 display_type=lookup。见 ACTIONS.md「跨表引用:Lookup」。" + }, + "visibleWhen": { + "description": "可选。表单字段条件显隐。宿主在数据管理/定时任务参数表单中:规则不满足则隐藏该字段、提交时省略、且不校验 required。支持 {field,equals} / {field,in:[...]},或单键简写 {collectMode:\"按关键词采集\"} / {collectMode:[\"a\",\"b\"]}。Agent/CLI 无 UI 时同样按当前 input 求值后剥离隐藏字段。", + "oneOf": [ + { + "type": "object", + "required": ["field", "equals"], + "additionalProperties": false, + "properties": { + "field": { "type": "string", "minLength": 1 }, + "equals": { "type": ["string", "number", "boolean"] } + } + }, + { + "type": "object", + "required": ["field", "in"], + "additionalProperties": false, + "properties": { + "field": { "type": "string", "minLength": 1 }, + "in": { + "type": "array", + "minItems": 1, + "items": { "type": ["string", "number", "boolean"] } + } + } + }, + { + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "additionalProperties": { + "oneOf": [ + { "type": ["string", "number", "boolean"] }, + { + "type": "array", + "minItems": 1, + "items": { "type": ["string", "number", "boolean"] } + } + ] + } + } + ] } } }, diff --git a/development/CHECKLIST.md b/development/CHECKLIST.md index ef653b2..adc8b48 100644 --- a/development/CHECKLIST.md +++ b/development/CHECKLIST.md @@ -97,6 +97,7 @@ checklist_version: 1.0 - [ ] A4-4 **P1** 进行中的进度/步骤信息对用户可理解 - [ ] A4-5 **P1** 已对外暴露的动作参数覆盖主路径关键输入(无“命令行能做、动作入口不能做”的关键缺口) - [ ] A4-6 **P0** 若 Action 需引用同库另一张业务表(话术/平台/账号等):参数为 Lookup 下拉(内联 `lookup` 或绑列),**不是**手填内部编号;可选「自动」可留空提交 +- [ ] A4-7 **P1** 若 Action 有互斥采集/操作模式(如「按关键词 / 按主页」):用 `inputSchema` 字段级 **`visibleWhen`** 联动显隐,勿只靠 `description` 灰字说明「仅某某时填写」(见 `references/ACTIONS.md`;勿与 Policy L3 行态 `visibleWhen` 混淆) --- diff --git a/development/SKILL_ACTION_RUNTIME.md b/development/SKILL_ACTION_RUNTIME.md index ae2f45f..6de2fe3 100644 --- a/development/SKILL_ACTION_RUNTIME.md +++ b/development/SKILL_ACTION_RUNTIME.md @@ -7,7 +7,7 @@ | 文档 | 职责 | |------|------| | 本文 | sync/async、任务中心、Agent 禁令、单内核编排、队列 pick、同步 vs 导出 | -| [`../references/ACTIONS.md`](../references/ACTIONS.md) | `actions.json` 字段、placements、`bind.tables`、inputSchema、跨表 Lookup | +| [`../references/ACTIONS.md`](../references/ACTIONS.md) | `actions.json` 字段、placements、`bind.tables`、inputSchema、跨表 Lookup、字段条件显隐 `visibleWhen` | | [`LOGGING.md`](LOGGING.md) | `job_context` / `emit` / `finish` / Run Journal | | [`DATA_PATHS.md`](DATA_PATHS.md) | 下载/导入等落盘路径 | @@ -19,7 +19,7 @@ 1. **宿主管调度,技能管业务** — 不自行实现任务中心 / Job 轮询 / 后台守护进程。 2. **单业务内核、多入口** — 数据管理、定时任务、Agent、技能详情与直接 CLI 复用同一 domain service;禁止按入口复制业务逻辑。 -3. **五职责正交** — `placements` / `bind.tables` / `executionProfile` / `entrypoint` / domain service 各管一事(见 ACTIONS.md)。可选第六轴 **policy**(见 `references/POLICY.md`):未启用 = L0 与今日一致。可选 **`hostEffects` / `steps`**(见 ACTIONS.md):数据管理交互前后的 UI/级联编排;省略 = 不额外改 UI(匠厂宿主 ≥2.8.5)。可选 **`hoverHint`**(见 ACTIONS.md):数据管理工具栏悬停;省略 = 不显示,不回退说明或确认黄条。可选 **`cellPresentation`**(见 `references/CELL_PRESENTATION.md`):单元格语义标色;省略 = P0。 +3. **五职责正交** — `placements` / `bind.tables` / `executionProfile` / `entrypoint` / domain service 各管一事(见 ACTIONS.md)。可选第六轴 **policy**(见 `references/POLICY.md`):未启用 = L0 与今日一致。可选 **`hostEffects` / `steps`**(见 ACTIONS.md):数据管理交互前后的 UI/级联编排;省略 = 不额外改 UI(匠厂宿主 ≥2.8.5)。可选 **`hoverHint`**(见 ACTIONS.md):数据管理工具栏悬停;省略 = 不显示,不回退说明或确认黄条。可选 **`visibleWhen`**(见 ACTIONS.md):Action 参数表单字段条件显隐;省略 = 字段始终平铺;**勿与** Policy L3 行态 `visibleWhen` 混淆。可选 **`cellPresentation`**(见 `references/CELL_PRESENTATION.md`):单元格语义标色;省略 = P0。 4. **长耗时建议 async** — 开浏览器、RPA、HTTP 大文件下载等建议 `executionProfile: "async"`,进**任务中心**;这是对**方法**的选择,**不**限制可以出现在哪些 placement / 入口。 5. **Agent:短 CLI / 长 Action** — 短查询可用共享 Python bash CLI;长任务 / RPA **禁止** `exec` + `process poll`,必须 `run_skill_action`。 6. **进度走 Journal** — `emit` / `video.add_step` → Run Journal;UI / Skill Run Card / 任务中心展示;**不靠 stdout 流**。 diff --git a/references/ACTIONS.md b/references/ACTIONS.md index b588e8e..7c6fe45 100644 --- a/references/ACTIONS.md +++ b/references/ACTIONS.md @@ -86,6 +86,7 @@ Action 是**可选能力**: | **`executionProfile`** | 宿主是**同步等待**还是**异步 Job** | 不限制可展示入口,不决定是否弹窗 | | **`interaction`** | 可点击入口(数据管理 toolbar/row/batch、默认 CRUD 接管、未来 skill-detail)**点了怎么交互** | 不决定 sync/async;**cron 触发与 agent 忽略** | | **`hoverHint`** | **可选**:数据管理**工具栏悬停**文案 | 不决定短按钮名;不决定点下去弹不弹窗;**不回退** `description` / `confirmation.message`;省略 = 不显示悬停。当前宿主实现为顶栏技能按钮 | +| **`inputSchema.visibleWhen`** | **可选**:Action **参数表单**字段条件显隐(先选模式再出对应参数) | **不是** Data Policy L3 行态 `visibleWhen`;不决定 placements;旧宿主忽略则字段平铺全显 | | **`hostEffects` / `steps`** | **可选**:数据管理交互跑完前后,宿主要不要刷新表 / 清选区 / 级联删等 | 不决定业务写什么;**省略 = 宿主不额外改 UI**(≥2.8.5 起不再硬编码 mutation 后刷新);cron/agent 忽略 | | **`cellPresentation`** | **可选**:某表某行某列的语义标色/强调(tone + 说明) | 不替代业务列;不下发 CSS;用途不限(超时、预警、驳回等);见 [`CELL_PRESENTATION.md`](CELL_PRESENTATION.md) | | **`policy` / Snapshot** | **可选**:当前身份在数据管理下允许哪些 Action / 宿主内置能力 / 行列范围 | 不替代 placements;不替代 domain 写路径鉴权;多数技能不配 = L0 全开 | @@ -106,9 +107,10 @@ Action 是**可选能力**: | **bind** | `{ "tables": ["business_table"] }`;可选 `inputMapping`(行内/批量预填,见下节) | | **policy** | 可选;`enabled` + `entrypoint`(推荐 `policy` CLI);见 POLICY.md。未启用 = L0 | | **cellPresentation** | 可选;`{ "enabled": true }` 时宿主应加载单元格呈现;见 [`CELL_PRESENTATION.md`](CELL_PRESENTATION.md)。未启用 = P0 | -| inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、`default`、`required`、`sensitive`、`readOnly`;可选 `display_type` / `format`(`textarea` / `date` / `datetime_unix_seconds` / `url` / `email`) | +| inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、`default`、`required`、`sensitive`、`readOnly`;可选 `display_type` / `format`(`textarea` / `date` / `datetime_unix_seconds` / `url` / `email` / `lookup`);可选 **`visibleWhen`**(字段条件显隐,见专节) | | confirmation | `{ "message": "..." }` — **风险文案**(confirm/form 弹窗黄条;agent 元数据);**不是**「要不要弹窗」的开关 | | **hoverHint** | 可选字符串。数据管理**工具栏悬停**文案;与 `description` / `confirmation.message` **独立**。省略则不显示悬停,不回退。短名已够懂时不要写。需宿主已支持该字段;当前实现为顶栏技能按钮,不含宿主内置删除/刷新/新增/导入/导出 | +| **visibleWhen** | 可选。写在 **property** 上(非 action 顶层):依赖同表单其它字段值决定是否展示;隐藏字段不提交、不校验 `required`。**勿与** [`POLICY.md`](POLICY.md) L3 行态 `visibleWhen` 混淆 | | **executionProfile** | `"sync"` \| `"async"`(**必须显式写出**,禁止依赖缺省) | | **interaction** | `"auto"` \| `"direct"` \| `"confirm"` \| `"form"`(可选;省略≈`auto`) | | **interactionByPlacement** | 可选;按 placement 覆盖为 `direct`/`confirm`/`form`(不可写 `auto`) | @@ -656,8 +658,9 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件 | 说明灰字 | `description` | `_jiangchang_columns.description` | | 默认值 | `default` | SQLite 列 `DEFAULT` | | 只读 | `readOnly: true` | 列 `editable: 0` / 宿主只读规则 | +| **条件显隐** | **`visibleWhen`**(见下节) | **本版 CRUD 无对称**;仅 Action / 定时任务参数表单 | -**本版不做(勿宣传):** 文件上传、多选/标签数组、复杂 `oneOf` / nested object 专用表单。 +**本版不做(勿宣传):** 文件上传、多选/标签数组、复杂 `oneOf` / nested object 专用表单;`visibleWhen` 多条件 AND/OR 树、跨 Action 依赖、CRUD 新增/编辑对称显隐。 日期时间约定: @@ -674,6 +677,46 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件 - 属性级 `description`(表单标签下方灰字说明) - 简单模板替换(`{{fieldName}}`) - 可选空数字字段提交时由宿主省略,避免 `""` 触发 number 校验失败 +- **字段条件显隐 `visibleWhen`**(可选):依赖同表单其它字段值决定是否展示;隐藏字段不进提交、不校验 `required` + +### 字段条件显隐(`visibleWhen`) + +用于「先选模式,再出对应参数」类表单。写在 `inputSchema.properties.` 上(**不是** action 顶层字段)。 + +> **消歧义:** 本文的 `visibleWhen` = **Action 参数表单控件显隐**。 +> [`POLICY.md`](POLICY.md) L3 预留的 `visibleWhen` = **行态/权限策略**(未冻结),**不是**同一能力。勿混用、勿互相替代。 + +```json +"keyword": { + "type": "string", + "title": "关键词", + "visibleWhen": { "field": "collectMode", "equals": "按关键词采集" } +}, +"authorUrl": { + "type": "string", + "title": "作者主页链接", + "visibleWhen": { "collectMode": "按作者主页采集" } +} +``` + +支持形态: + +| 写法 | 含义 | +|------|------| +| `{ "field": "x", "equals": "a" }` | `x === a` 时显示 | +| `{ "field": "x", "in": ["a", "b"] }` | `x` 属于集合时显示 | +| `{ "x": "a" }` / `{ "x": ["a","b"] }` | 单键简写,同上 | + +宿主行为(需已支持该字段的匠厂宿主;更旧版本会忽略未知字段 → 参数平铺全显): + +- 数据管理 / 定时任务参数表单按当前值求值并隐藏 +- 若依赖字段自身因 `visibleWhen` 隐藏,则下游字段一并隐藏(避免切模式后残留) +- 提交与服务端 `validateActionInput` 会剥离隐藏字段;隐藏字段不校验 `required` +- 无 `visibleWhen` 的字段保持始终可见(兼容旧技能) + +形状参考:[`assets/examples/visible-when-action.example.json`](../assets/examples/visible-when-action.example.json)。 + +互斥模式**优先**用 `visibleWhen`,不要只靠 `description` 灰字写「仅某某时填写」。 **与数据管理「新增 / 编辑」表单的分工(勿混用配置入口):** diff --git a/references/POLICY.md b/references/POLICY.md index f4dbb2e..3df9a74 100644 --- a/references/POLICY.md +++ b/references/POLICY.md @@ -2,7 +2,8 @@ > **状态:正式契约 v1.0(`schemaVersion` 1)。** > **目标:** 冻结「启用方式 + Snapshot 最小字段 + L2 scope 白名单」,使多数技能(L0)零配置,需要权限的技能(L1/L2)有稳定扩展面。 -> **非目标:** 宿主实现业务用户/角色库;规定飞书/HTTP/本地表等权限数据源;本期不强制 L3(`visibleWhen` 等)。 +> **非目标:** 宿主实现业务用户/角色库;规定飞书/HTTP/本地表等权限数据源;本期不强制 L3(行态 `visibleWhen` 等)。 +> **注意:** L3 预留的行态 `visibleWhen` **不是** Action 参数表单的 `inputSchema.properties.*.visibleWhen`(表单控件显隐,见 [`ACTIONS.md`](ACTIONS.md)「字段条件显隐」)。二者同名不同义,勿混用。 > **配套 Schema:** [`../assets/schemas/skill-policy-snapshot.schema.json`](../assets/schemas/skill-policy-snapshot.schema.json);启用块见 [`../assets/schemas/skill-actions.schema.json`](../assets/schemas/skill-actions.schema.json)。 相关正交轴见 [`ACTIONS.md`](ACTIONS.md)。本契约增加第六轴:**policy(当前身份允许什么)**。 @@ -16,7 +17,7 @@ | **L0** | 无 `policy`,或 `policy.enabled !== true` | **与今日完全一致**:不拉 Snapshot,不按身份过滤按钮/行/列 | | **L1** | `policy.enabled: true`,Snapshot 至少给出 Action 授权 | 数据管理展示 skill Action 前与 allowlist 求交;可选限制宿主内置能力 | | **L2** | 在 L1 基础上 Snapshot 带 `data.tables[table]` | 列表查询应用声明式 `rows.scope`;列按 `columns` 投影 | -| **L3** | (预留,本契约不冻结行为) | 如行态 `visibleWhen`、菜单级藏表、`rows.mode=delegate` 等 | +| **L3** | (预留,本契约不冻结行为) | 如**行态** `visibleWhen`、菜单级藏表、`rows.mode=delegate` 等(≠ Action 表单 `inputSchema.visibleWhen`) | **80% 技能停留 L0。** 启用 policy 的技能必须实现约定 CLI,并在写路径自行再鉴权(见 §7)。 @@ -366,7 +367,7 @@ IF mode == open: 候选不变 |----|------| | `actions.mode: denylist` | 另开 `schemaVersion` | | `valueFrom: actor.roles` | **明确不做** | -| `visibleWhen` / 嵌套 scope / `like` | L3 或永不做 | +| `visibleWhen` / 嵌套 scope / `like` | L3 或永不做(此处指**行态**策略字段;Action 表单 `inputSchema.visibleWhen` 见 ACTIONS.md,不属本表) | | `rows.mode: delegate` 宿主实现 | 字段名已占坑 | | 平台全局 Role UI / cron·agent 套用 Snapshot | 另议 | diff --git a/scripts/util/constants.py b/scripts/util/constants.py index bc07c5f..8af7e30 100644 --- a/scripts/util/constants.py +++ b/scripts/util/constants.py @@ -1,6 +1,6 @@ """技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。""" SKILL_SLUG = "your-skill-slug" -SKILL_VERSION = "1.0.83" +SKILL_VERSION = "1.0.84" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" PLATFORM_KIT_MIN_VERSION = "1.2.11" diff --git a/tests/test_actions_manifest.py b/tests/test_actions_manifest.py index 08d28dd..222735b 100644 --- a/tests/test_actions_manifest.py +++ b/tests/test_actions_manifest.py @@ -257,6 +257,38 @@ class TestActionsManifest(unittest.TestCase): self.assertNotIn("hoverHint", action_def.get("required") or []) self.assertEqual(props["hoverHint"].get("type"), "string") + def test_schema_defines_visible_when_on_input_property(self) -> None: + schema = _load_actions_schema() + prop_def = schema["$defs"]["inputProperty"] + props = prop_def["properties"] + self.assertIn("visibleWhen", props) + self.assertNotIn("visibleWhen", prop_def.get("required") or []) + # Must live on input property, not action top-level (≠ Policy L3). + action_props = schema["$defs"]["action"]["properties"] + self.assertNotIn("visibleWhen", action_props) + + def test_default_manifest_omits_visible_when(self) -> None: + for action in _load_actions_manifest()["actions"]: + properties = ((action.get("inputSchema") or {}).get("properties")) or {} + for name, prop in properties.items(): + self.assertNotIn( + "visibleWhen", + prop if isinstance(prop, dict) else {}, + msg=f"{action['id']}.{name} default template should omit visibleWhen", + ) + + def test_visible_when_example_exists_and_uses_field_rules(self) -> None: + path = os.path.join( + get_skill_root(), "assets", "examples", "visible-when-action.example.json" + ) + self.assertTrue(os.path.isfile(path), msg="visible-when example must exist") + with open(path, encoding="utf-8") as f: + example = json.load(f) + props = example["actions"][0]["inputSchema"]["properties"] + self.assertIn("visibleWhen", props["keyword"]) + self.assertIn("visibleWhen", props["authorUrl"]) + self.assertNotIn("visibleWhen", props["collectMode"]) + def test_default_manifest_omits_hover_hint(self) -> None: for action in _load_actions_manifest()["actions"]: self.assertNotIn( @@ -554,6 +586,8 @@ class TestActionsManifest(unittest.TestCase): "hostEffects", "steps", "hoverHint", + "visibleWhen", + "字段条件显隐", "reloadTable", "afterSuccess", "afterFailure", diff --git a/tests/test_docs_standards.py b/tests/test_docs_standards.py index 749ed75..c6b7dbb 100644 --- a/tests/test_docs_standards.py +++ b/tests/test_docs_standards.py @@ -363,9 +363,34 @@ class TestDocsStandards(unittest.TestCase): "_jiangchang_columns.description", "datetime_unix_seconds", "SCHEMA.md", + "字段条件显隐", + "visibleWhen", ): self.assertIn(marker, text, msg=f"ACTIONS.md missing {marker!r}") + def test_actions_md_disambiguates_visible_when_from_policy(self) -> None: + actions = self._read("references/ACTIONS.md") + policy = self._read("references/POLICY.md") + self.assertIn("字段条件显隐", actions) + self.assertIn("inputSchema.properties", actions) + self.assertTrue( + "POLICY.md" in actions and "L3" in actions, + msg="ACTIONS.md must point readers away from Policy L3 visibleWhen", + ) + self.assertTrue( + "inputSchema" in policy and "ACTIONS.md" in policy, + msg="POLICY.md must disambiguate L3 visibleWhen from Action form visibleWhen", + ) + + def test_visible_when_example_is_listed(self) -> None: + text = self._read("assets/examples/README.md") + self.assertIn("visible-when-action.example.json", text) + checklist = self._read("development/CHECKLIST.md") + self.assertIn("A4-7", checklist) + self.assertIn("visibleWhen", checklist) + runtime = self._read("development/SKILL_ACTION_RUNTIME.md") + self.assertIn("visibleWhen", runtime) + def test_guarded_docs_do_not_reference_amazon_implementation(self) -> None: forbidden = ( "download-settlement-report-amazon",