From afe0788488ad9059aed0ea3385930a284257db6c Mon Sep 17 00:00:00 2001 From: chendelian <116870791@qq.com> Date: Tue, 29 Sep 2026 20:41:23 +0800 Subject: [PATCH] Release v1.0.120: document bind.visibleWhen for row menu visibility --- CHANGELOG.md | 6 ++ SKILL.md | 3 +- assets/README.md | 2 +- assets/examples/README.md | 3 +- .../bind-visible-when-row.action.example.json | 39 ++++++++ assets/schemas/skill-actions.schema.json | 90 ++++++++++--------- development/CHECKLIST.md | 5 +- development/SKILL_ACTION_RUNTIME.md | 4 +- references/ACTIONS.md | 46 ++++++++-- references/POLICY.md | 8 +- scripts/util/constants.py | 2 +- tests/test_actions_manifest.py | 59 +++++++++++- tests/test_docs_standards.py | 6 ++ 13 files changed, 215 insertions(+), 58 deletions(-) create mode 100644 assets/examples/bind-visible-when-row.action.example.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb3d76..ddb57d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.120 + +- 正式沉淀行菜单显隐契约 **`bind.visibleWhen`**(宿主已支持:按当前行列值显示/隐藏行按钮) +- 与表单字段 `visibleWhen`、Policy L3 预留消歧;更新 Schema / ACTIONS / POLICY / CHECKLIST A4-11 / RUNTIME +- 新增示例 `bind-visible-when-row.action.example.json` + ## 1.0.119 - 沉淀匠厂宿主 **Action Lookup 三源**:`local_sql`(同库)/ **`skill_cli`(兄弟技能 CLI JSON)** / `external_api`(预留) diff --git a/SKILL.md b/SKILL.md index 610f280..b269489 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.119 +version: 1.0.120 author: 深圳匠厂科技有限公司 metadata: openclaw: @@ -39,6 +39,7 @@ allowed-tools: - **`placements`**:只决定 Action **出现在哪里**(toolbar / row / batch / cron / agent / skill-detail)。其中 `row` 表示**行级入口**(宿主可呈现为操作列、行右键、行内「⋯」等;技能配什么就显示什么,无独立菜单 DSL)。数据管理技能按钮顺序见 `actions` **数组相对顺序**(详见 `references/ACTIONS.md`),无独立 sortOrder。 - **`bind.tables`**:只决定数据管理出现在哪些表;含 `toolbar` 时**必须**显式非空绑定。 - **`bind.selectionMode`(可选)**:表勾选如何驱动 Action(`required` / `none` / `optional`);省略由宿主推断。同一能力勿拆两个同义顶栏按钮,见 `references/ACTIONS.md`。 +- **`bind.visibleWhen`(可选)**:`row` 菜单/按钮按当前行列值显隐(如仅已采用可入队);省略则始终显示。≠ 表单字段 `visibleWhen`,≠ Policy L3。见 `references/ACTIONS.md`「行菜单显隐」。 - **用户可见文案**:数据管理弹窗 / 任务中心文案须全中文产品用语;勿把 `pickCount`、`async`、`pending`、CLI 子命令写进弹窗(见 `references/ACTIONS.md`「用户可见文案」)。 - **`executionProfile`**:只决定执行方式——`sync` 当场返回结果,`async` 进**任务中心**。与 placements / 入口**无关**。心智上多数为 sync,仅长任务 / RPA 标 async;宿主省略该字段时按 sync。每个 Action **仍必须**显式写 `sync` 或 `async`。 - **`policy`(可选)**:需要按用户/角色控制数据管理按钮或行列时,在 `actions.json` 顶层启用 Data Policy,由技能 `policy` CLI 输出 Snapshot;**未启用 = 与今日一致(L0)**。详见 `references/POLICY.md`。用户/角色数据源(飞书/库/API)属技能内部,宿主不托管。 diff --git a/assets/README.md b/assets/README.md index 3f28dd5..e03c2ee 100644 --- a/assets/README.md +++ b/assets/README.md @@ -8,7 +8,7 @@ - `examples/`:CLI 成功输出形状示例、Action 形状参考(含 `data-sources.mysql.example.json`、`path-root-file-action.example.json`、`selection-label-column.action.example.json`、`selection-mode-optional.action.example.json`、`render-html.action.example.json` 等;虚构路径与数据)。 - `schemas/`:轻量 JSON Schema(`skill-actions.schema.json` 为**新技能严格规范**)。 -`skill-actions.schema.json` 使用 `additionalProperties: false`。正式支持 `bind.tables`(toolbar Action 必填)与可选 `bind.inputMapping` / `columnMapping` / **`labelColumn`**(选择摘要标签列)/ **`selectionMode`**(`required` \| `none` \| `optional`:表勾选如何驱动 Action;省略则宿主兼容推断)。placements 含 `toolbar` / `row` / `batch` / `cron` / `agent` / `skill-detail`(对齐匠厂宿主)。数据管理 confirm/form 弹层在会应用勾选时展示「已选择」摘要;**form + 有摘要时映射 `readOnly` 默认不占参数区(仍提交)**,可选 **`revealInForm`** 强制显示。弹窗 `description` / 确认黄条 / 参数说明须**用户可见中文**(勿写 `pickCount`/`async`/`pending`/CLI 子命令;见 ACTIONS.md「用户可见文案」)。可选 `hoverHint` 为数据管理工具栏悬停(省略不显示,不回退)。`concurrency`/`locks` 仍为预留。`placements` 与 `executionProfile` 正交——进任务中心只看 `async`,与入口无关。表级 Action 与 Agent 契约见 [`references/ACTIONS.md`](../references/ACTIONS.md);开发深度规范见 [`development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。 +`skill-actions.schema.json` 使用 `additionalProperties: false`。正式支持 `bind.tables`(toolbar Action 必填)与可选 `bind.inputMapping` / `columnMapping` / **`labelColumn`**(选择摘要标签列)/ **`selectionMode`**(`required` \| `none` \| `optional`:表勾选如何驱动 Action;省略则宿主兼容推断)/ **`visibleWhen`**(行菜单/行按钮按当前行列值显隐)。placements 含 `toolbar` / `row` / `batch` / `cron` / `agent` / `skill-detail`(对齐匠厂宿主)。数据管理 confirm/form 弹层在会应用勾选时展示「已选择」摘要;**form + 有摘要时映射 `readOnly` 默认不占参数区(仍提交)**,可选 **`revealInForm`** 强制显示。弹窗 `description` / 确认黄条 / 参数说明须**用户可见中文**(勿写 `pickCount`/`async`/`pending`/CLI 子命令;见 ACTIONS.md「用户可见文案」)。可选 `hoverHint` 为数据管理工具栏悬停(省略不显示,不回退)。`concurrency`/`locks` 仍为预留。`placements` 与 `executionProfile` 正交——进任务中心只看 `async`,与入口无关。表级 Action 与 Agent 契约见 [`references/ACTIONS.md`](../references/ACTIONS.md);开发深度规范见 [`development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。 - 用户市场四 Tab 见根目录 [`README.md`](../README.md) / [`TUTORIAL.md`](../TUTORIAL.md) / [`DEMO.md`](../DEMO.md) / [`CHANGELOG.md`](../CHANGELOG.md);写法契约见 [`development/TUTORIAL_SURFACES.md`](../development/TUTORIAL_SURFACES.md) - Agent 调用/编排参考见 [`references/`](../references/)(含 [`ACTIONS.md`](../references/ACTIONS.md)) diff --git a/assets/examples/README.md b/assets/examples/README.md index 40dd30e..e78052a 100644 --- a/assets/examples/README.md +++ b/assets/examples/README.md @@ -9,7 +9,8 @@ - `lookup-action.example.json`:Action 内联 Lookup(**local_sql**)形状参考(单选 + `multiple: true`;同库跨表勿填编号;见 `references/ACTIONS.md`「跨表引用:Lookup」)。 - `lookup-skill-cli.action.example.json`:Action Lookup **`skill_cli`** 形状参考(打开表单时宿主调兄弟技能 CLI 抽 JSON 选项;跨技能账号列表勿再造本库缓存表;见同节)。 - `year-month-action.example.json`:Action / CRUD 账期月份字段 `display_type: year_month` 形状参考(见 `references/ACTIONS.md`「表单字段类型对照」)。 -- `visible-when-action.example.json`:Action 参数字段条件显隐 `visibleWhen` 形状参考(互斥模式联动;见 `references/ACTIONS.md`「字段条件显隐」;≠ Policy L3)。 +- `visible-when-action.example.json`:Action 参数字段条件显隐 `visibleWhen` 形状参考(互斥模式联动;见 `references/ACTIONS.md`「字段条件显隐」;≠ `bind.visibleWhen`,≠ Policy L3)。 +- `bind-visible-when-row.action.example.json`:行菜单显隐 `bind.visibleWhen` 形状参考(按当前行状态显示/隐藏行按钮;见 `references/ACTIONS.md`「行菜单显隐」)。 - `selection-label-column.action.example.json`:选择摘要 `bind.labelColumn` 形状参考(见 `references/ACTIONS.md`「选择摘要」)。 - `selection-mode-optional.action.example.json`:`bind.selectionMode: "optional"` 智能勾选/按数量合并形状参考;用户可见文案为口语中文(见 `references/ACTIONS.md`「勾选驱动模式」「用户可见文案」)。 - `replaces-create.action.example.json` / `replaces-update.action.example.json` / `replaces-delete.action.example.json` / `replaces-import.action.example.json`:数据管理默认按钮接管(`replaces`)形状参考;**import 时 UI 仍用宿主「导入数据」面板**,确认后调技能写库(见 `references/ACTIONS.md`「`replaces`」)。 diff --git a/assets/examples/bind-visible-when-row.action.example.json b/assets/examples/bind-visible-when-row.action.example.json new file mode 100644 index 0000000..cc5db2a --- /dev/null +++ b/assets/examples/bind-visible-when-row.action.example.json @@ -0,0 +1,39 @@ +{ + "id": "approve-item", + "label": "采用", + "description": "把当前记录改为已采用。仅待确认或已跳过的行显示此按钮。", + "placements": ["row", "agent"], + "executionProfile": "sync", + "interaction": "direct", + "bind": { + "tables": ["business_items"], + "visibleWhen": { + "field": "status", + "in": ["pending_review", "skipped"] + }, + "labelColumn": "display_name", + "inputMapping": { + "itemId": "$row.$pk" + } + }, + "inputSchema": { + "type": "object", + "required": ["itemId"], + "properties": { + "itemId": { + "type": "number", + "title": "编号", + "description": "行内触发时自动填入,一般无需修改", + "readOnly": true + } + } + }, + "entrypoint": { + "type": "cli", + "command": "approve", + "args": ["--id", "{{itemId}}"] + }, + "hostEffects": { + "onSuccess": ["reloadTable"] + } +} diff --git a/assets/schemas/skill-actions.schema.json b/assets/schemas/skill-actions.schema.json index 833a58d..aa0e47b 100644 --- a/assets/schemas/skill-actions.schema.json +++ b/assets/schemas/skill-actions.schema.json @@ -90,6 +90,48 @@ "scalarArg": { "type": ["string", "number", "boolean"] }, + "visibleWhenRule": { + "description": "条件显隐规则。支持 {field,equals} / {field,in:[...]},或单键简写 {status:\"approved\"} / {status:[\"a\",\"b\"]}。", + "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"] } + } + ] + } + } + ] + }, "inputProperty": { "type": "object", "required": ["type"], @@ -142,46 +184,8 @@ "description": "可选。Action 内联 Lookup:local_sql(同库表)/ skill_cli(兄弟技能 CLI JSON)/ external_api(预留)。打开表单时由宿主解析。推荐同时设 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"] } - } - ] - } - } - ] + "description": "可选。表单字段条件显隐。宿主在数据管理/定时任务参数表单中:规则不满足则隐藏该字段、提交时省略、且不校验 required。Agent/CLI 无 UI 时同样按当前 input 求值后剥离隐藏字段。≠ bind.visibleWhen(行菜单),≠ Policy L3。", + "allOf": [{ "$ref": "#/$defs/visibleWhenRule" }] } } }, @@ -397,9 +401,13 @@ "type": "string", "enum": ["required", "none", "optional"], "description": "可选。数据管理勾选如何驱动本 Action:required=必须有勾选(无勾选禁用);none=忽略表勾选(不出现假「已选择」);optional=有勾选走 $selection.ids,无勾选走 pickCount/maxLeads 等空选数量字段。省略时宿主推断:有 $selection.ids 或 batch → required,否则 → none。与 placements/executionProfile 正交。" + }, + "visibleWhen": { + "description": "可选。placements 含 row 时:按当前行原始列值决定是否出现在行菜单/行按钮(宿主已支持)。形状同表单字段 visibleWhen。省略=始终显示。≠ inputSchema 字段 visibleWhen,≠ Policy L3 预留。见 ACTIONS.md「行菜单显隐」。", + "allOf": [{ "$ref": "#/$defs/visibleWhenRule" }] } }, - "description": "表级绑定;toolbar 必填。可选 inputMapping / columnMapping / labelColumn / selectionMode。concurrency/locks 勿写入。" + "description": "表级绑定;toolbar 必填。可选 inputMapping / columnMapping / labelColumn / selectionMode / visibleWhen。concurrency/locks 勿写入。" }, "hostEffectId": { "type": "string", diff --git a/development/CHECKLIST.md b/development/CHECKLIST.md index ad66874..74a68ac 100644 --- a/development/CHECKLIST.md +++ b/development/CHECKLIST.md @@ -109,10 +109,11 @@ checklist_version: 1.0 - [ ] A4-4 **P1** 进行中的进度/步骤信息对用户可理解 - [ ] A4-5 **P1** 已对外暴露的动作参数覆盖主路径关键输入(无“命令行能做、动作入口不能做”的关键缺口) - [ ] A4-6 **P0** 若 Action 需引用业务行(话术/平台/账号等):参数为 Lookup 下拉(**不是**手填内部编号);同库用 `local_sql`(内联或绑列),跨技能用 **`skill_cli`**(勿在本库造镜像缓存表);可选「自动」可留空提交(见 `references/ACTIONS.md`) -- [ ] A4-7 **P1** 若 Action 有互斥采集/操作模式(如「按关键词 / 按主页」):用 `inputSchema` 字段级 **`visibleWhen`** 联动显隐,勿只靠 `description` 灰字说明「仅某某时填写」(见 `references/ACTIONS.md`;勿与 Policy L3 行态 `visibleWhen` 混淆) -- [ ] A4-8 **P1** 行内/勾选打开的 `form`:有「已选择」时参数区不应再重复映射主键(`inputMapping` + `readOnly`);需要强制露出时用 `revealInForm`,**禁止**用 `visibleWhen` 藏主键(会不提交) +- [ ] A4-7 **P1** 若 Action 有互斥采集/操作模式(如「按关键词 / 按主页」):用 `inputSchema` 字段级 **`visibleWhen`** 联动显隐,勿只靠 `description` 灰字说明「仅某某时填写」(见 `references/ACTIONS.md`;勿与 **`bind.visibleWhen`** 行菜单、Policy L3 混淆) +- [ ] A4-8 **P1** 行内/勾选打开的 `form`:有「已选择」时参数区不应再重复映射主键(`inputMapping` + `readOnly`);需要强制露出时用 `revealInForm`,**禁止**用表单 `visibleWhen` 藏主键(会不提交) - [ ] A4-9 **P1** 若同一能力既支持「勾选处理」又支持「按数量从队列领取」:用一个 Action + `bind.selectionMode: "optional"`(勿拆两个同义顶栏按钮);无勾选可点、有勾选显示「已选择」、空选数量字段用 `pickCount` 等约定名(见 `references/ACTIONS.md`) - [ ] A4-10 **P0** 数据管理弹窗与任务失败文案:全中文产品用语;禁止在 `description` / `confirmation.message` / 参数 `description` / 用户向 `message` 写英文字段名、CLI 子命令、`async`/`pending`/`status=` 等(Agent 路由放 `SKILL.md`;见 `references/ACTIONS.md`「用户可见文案」) +- [ ] A4-11 **P1** 若行操作应按业务状态显隐(如仅待确认可「采用」、仅已采用可「加入待写」):用 **`bind.visibleWhen`**(对当前行列值求值;存库原始值如 `approved`);勿指望 Policy L3;CLI 侧仍须校验状态(见 `references/ACTIONS.md`「行菜单显隐」) --- ## 5. 账号与浏览器会话(网页 RPA 适用;其他形态标 N/A 或改测等价项) diff --git a/development/SKILL_ACTION_RUNTIME.md b/development/SKILL_ACTION_RUNTIME.md index 4b5383b..19ed30f 100644 --- a/development/SKILL_ACTION_RUNTIME.md +++ b/development/SKILL_ACTION_RUNTIME.md @@ -9,7 +9,7 @@ | 文档 | 职责 | |------|------| | 本文 | sync/async、任务中心、Agent 禁令、单内核编排、队列 pick、同步 vs 导出 | -| [`../references/ACTIONS.md`](../references/ACTIONS.md) | `actions.json` 字段、placements、`bind.tables`、`bind.selectionMode`、**用户可见文案**、inputSchema、跨表 Lookup、字段条件显隐 `visibleWhen`、本地路径 `file`/`directory` + 可选 **`pathRoot`**、选择摘要「已选择」与映射 **`readOnly` / `revealInForm`**、**按钮中文名 `label` 参考** | +| [`../references/ACTIONS.md`](../references/ACTIONS.md) | `actions.json` 字段、placements、`bind.tables`、`bind.selectionMode`、`bind.visibleWhen`(行菜单显隐)、**用户可见文案**、inputSchema、跨表 Lookup、字段条件显隐 `visibleWhen`、本地路径 `file`/`directory` + 可选 **`pathRoot`**、选择摘要「已选择」与映射 **`readOnly` / `revealInForm`**、**按钮中文名 `label` 参考** | | [`LOGGING.md`](LOGGING.md) | `job_context` / `emit` / `finish` / Run Journal | | [`DATA_PATHS.md`](DATA_PATHS.md) | 下载/导入等落盘路径 | @@ -21,7 +21,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):数据管理工具栏悬停;省略 = 不显示,不回退说明或确认黄条。可选 **`visibleWhen`**(见 ACTIONS.md):Action 参数表单字段条件显隐;省略 = 字段始终平铺;**勿与** Policy L3 行态 `visibleWhen` 混淆;**勿**用其藏映射主键。可选 **`pathRoot`**(见 ACTIONS.md):`file`/`directory` 原生选路对话框起始目录(相对技能数据根;省略默认 `{skill_data}/input`)。可选 **`bind.labelColumn`**(见 ACTIONS.md):选择摘要可读标签列;省略 = 宿主常见列名启发。可选 **`bind.selectionMode`**(见 ACTIONS.md):表勾选如何驱动 Action(`required` / `none` / `optional`);省略 = 宿主按 `$selection.ids` / `batch` 推断。可选 **`revealInForm`**(见 ACTIONS.md):有「已选择」时仍强制显示某 `readOnly` 字段;省略 = 映射只读默认不占 form 参数区(仍提交)。可选 **`cellPresentation`**(见 `references/CELL_PRESENTATION.md`):单元格/整行/整列/区域语义标色;省略 = P0。**用户可见文案**(见 ACTIONS.md「用户可见文案」):弹窗 `description` / 确认黄条 / 参数说明 / 任务失败 `message` 须全中文产品用语;Agent 路由勿塞进弹窗。 +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 参数表单字段条件显隐;省略 = 字段始终平铺;**勿与** **`bind.visibleWhen`**(行菜单)或 Policy L3 混淆;**勿**用其藏映射主键。可选 **`pathRoot`**(见 ACTIONS.md):`file`/`directory` 原生选路对话框起始目录(相对技能数据根;省略默认 `{skill_data}/input`)。可选 **`bind.labelColumn`**(见 ACTIONS.md):选择摘要可读标签列;省略 = 宿主常见列名启发。可选 **`bind.selectionMode`**(见 ACTIONS.md):表勾选如何驱动 Action(`required` / `none` / `optional`);省略 = 宿主按 `$selection.ids` / `batch` 推断。可选 **`bind.visibleWhen`**(见 ACTIONS.md「行菜单显隐」):`row` 菜单按当前行列值显隐;省略 = 始终显示。可选 **`revealInForm`**(见 ACTIONS.md):有「已选择」时仍强制显示某 `readOnly` 字段;省略 = 映射只读默认不占 form 参数区(仍提交)。可选 **`cellPresentation`**(见 `references/CELL_PRESENTATION.md`):单元格/整行/整列/区域语义标色;省略 = P0。**用户可见文案**(见 ACTIONS.md「用户可见文案」):弹窗 `description` / 确认黄条 / 参数说明 / 任务失败 `message` 须全中文产品用语;Agent 路由勿塞进弹窗。 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 e7c5fca..781047b 100644 --- a/references/ACTIONS.md +++ b/references/ACTIONS.md @@ -107,7 +107,7 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** | entrypoint | `type = cli`、`command`、`args`(**必填** JSON 数组,项为 `string` / `number` / `boolean`) | | placements | `toolbar`、`row`、`batch`、`skill-detail`、`agent`、`cron`;仅 `replaces` 时可为空数组 | | **replaces** | 可选 `"create"` \| `"update"` \| `"delete"` \| `"import"` — 接管默认新增/编辑/删除/导入(须 `bind.tables`) | -| **bind** | `{ "tables": ["business_table"] }`;可选 `inputMapping`、`columnMapping`、**`labelColumn`**、**`selectionMode`**(行内/批量预填、选择摘要标签、勾选驱动模式,见下节) | +| **bind** | `{ "tables": ["business_table"] }`;可选 `inputMapping`、`columnMapping`、**`labelColumn`**、**`selectionMode`**、**`visibleWhen`**(行内/批量预填、选择摘要标签、勾选驱动模式、**行菜单显隐**,见下节) | | **policy** | 可选;`enabled` + `entrypoint`(推荐 `policy` CLI);见 POLICY.md。未启用 = L0 | | **cellPresentation** | 可选;`{ "enabled": true }` 时宿主应加载格子与 grid 呈现;见 [`CELL_PRESENTATION.md`](CELL_PRESENTATION.md)。未启用 = P0 | | inputSchema | 根 `type: object`;properties 使用 `string` / `number` / `boolean`;支持 `enum`、`default`、`required`、`sensitive`、`readOnly`、可选 **`revealInForm`**;可选 `display_type` / `format`(含 `file` / `directory` 等);可选 **`pathRoot`**(file/directory 起始目录,相对技能数据根);可选 **`visibleWhen`**(字段条件显隐,见专节) | @@ -133,7 +133,7 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** 规则: -- `bind` 为 object,`additionalProperties: false`;当前允许 `tables`(必填键当存在 bind 时)与可选 `inputMapping`、`columnMapping`、**`labelColumn`**、**`selectionMode`**。 +- `bind` 为 object,`additionalProperties: false`;当前允许 `tables`(必填键当存在 bind 时)与可选 `inputMapping`、`columnMapping`、**`labelColumn`**、**`selectionMode`**、**`visibleWhen`**。 - `tables` 非空数组、元素唯一、英文 **snake_case**、不得为空字符串。 - **`placements` 含 `toolbar` 时必须显式声明非空 `bind.tables`**(模板 Schema `if/then` + 自动测试强制)。 - `row` / `batch` **建议**同样声明 `bind.tables`,避免在无关表上露出按钮;未声明时宿主可能按兼容逻辑在更多表上展示。 @@ -143,8 +143,42 @@ Action 是否提供、挂哪些 `placements`,由本技能 **入口定位表** - **`columnMapping`(可选)**:把 inputSchema 属性名映射到物理列名,供表单复用列展示名 / lookup。 - **`labelColumn`(可选)**:物理列名,供数据管理 Action 弹层「已选择」摘要展示 `#id + 标签`。**仅影响展示,不改变 CLI/提交 payload,也不决定业务是否跳过某些状态。** 省略时宿主用常见列名启发(如 `title` / `name` / `nickname` 等);再没有则只显示编号。列名不常见、或启发会选错列时再显式声明。 - **`selectionMode`(可选)**:数据管理**表勾选**如何驱动本 Action(与 `placements` / `executionProfile` 正交)。见下节「勾选驱动模式」。 +- **`visibleWhen`(可选)**:`placements` 含 `row` 时,按**当前行原始列值**决定是否出现在行菜单 / 行按钮。见下节「行菜单显隐」。 - `concurrency` / `locks` 仍未稳定,**不要**写入新技能 manifest。 +### 数据管理:行菜单显隐 `bind.visibleWhen` + +行级 Action 常需按业务状态显隐,例如:已采用不再出「采用」;仅已采用才出「加入待写」。**宿主已支持**写在 `bind.visibleWhen`(对当前行求值;不匹配则该行菜单不出现此项)。这是正式 Action 契约,**不是** Policy L3 预留,也**不是**表单字段 `inputSchema.properties.*.visibleWhen`。 + +| | `bind.visibleWhen` | 表单字段 `visibleWhen` | Policy L3(预留) | +|--|--------------------|------------------------|-------------------| +| 作用对象 | 行菜单 / 行按钮是否出现 | Action 参数表单里某个控件 | (未冻结)权限策略等 | +| 求值上下文 | **当前行列值** | 当前表单参数值 | — | +| 省略时 | 始终显示 | 字段始终显示 | — | + +**写法(形状与表单字段相同):** + +```json +"bind": { + "tables": ["long_tails"], + "visibleWhen": { "status": "approved" }, + "inputMapping": { "longTailId": "$row.$pk" } +} +``` + +```json +"visibleWhen": { "field": "status", "in": ["pending_review", "skipped"] } +``` + +也支持 `{ "field": "status", "equals": "approved" }`。规则缺失或无法解析时宿主视为始终可见(兼容旧技能)。 + +**要点:** + +1. 只影响 **UI 是否展示**;CLI / Agent 仍可直接调同一 entrypoint,技能侧仍应校验状态。 +2. 比较用行的**存库原始值**(如 `approved`),不是界面中文「已采用」。 +3. 顶栏 / batch 按钮不受 `bind.visibleWhen` 约束(无「当前行」);批量入口靠勾选 + 技能跳过非法状态。 +4. 示例:[`assets/examples/bind-visible-when-row.action.example.json`](../assets/examples/bind-visible-when-row.action.example.json)。 + ### 数据管理:勾选驱动模式 `bind.selectionMode` 同一业务能力应尽量用**一个** Action(多个 `placements`),避免「勾选下载」与「按数量处理队列」拆成两个同义顶栏按钮、却让用户误以为勾选生效。 @@ -746,7 +780,7 @@ HINT: ### 行级入口 `row`(宿主呈现契约) -- **技能决定有什么**:`placements` 含 `"row"`(且通过 `bind.tables` / policy 等过滤)的 Action,构成该表行的技能菜单项集合;配什么就出现什么。 +- **技能决定有什么**:`placements` 含 `"row"`(且通过 `bind.tables` / policy 等过滤)的 Action,构成该表行的技能菜单项集合;再用可选 **`bind.visibleWhen`** 按行状态过滤(见「行菜单显隐」)。 - **宿主决定怎么摆**:可用操作列、行右键、行内「⋯」等呈现**同一批** Action;点击均走同一 `launch`(`placement: "row"`),`interaction` / `executionProfile` 与壳无关。 - **顺序**:各壳内技能项顺序 = 下方「数据管理技能按钮顺序」契约(`actions` 数组相对顺序)。 - **宿主内置项**:查看 / 编辑 / 删除由宿主提供,**不是**技能 `actions.json` 配置出来的;若出现在行右键,须与技能项分组(例如分隔线后),且不得要求技能再配一份菜单 DSL。 @@ -1166,8 +1200,10 @@ Skill Action 弹窗与数据管理「新增 / 编辑」使用**同一套控件 用于「先选模式,再出对应参数」类表单。写在 `inputSchema.properties.` 上(**不是** action 顶层字段)。 -> **消歧义:** 本文的 `visibleWhen` = **Action 参数表单控件显隐**。 -> [`POLICY.md`](POLICY.md) L3 预留的 `visibleWhen` = **行态/权限策略**(未冻结),**不是**同一能力。勿混用、勿互相替代。 +> **消歧义:** +> - **表单** `inputSchema.properties.*.visibleWhen` = Action **参数控件**显隐。 +> - **`bind.visibleWhen`** = 数据管理 **行菜单 / 行按钮**显隐(见上文「行菜单显隐」)。 +> - [`POLICY.md`](POLICY.md) L3 预留的行态 `visibleWhen` = **权限策略**(未冻结),**不是**以上二者。勿混用、勿互相替代。 ```json "keyword": { diff --git a/references/POLICY.md b/references/POLICY.md index d0afea3..166d20b 100644 --- a/references/POLICY.md +++ b/references/POLICY.md @@ -2,8 +2,10 @@ > **状态:正式契约 v1.0(`schemaVersion` 1)。** > **目标:** 冻结「启用方式 + Snapshot 最小字段 + L2 scope 白名单」,使多数技能(L0)零配置,需要权限的技能(L1/L2)有稳定扩展面。 -> **非目标:** 宿主实现业务用户/角色库;规定飞书/HTTP/本地表等权限数据源;本期不强制 L3(行态 `visibleWhen` 等)。 -> **注意:** L3 预留的行态 `visibleWhen` **不是** Action 参数表单的 `inputSchema.properties.*.visibleWhen`(表单控件显隐,见 [`ACTIONS.md`](ACTIONS.md)「字段条件显隐」)。二者同名不同义,勿混用。 +> **非目标:** 宿主实现业务用户/角色库;规定飞书/HTTP/本地表等权限数据源;本期不强制 L3(菜单级藏表、`rows.mode=delegate` 等)。 +> **注意:** +> - 行菜单按业务状态显隐请用 Action 契约 **`bind.visibleWhen`**(见 [`ACTIONS.md`](ACTIONS.md)「行菜单显隐」)——宿主已支持,**不属于**本 Policy L3。 +> - L3 预留的行态 `visibleWhen` **不是** Action 参数表单的 `inputSchema.properties.*.visibleWhen`,也**不是** `bind.visibleWhen`。三者同名不同义,勿混用。 > **配套 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(当前身份允许什么)**。 @@ -17,7 +19,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` 等(≠ Action 表单 `inputSchema.visibleWhen`) | +| **L3** | (预留,本契约不冻结行为) | 如菜单级藏表、`rows.mode=delegate`、更复杂的行态策略字段等(≠ `bind.visibleWhen` 行菜单,≠ 表单 `inputSchema.visibleWhen`) | **80% 技能停留 L0。** 启用 policy 的技能必须实现约定 CLI,并在写路径自行再鉴权(见 §7)。 diff --git a/scripts/util/constants.py b/scripts/util/constants.py index 6d521fb..3abaf33 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.119" +SKILL_VERSION = "1.0.120" 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 df724fd..27e8011 100644 --- a/tests/test_actions_manifest.py +++ b/tests/test_actions_manifest.py @@ -74,7 +74,14 @@ def _validate_bind_tables(action_id: str, bind: object) -> list[str]: errors: list[str] = [] if not isinstance(bind, dict): return [f"{action_id}: bind must be object"] - allowed = {"tables", "inputMapping", "columnMapping", "labelColumn", "selectionMode"} + allowed = { + "tables", + "inputMapping", + "columnMapping", + "labelColumn", + "selectionMode", + "visibleWhen", + } unknown = set(bind.keys()) - allowed if unknown: errors.append(f"{action_id}: bind only allows {sorted(allowed)}, got {sorted(bind.keys())}") @@ -103,6 +110,9 @@ def _validate_bind_tables(action_id: str, bind: object) -> list[str]: errors.append( f"{action_id}: bind.selectionMode must be required|none|optional, got {selection_mode!r}" ) + visible_when = bind.get("visibleWhen") + if visible_when is not None and (not isinstance(visible_when, dict) or not visible_when): + errors.append(f"{action_id}: bind.visibleWhen must be a non-empty object") return errors @@ -522,6 +532,51 @@ class TestActionsManifest(unittest.TestCase): } jsonschema.validate(instance=manifest, schema=schema) + def test_schema_allows_bind_visible_when(self) -> None: + try: + import jsonschema + except ImportError: + self.fail("jsonschema is required") + schema = _load_actions_schema() + bind_props = schema["$defs"]["bind"]["properties"] + self.assertIn("visibleWhen", bind_props) + self.assertIn("visibleWhenRule", schema["$defs"]) + manifest = { + "schemaVersion": 1, + "skill": "your-skill-slug", + "actions": [ + { + "id": "probe-bind-visible-when", + "label": "采用", + "description": "验证 bind.visibleWhen 可通过 Schema", + "placements": ["row"], + "executionProfile": "sync", + "interaction": "direct", + "bind": { + "tables": ["demo_items"], + "visibleWhen": { + "field": "status", + "in": ["pending_review", "skipped"], + }, + "inputMapping": {"itemId": "$row.$pk"}, + }, + "inputSchema": { + "type": "object", + "required": ["itemId"], + "properties": { + "itemId": {"type": "number", "readOnly": True}, + }, + }, + "entrypoint": { + "type": "cli", + "command": "approve", + "args": ["--id", "{{itemId}}"], + }, + } + ], + } + jsonschema.validate(instance=manifest, schema=schema) + def test_action_examples_user_facing_copy_avoids_jargon(self) -> None: """示例 Action 的用户可见字符串不得含常见开发黑话(属性名本身除外)。""" examples_dir = os.path.join(get_skill_root(), "assets", "examples") @@ -740,6 +795,8 @@ class TestActionsManifest(unittest.TestCase): "labelColumn", "selectionMode", "勾选驱动", + "行菜单显隐", + "bind.visibleWhen", "用户可见文案", "双受众", "已选择", diff --git a/tests/test_docs_standards.py b/tests/test_docs_standards.py index 2cd7262..14b8bd3 100644 --- a/tests/test_docs_standards.py +++ b/tests/test_docs_standards.py @@ -464,6 +464,7 @@ class TestDocsStandards(unittest.TestCase): def test_visible_when_example_is_listed(self) -> None: text = self._read("assets/examples/README.md") self.assertIn("visible-when-action.example.json", text) + self.assertIn("bind-visible-when-row.action.example.json", text) self.assertIn("selection-mode-optional.action.example.json", text) self.assertIn("skill-cell-presentation.example.json", text) self.assertIn("skill-grid-presentation.example.json", text) @@ -475,15 +476,20 @@ class TestDocsStandards(unittest.TestCase): self.assertIn("A4-9", checklist) self.assertIn("selectionMode", checklist) self.assertIn("A4-10", checklist) + self.assertIn("A4-11", checklist) + self.assertIn("bind.visibleWhen", checklist) self.assertIn("用户可见文案", checklist) runtime = self._read("development/SKILL_ACTION_RUNTIME.md") self.assertIn("visibleWhen", runtime) self.assertIn("revealInForm", runtime) self.assertIn("selectionMode", runtime) + self.assertIn("bind.visibleWhen", runtime) self.assertIn("用户可见文案", runtime) actions = self._read("references/ACTIONS.md") self.assertIn("用户可见文案", actions) self.assertIn("双受众", actions) + self.assertIn("行菜单显隐", actions) + self.assertIn("bind.visibleWhen", actions) def test_guarded_docs_do_not_reference_amazon_implementation(self) -> None: forbidden = (