From 9d33c5ea2cc8888c1e8af4b03bf91e94ba82d323 Mon Sep 17 00:00:00 2001 From: chendelian <116870791@qq.com> Date: Mon, 31 Aug 2026 14:18:42 +0800 Subject: [PATCH] docs: sediment TUTORIAL_SURFACES contract for README vs tutorial and three entry surfaces --- CHANGELOG.md | 6 + DEMO.md | 10 +- README.md | 60 +++---- SKILL.md | 11 +- TUTORIAL.md | 144 +++++++++++++---- assets/README.md | 3 +- development/CHECKLIST.md | 7 +- development/DEVELOPMENT.md | 6 +- development/POLICY_MATRIX.md | 1 + development/PRODUCT_SURFACES.md | 18 ++- development/README.md | 2 + development/REQUIREMENTS.md | 15 +- development/TUTORIAL_SURFACES.md | 206 +++++++++++++++++++++++++ references/ACTIONS.md | 14 +- scripts/util/constants.py | 2 +- tests/test_development_policy_guard.py | 1 + tests/test_docs_standards.py | 11 ++ tests/test_tutorial_surfaces.py | 198 ++++++++++++++++++++++++ 18 files changed, 630 insertions(+), 85 deletions(-) create mode 100644 development/TUTORIAL_SURFACES.md create mode 100644 tests/test_tutorial_surfaces.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6f1d4..e21c71c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.100 + +- 沉淀用户市场文档契约 `development/TUTORIAL_SURFACES.md`:**说明 Tab ≠ 教程 Tab**;教程按数据管理 ∥ 对话、定时可选三入口结构 +- 重写根 `README.md` / `TUTORIAL.md` 模板骨架;CHECKLIST 增 A2-7~A2-11;`actions.json` 准备类优先 toolbar、skill-detail 不可作教程主路径 +- 新增 `tests/test_tutorial_surfaces.py`(POLICY-TUTORIAL-001);REQUIREMENTS §0.1 增教程章节列与用户文档对齐要求 + ## 1.0.99 - 沉淀数据管理**表注册门禁**契约:存在 `_jiangchang_tables` 时,宿主仅展示注册且 `visible=1` 的表;孤儿物理表不展示(匠厂宿主 ≥6.8.25) diff --git a/DEMO.md b/DEMO.md index 4c88bb6..6f0f474 100644 --- a/DEMO.md +++ b/DEMO.md @@ -15,9 +15,13 @@ demo_video_url: "" ## 建议观看顺序 -1. 如何发起一次典型任务 -2. 执行过程中会看到什么提示 / 进度 -3. 成功后的结果长什么样 +建议与 `TUTORIAL.md` 三入口结构对齐(有则拍、无则跳过): + +1. 数据管理:准备数据 → 主流程按钮 → 结果表/报告 +2. 对话任务(若有):一句自然语言触发同一能力 +3. 定时任务(若有):到点自动跑;若本技能无定时,演示中可说明「不需要定时」 + +成功后的结果长什么样(表、任务中心、HTML 等) ## 没有视频时 diff --git a/README.md b/README.md index b2b457a..9e716e2 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ --- -description: "用用户能理解的方式说明这个技能能做什么、适合什么场景,以及如何开始使用。" +description: "用用户能理解的方式说明这个技能能做什么、适合什么场景,以及边界与结果形态。" --- > **模板说明(复制后删除本段)** -> 本文是技能市场详情与普通用户说明,**不是** Agent 参考索引、CLI 契约或开发文档。 -> frontmatter 的 `description` 必须是用户能看懂的一句话能力描述;**不得**写成 CLI、schema、references、development、契约、数据结构、开发规范或 Agent 参考索引。 -> 正文只回答用户关心的问题,**不要**把 Playwright、DOM、Python、RPA、references、development、schema 等作为面向用户的主说明内容;若业务上必须出现技术词,须用用户能理解的方式解释,且不能喧宾夺主。 -> **复制后验收章节(标题可微调,建议保留):**「能帮你做什么」「适合什么场景」「使用前需要准备什么」「你可以这样告诉 Agent」「执行后你会得到什么」「注意事项」「常见问题」。 +> 本文是技能市场 **「说明」** Tab(`readme_md`),**不是**使用教程。 +> frontmatter 的 `description` 必须是用户能看懂的一句话能力描述;**不得**写成 CLI、schema、references、development、契约或「如何开始使用」类教程承诺。 +> **禁止**写入:分步操作(1/2/3)、Agent 示例话术块、「入口一/入口二」专章、逐步排障。这些一律写在根目录 `TUTORIAL.md`。 +> 正文只回答:是什么、能做什么、适合谁、准备什么、得到什么、注意事项。完整契约见 `development/TUTORIAL_SURFACES.md` §1。 +> **复制后验收章节(标题可微调):**「能帮你做什么」「适合什么场景」「使用前需要准备什么」「怎么使用」「执行后你会得到什么」「注意事项」「常见问题」。 # 【技能名称】 @@ -17,7 +18,7 @@ description: "用用户能理解的方式说明这个技能能做什么、适合 ## 这个技能能帮你做什么 -用 3 到 6 条业务能力说明技能价值,例如: +用 3 到 6 条业务能力说明技能价值(**不要**写成操作步骤),例如: - 【自动整理/查询/生成/同步/核对/提交】某类业务数据 - 帮你减少重复操作,降低人工录入或核对成本 @@ -38,42 +39,33 @@ description: "用用户能理解的方式说明这个技能能做什么、适合 ## 使用前需要准备什么 -说明用户在使用前要准备的东西,例如: +说明**前置条件**(装什么、要不要账号、要不要外站登录),**不要**写「去哪张表点哪个按钮」: -- **网页自动化类:** 首次运行可由技能自动登记平台账号(浏览器会话由系统隔离管理)。也可在「账号管理」里指定登录标识。登记账号 ≠ 已登录目标站 -- **账号在哪里看:** 已登记账号可在「数据管理 / 账号管理」中查看;每个账号使用独立浏览器会话,批量或多账号任务不会共用登录状态 -- **是否要登录目标站:** 按本技能说明(有的平台必须登录,有的公开页可不登录);与「是否有账号记录」不是一回事 +- **网页自动化类:** 可能需要在账号管理中登记平台账号;登记账号 ≠ 已登录目标站(细节见使用教程) - 相关业务权限(查询、导出、提交等)已具备 -- 需要处理的文件、时间范围、订单号、客户名称、项目名称等信息 -- 如果涉及验证码或人工确认,请保证能及时在弹出的浏览器窗口中操作 +- 需要处理的文件、时间范围、单号等信息(若适用) +- 若涉及验证码或人工确认,使用时请能及时在弹出窗口中操作 --- -## 你可以这样告诉 Agent +## 怎么使用 -用自然语言示例展示用户可以怎么发起任务,例如: +本技能支持哪些宿主入口,用**一两句话**概括即可,例如: -> “帮我查询今天的【业务对象】,整理成表格。” -> -> “帮我核对这些【数据/单据】是否有异常,并告诉我问题在哪里。” -> -> “帮我把【时间范围】内的【业务数据】导出并生成处理报告。” -> -> “帮我根据这个文件里的内容,逐条完成【业务动作】。” +- 支持 **数据管理**(点表操作)与 **对话任务**(自然语言);是否提供定时任务按本技能说明。 -> 注意:示例应是自然语言,不要写命令行命令。 +**逐步操作、示例话术与排障,请查看技能市场「使用教程」Tab。** +(完整写法见 `development/TUTORIAL_SURFACES.md`。) --- ## 执行后你会得到什么 -说明用户会看到什么结果,例如: +说明用户会看到什么**结果形态**(不要教如何点出来),例如: - 任务是否成功完成 -- 处理了多少条数据 -- 哪些内容成功、哪些失败 -- 失败原因和建议处理方式 -- 生成的文件、表格、报告或记录 +- 处理了多少条数据;哪些成功、哪些失败 +- 生成的文件、表格、报告或业务表记录 --- @@ -90,18 +82,16 @@ description: "用用户能理解的方式说明这个技能能做什么、适合 ## 常见问题 -### 为什么任务没有查到数据? +> FAQ 写**概念与边界**;需要「点哪里」的排障写到「使用教程」。 -> 可能是时间范围、关键词、账号权限或系统筛选条件不正确。请先确认输入信息是否准确。 +### 这个技能适合我吗? -### 为什么任务执行到一半需要我操作? +> 若你的场景与上文「适合什么场景」一致,可以安装后按「使用教程」完成第一次操作。 -> 可能遇到了登录、验证码、权限确认、二次审批或系统异常。请根据 Agent 的提示完成必要操作。 +### 我不会操作,从哪里开始? -### 结果文件在哪里查看? - -> 请根据 Agent 返回的结果说明查看生成文件、任务记录或处理报告。 +> 请打开技能市场 **「使用教程」** Tab,按「入口一:数据管理」或「入口二:对话任务」任选一种跟做。 ### 已经登记的账号在哪里查看? -> 请到匠厂的「数据管理 / 账号管理」查看账号记录和状态。若任务仍提示账号异常,请把账号编号、任务时间和界面提示原文提供给支持人员;不要发送密码、Cookie 或验证码。 +> 请到匠厂的「数据管理 / 账号管理」查看账号记录和状态。不要发送密码、Cookie 或验证码。 diff --git a/SKILL.md b/SKILL.md index 25bc30c..b69772a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.99 +version: 1.0.100 author: 深圳匠厂科技有限公司 metadata: openclaw: @@ -109,10 +109,11 @@ python {baseDir}/scripts/main.py init-db **回答「怎么用 / 能做什么」时的要求:** -1. 「能做什么 / 场景 / FAQ」以 `README.md` 为主;「第一次怎么用完」以 `TUTORIAL.md` 为主。 +1. 「能做什么 / 场景 / 概念类 FAQ」以 `README.md`(说明 Tab)为主;「点哪里 / 说什么 / 排障」以 `TUTORIAL.md`(教程 Tab)为主。说明 Tab **不应**含分步操作;若 README 仍有旧式步骤,以教程为准并提示开发者按 `development/TUTORIAL_SURFACES.md` 修正。 2. **禁止**在回答中粘贴 `python …/scripts/main.py …` 或其它命令行(除非用户明确索要命令,或自称技术人员/开发者)。 3. **禁止**为答用法而去 `dir` / `ls` / 列技能目录,或默认打开 `references/`、`development/`;仅当用户明确问命令、参数、集成或排错时再读 `references/`。 4. **不要**向普通用户暴露 Playwright、DOM、Python、RPA、库表名、`error_code` 等实现细节,除非用户明确询问技术实现。 +5. 用户问「怎么用」时:**优先读 `TUTORIAL.md`**;不要只根据 README 臆造按钮路径。 **文档读取边界:** @@ -125,13 +126,13 @@ python {baseDir}/scripts/main.py init-db | 文档 | 读者 | 用途 | |------|------|------| -| 根目录 `README.md` | 普通用户 | 市场 **说明** Tab(`readme_md`) | -| 根目录 `TUTORIAL.md` | 普通用户 | 市场 **教程** Tab(`tutorial_md`) | +| 根目录 `README.md` | 普通用户 | 市场 **说明** Tab(`readme_md`):能力/场景/边界;**非教程** | +| 根目录 `TUTORIAL.md` | 普通用户 | 市场 **教程** Tab(`tutorial_md`):分入口操作;结构见 `development/TUTORIAL_SURFACES.md` | | 根目录 `DEMO.md` | 普通用户 | 市场 **演示** Tab(`demo_video_url` + `demo_md`) | | 根目录 `CHANGELOG.md` | 普通用户 | 市场 **更新日志** Tab(按版本小节 → `changelog`) | | `SKILL.md`(本文) | LLM / OpenClaw 平台 | 技能入口、触发与运行契约摘要 | | `references/` | Agent 编排 / 调用 | CLI、Action、schema 契约(Agent 主读) | -| `development/` | 开发者 / AI 编程代理 | 需求、开发教程、测试、深度技术规范;**产品入口**见 `development/PRODUCT_SURFACES.md` | +| `development/` | 开发者 / AI 编程代理 | 需求、开发教程、测试、深度技术规范;**产品入口**见 `development/PRODUCT_SURFACES.md`;**用户文档契约**见 `development/TUTORIAL_SURFACES.md` | ## 目录约定 diff --git a/TUTORIAL.md b/TUTORIAL.md index ae90bbb..56e4b97 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,12 +1,34 @@ > **模板说明(复制后删除本段)** > 本文对应技能市场 **「使用教程」** Tab(发布时同步为 `tutorial_md`)。 > 面向普通用户写**可跟着做的步骤**,不要写 CLI、目录结构、release、开发规范。 -> 与根 `README.md`(技能说明)分工:README 讲「是什么 / 能做什么」;本文讲「第一次怎么用完」。 -> **复制后验收章节(标题可微调):**「开始前」「第一步…」「第二步…」「常见卡住怎么办」。 +> 与根 `README.md`(技能说明)分工:README 讲「是什么 / 能做什么 / 边界」;本文讲「从哪个入口怎么用完」。 +> **完整契约:** `development/TUTORIAL_SURFACES.md`(三入口结构、说明≠教程、skill-detail 不可作主路径)。 +> **复制后验收章节(标题可微调,语义不可缺):**「本技能做什么」「开始前」「入口一:数据管理」「入口二:对话任务」「入口三:定时任务」「主能力对照表」「常见卡住怎么办」。 +> 若本技能 §0.1 未挂数据管理 / 对话 / 定时,对应入口节改为「不适用」并写一句理由(与 REQUIREMENTS 一致)。 -# 【技能名称】使用教程 +# 【技能名称】· 使用教程 -用最短路径带用户完成一次成功使用。步骤要具体,可用「打开匠厂 → …」这类操作语言。 +本技能有两种**平行**的使用方式(按本技能入口定位填写;未挂的入口删掉或写不适用): + +- **数据管理**:在左侧表上点工具栏 / 行内按钮 +- **对话任务**:用自然语言让 Agent 完成同一能力 + +两种方式触发同一套业务能力,结果一致,选你习惯的即可。 +定时任务是否提供:见下方「入口三」(没有则写「本技能不需要定时」+ 理由)。 + +--- + +## 本技能做什么 + +用业务语言画清闭环(**不要**写库表英文名或命令行),例如: + +```text +【输入/准备】 → 【核心动作】 → 【结果可见处】 → 【可选报告/导出】 +``` + +- **输入**:…… +- **输出**:…… +- **说明**:边界(教学/正式、是否接外站等) --- @@ -14,43 +36,105 @@ 确认: -1. 已安装并登录匠厂客户端 -2. 已在技能市场安装本技能(或更新到最新版) -3. 【网页自动化类技能】首次运行一般会自动登记本平台账号;也可在「账号管理」中指定登录标识。浏览器会话由系统隔离管理——**不要**用本机日常浏览器用户目录去跑真实站 -4. 【按平台】若本技能说明「需要先登录目标站」,请在弹出的有头浏览器中按提示登录或扫码;「登记账号」不等于「已登录目标站」。若说明「可不登录也能用」,仍须有可用的账号会话目录 -5. 【网页自动化类技能】已登记账号可在「数据管理 / 账号管理」查看;每个账号都有独立浏览器会话 -6. 【如需】需要的文件 / 时间范围 / 单号等已准备好 +1. 已安装并登录匠厂客户端 +2. 已在技能市场安装本技能(建议更新到最新版) +3. 【网页自动化类】账号登记 / 目标站登录要求(按本技能;登记 ≠ 已登录) +4. 【如需】文件 / 时间范围 / 单号等已准备好 +5. 【如需】首次须先完成「载入样例 / 导入 / 同步」等准备动作(见入口一或入口二) --- -## 第一步:打开技能并说清目标 +## 入口一:数据管理 -在匠厂里新建任务或打开对话,用自然语言说明要做什么,例如: +> 若本技能无业务表、无 toolbar/row/batch:整节改为「不适用」+ 理由。 -> “帮我【完成某件业务事】,范围是【时间 / 对象 / 条件】。” +适合:对照数据、喜欢点界面、机房/运营用户。 -建议一次只交代一件事;范围说清楚,减少来回确认。 +### 左侧各表是干什么的 + +| 表名(中文) | 作用 | +|--------------|------| +| 【表 A】 | …… | +| 【表 B】 | …… | + +### 首次准备(若需要) + +1. 进入 **数据管理** → **【主表】** +2. 点击表顶 **「【准备类按钮 label】」**(若为空表可点,写明是否须选中行) +3. 成功后应看到:…… + +> **注意:** 准备/载入/导入类按钮须挂在数据管理 **toolbar**(用户看得见)。不要把「技能详情页点按钮」写成主路径——详情页常只显示四 Tab,不一定渲染 Action 按钮。 + +### 主流程 + +1. 进入 **【主表】** +2. 【是否选中行】 +3. 点击 **「【主能力 label】」** +4. 到 **【结果表 / 任务中心 / 报告】** 核对结果 + +### 行级 / 批量(若有) + +| 你想做什么 | 操作 | +|------------|------| +| …… | **【表】** 行内 → **「【label】」** | +| …… | 勾选多行 → **「【label】」** | --- -## 第二步:按提示补充信息 +## 入口二:对话任务 -如果 Agent 问你缺什么(账号、文件、筛选条件),按提示补充即可。 +> 若本技能不挂 `agent`:整节改为「不适用」+ 理由。 -- 有文件时:按界面提示选择或拖入 -- 有账号时:确认已在对应平台登录,且具备操作权限 +适合:临时提问、要解释、不想切换表。 + +### 什么时候用对话 + +- …… +- 与入口一效果相同;习惯看表时优先用数据管理 + +### 示例话术 + +| 你想做什么 | 可以这样说 | +|------------|------------| +| …… | 「……」 | +| …… | 「……」 | + +### Agent 可能追问什么 + +- 缺参数时:…… +- 失败时:可改用数据管理点按钮 --- -## 第三步:等待结果并核对 +## 入口三:定时任务 -任务结束后,请核对: +> **有 `cron`:** 写如何新建定时、选哪个 Action、默认参数、预期结果。 +> **无 `cron`:** 用下面「不需要」模板,删掉「如何配置」小节。 -- 是否成功完成 -- 处理了多少条 / 哪些失败 -- 生成的文件、表格或记录是否在预期位置 +### 本技能不需要定时时(示例文案,按需保留/改写) -若失败,先看失败原因说明,再按下一节排查。 +本技能**未提供定时任务入口**,原因:【一次性 / 须人指定对象 / 须当场确认 / ……】。 +请使用 **数据管理** 或 **对话任务**。 + +### 本技能需要定时时(示例结构) + +1. 打开侧栏 **定时任务** → 新建 +2. 选择本技能 → Action **「【label】」** +3. 填写参数 → 保存;可「立即运行」验证 +4. 到点后在【结果表 / 任务中心】查看 + +--- + +## 主能力对照表 + +同一能力,两种主入口(按本技能实际填写;只挂一端的注明): + +| 想做什么 | 数据管理 | 对话任务 | +|----------|----------|----------| +| …… | **【表】** → **「【label】」** | 「……」 | +| …… | …… | …… | + +按钮名必须与 `assets/actions.json` 的 `label` 一致。 --- @@ -58,10 +142,10 @@ | 现象 | 你可以先试 | |------|------------| -| 提示未登录 / 无权限 | 确认匠厂已登录;业务账号是否在线、权限是否足够 | -| 提示账号不存在 / 被占用 | 到「数据管理 / 账号管理」确认账号记录和状态,稍后再试;不要重复创建账号 | -| 提示缺少必要信息 | 把时间范围、单号、文件等说完整后再试一次 | -| 一直没有结果 | 到任务中心查看进度或失败原因;必要时重新发起 | -| 结果不对 | 用更明确的条件再说一次,或缩小范围重试 | +| 主表是空的 | 先做「首次准备」或对话载入/导入 | +| 按钮点不了 | 是否须先选中行;是否装错版本 | +| 一直没有结果 | 看任务中心 / 运行日志;async 任务看进度 | +| 【网页自动化】未登录 / 无权限 | 确认账号在线与目标站登录状态 | +| 结果不对 | 缩小范围重试,或改用数据管理核对表数据 | -仍无法解决时,把「你说了什么 + 界面提示原文」发给支持人员,便于排查。 +仍无法解决时,把「你用的入口 + 操作步骤 + 界面提示原文」发给支持人员。 diff --git a/assets/README.md b/assets/README.md index ed3fa6a..c10768e 100644 --- a/assets/README.md +++ b/assets/README.md @@ -1,12 +1,13 @@ # assets - `actions.json`:Skill Action manifest 最小示例(按入口定位表决定是否保留/扩展;见 `development/PRODUCT_SURFACES.md`)。每个 action **必须**显式声明 `executionProfile`(`sync` \| `async`)。默认仅 `health` / `version` / `config-path`,不放 toolbar,故无 `bind` 示例。 +- **业务技能注意:** 若主路径在数据管理,准备/载入/导入类 Action 应挂 `toolbar` + `bind.tables`(用户看得见),**不要**只挂 `skill-detail` 却在教程里写成「技能详情点按钮」。见 `development/TUTORIAL_SURFACES.md` §5、`references/ACTIONS.md`「用户可见性」。 - `html/example-report.html`:HTML 报告页最小壳(`{{TITLE}}` / `{{GENERATED_AT}}` / `{{BODY}}`);含数据管理 iframe 预览布局基线(流体铺满、禁横向滚动);渲染后须写入数据目录 `html/`,见 `references/ACTIONS.md`「HTML 页面报告」。 - `examples/`:CLI 成功输出形状示例、以及 `render-html.action.example.json`(虚构路径与数据)。 - `schemas/`:轻量 JSON Schema(`skill-actions.schema.json` 为**新技能严格规范**)。 `skill-actions.schema.json` 使用 `additionalProperties: false`。正式支持 `bind.tables`(toolbar Action 必填)与可选 `bind.inputMapping`;placements 含 `toolbar` / `row` / `batch` / `cron` / `agent` / `skill-detail`(对齐匠厂宿主)。可选 `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) +- 用户市场四 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)) - 开发规范见 [`development/`](../development/) diff --git a/development/CHECKLIST.md b/development/CHECKLIST.md index e2fd4d7..8f9559a 100644 --- a/development/CHECKLIST.md +++ b/development/CHECKLIST.md @@ -77,10 +77,15 @@ checklist_version: 1.0 - [ ] A2-1 **P0** 市场「说明」能让非开发者理解:做什么、给谁用、开始前准备什么 - [ ] A2-2 **P0** 「说明/教程」不以开发目录、内部实现细节作为用户主路径 -- [ ] A2-3 **P1** 「教程」可按真实顺序跟做(准备 → 步骤 → 失败怎么办) +- [ ] A2-3 **P1** 「教程」可按真实顺序跟做(准备 → 分入口步骤 → 失败怎么办);结构见 `TUTORIAL_SURFACES.md` - [ ] A2-4 **P1** 「演示」结构完整(有演示信息位;无视频则标明暂无,不假装有) - [ ] A2-5 **P0** 「更新日志」能对应到当前交付版本 - [ ] A2-6 **P0** 平台/Agent 侧说明中,常见用户意图的触发方式与真实能力一致(短操作 vs 长任务不混淆) +- [ ] A2-7 **P0** 「说明」Tab(`README.md`)**不含**分步操作(1/2/3)、Agent 话术块、「入口一/二」专章;操作指引指向「使用教程」 +- [ ] A2-8 **P0** 「教程」Tab 覆盖 `REQUIREMENTS` §0.1 / `actions.json` 中所有「挂」的入口(数据管理 / 对话;结构见 `TUTORIAL_SURFACES.md`) +- [ ] A2-9 **P0** 无 `cron` 时教程有「不需要定时」+ 理由;有 `cron` 时教程写清如何配置 +- [ ] A2-10 **P1** 教程主能力对照表与 `actions.json` 的 `label` 一致 +- [ ] A2-11 **P1** 若有数据管理主路径:准备/载入/导入类能力在对应表 **toolbar** 可见;教程**不以** skill-detail 为唯一主路径 --- diff --git a/development/DEVELOPMENT.md b/development/DEVELOPMENT.md index 9df5ae5..348b742 100644 --- a/development/DEVELOPMENT.md +++ b/development/DEVELOPMENT.md @@ -352,9 +352,9 @@ git remote -v | `DEMO.md` | 演示 | `demo_md` + frontmatter `demo_video_url` | | `CHANGELOG.md` | 更新日志 | 匹配当前 tag 版本的 `##` 小节 → `changelog` | -**`README.md`**:能力与场景说明。含 frontmatter `description`;写能做什么、场景、准备、如何告诉 Agent、结果、注意事项、FAQ。不要写开发教程、目录规范、release、requirements。 +**`README.md`(说明 Tab)**:能力、场景、前置条件、结果形态、注意事项、概念类 FAQ。含 frontmatter `description`(**不要**写「如何开始使用」)。**禁止**分步操作、Agent 话术块、「入口一/二」专章、逐步排障——这些属教程。完整契约见 [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md) §1。 -**`TUTORIAL.md`**:第一次怎么用完的逐步操作。写「开始前 → 步骤 → 卡住怎么办」;不要写 CLI / 目录 / 开发规范。 +**`TUTORIAL.md`(教程 Tab)**:按 [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md) 写三入口结构(数据管理 ∥ 对话;定时有则教、无则 N/A)+ 主能力对照表 + 常见卡住。步骤必须与 `REQUIREMENTS` §0.1、`actions.json` 及宿主真实 UI 对齐;**禁止**把仅 `skill-detail` 的 Action 写成用户主路径。不要写 CLI / 目录 / 开发规范。 **`DEMO.md`**:演示视频与补充说明。frontmatter 必须含 `demo_video_url`(无视频时用 `""`);正文可选。 @@ -837,7 +837,7 @@ uses: client-jiangchang/jiangchang-platform-kit/.github/workflows/reusable-relea - 长任务(async)是否有可见进度,能否取消或等待说明 - 网页 RPA 是否有头运行;需登录/验证码时是否停下等人,而不是静默失败 - 数据管理:表名/字段中文、按钮文案是否清楚;结果是否写回用户看得见的地方 -- 市场说明 / 教程是否与真实行为一致 +- 市场说明 / 教程是否与真实行为一致;**说明 Tab 不含分步操作**,教程覆盖已挂入口(见 [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md)) | 宿主入口(侧栏 / 界面) | 技能侧如何挂上 | 建议验收什么 | |-------------------------|----------------|--------------| diff --git a/development/POLICY_MATRIX.md b/development/POLICY_MATRIX.md index 1c34bfa..e934b71 100644 --- a/development/POLICY_MATRIX.md +++ b/development/POLICY_MATRIX.md @@ -41,6 +41,7 @@ | POLICY-SKILL-ACTION-002 | 每个 Action 必须显式声明 `executionProfile`,且只能是 `sync` 或 `async` | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | schema required + manifest 扫描 | `tests/test_development_policy_guard.py::TestPolicySkillAction002`;`tests/test_actions_manifest.py` | | POLICY-SKILL-ACTION-003 | toolbar Action 必须声明合法非空 `bind.tables`(snake_case、唯一) | references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | schema if/then + manifest 扫描 | `tests/test_development_policy_guard.py::TestPolicySkillAction003`;`tests/test_actions_manifest.py` | | POLICY-SKILL-ACTION-004 | `placements` 与 `executionProfile` 正交;文档不得把数据管理入口与异步执行方式硬绑定;Schema 须接受 toolbar/row/batch/cron/agent/skill-detail × sync/async 全部组合 | development/SKILL_ACTION_RUNTIME.md;references/ACTIONS.md;assets/schemas/skill-actions.schema.json | hard | Schema 6×2 矩阵校验 + 文档禁止短语扫描 | `tests/test_actions_manifest.py::test_schema_allows_all_placement_execution_profile_combinations`;`tests/test_development_policy_guard.py::TestPolicySkillAction004` | +| POLICY-TUTORIAL-001 | 存在 `development/TUTORIAL_SURFACES.md`;`TUTORIAL.md` 须含数据管理/对话/定时/对照表骨架;`README.md` 禁止「告诉 Agent」专章与入口专章;业务技能教程与 `actions.json` placements 对齐(无 cron 须声明不需要定时) | development/TUTORIAL_SURFACES.md;development/CHECKLIST.md A2-7~A2-11 | hard | 文档存在性 + 章节/反模式扫描 + placements 对齐 | `tests/test_tutorial_surfaces.py`;`tests/test_docs_standards.py::test_tutorial_surfaces_contract_doc_and_checklist` | | POLICY-ARCH-CORE-001 | 同一业务能力的 Action、CLI、Agent、cron、数据管理入口复用同一业务内核(文档要求已声明) | development/DEVELOPMENT.md;development/SKILL_ACTION_RUNTIME.md | hard(文档标记) / soft(语义) | 文档关键表述存在性;真实复用靠 sample/review | `tests/test_development_policy_guard.py::TestPolicyArchCore001`;`tests/samples/test_action_core_contract.py.sample` | | POLICY-DATA-SYNC-001 | 同步型业务须遵循幂等、事务、完整性与空结果保护(文档已声明) | references/SCHEMA.md;development/SKILL_ACTION_RUNTIME.md | hard(文档标记) / soft(语义) | 文档关键表述存在性;完整语义靠 sample/review | `tests/test_development_policy_guard.py::TestPolicyDataSync001`;`tests/samples/test_sync_contract.py.sample` | diff --git a/development/PRODUCT_SURFACES.md b/development/PRODUCT_SURFACES.md index a3c5383..1319808 100644 --- a/development/PRODUCT_SURFACES.md +++ b/development/PRODUCT_SURFACES.md @@ -164,9 +164,25 @@ --- -## 7. 相关文档 +## 7. 用户教程如何投影本文 + +入口定位表填完后,**必须**落到用户市场文档(见 [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md)): + +| 定位结果 | `TUTORIAL.md` | `README.md` | +|----------|---------------|-------------| +| 挂数据管理 | 「入口一」专章 + 表/按钮步骤 | 仅一句「支持数据管理」 | +| 挂对话 | 「入口二」专章 + 话术 | 仅一句「支持对话」;**禁止**话术块 | +| 挂定时 | 「入口三」教怎么建 | 可选一句边界 | +| 不挂定时 | 「入口三」写不需要 + 理由 | 可选一句「不提供定时」 | + +准备/载入类 Action:优先 `toolbar` + `bind.tables`,避免教程写「技能详情点按钮」而宿主详情页不渲染。 + +--- + +## 8. 相关文档 - 需求里填定位表:[`REQUIREMENTS.md`](REQUIREMENTS.md) +- **用户说明 / 教程怎么写:** [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md) - 交活怎么验:[`CHECKLIST.md`](CHECKLIST.md) - 开发步骤中的 Action 挂载:[`DEVELOPMENT.md`](DEVELOPMENT.md) §11.5、§15 - Agent 侧字段契约:[`../references/ACTIONS.md`](../references/ACTIONS.md) diff --git a/development/README.md b/development/README.md index fd125ad..d869617 100644 --- a/development/README.md +++ b/development/README.md @@ -10,6 +10,7 @@ |------|--------|------| | 1 | **拿到仓库**:① 项目经理在 [自建 Gitea](https://git.jc2009.com/)(`git.jc2009.com`,非 Gitea 官网)开仓并授权后 `git clone`;或 ② 本地已有 `skill-template` 时用 scaffold / 复制出新目录(遵守 Git 红线) | [`DEVELOPMENT.md`](DEVELOPMENT.md) §1、§4 | | 2 | **先读宿主产品入口**:对话 / 数据管理 / 定时任务平级;何时走哪个;为本技能填入口定位表 | [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md) | +| 2b | **用户市场文档**:说明 Tab ≠ 教程 Tab;教程按三入口写;与 §0.1 / actions 对齐 | [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md) | | 3 | **先读 AI + RPA 总纲**;界面技能先完成元素拾取/element map,再按浏览器、桌面或手机读分端手册 | [`AI_RPA.md`](AI_RPA.md)、[`RPA_ELEMENT_LOCATING.md`](RPA_ELEMENT_LOCATING.md) | | 4 | 按 [`NAMING.md`](NAMING.md) 确认 slug;**先填** [`REQUIREMENTS.md`](REQUIREMENTS.md)(含入口定位表),再写业务代码 | [`REQUIREMENTS.md`](REQUIREMENTS.md)、[`NAMING.md`](NAMING.md) | | 5 | 替换标识与占位,按四象限 / examples 实现 `scripts/service/` | [`DEVELOPMENT.md`](DEVELOPMENT.md) §4~§14 | @@ -27,6 +28,7 @@ 开始定制 skill 前,建议按以下顺序阅读: 1. [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md) — **宿主产品入口**:对话 / 数据管理 / 定时任务平级;何时走哪个;入口定位表 +1b. [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md) — **用户说明 / 教程契约**:README≠TUTORIAL;三入口结构;skill-detail 不可作主路径 2. [`AI_RPA.md`](AI_RPA.md) — **AI Agent + RPA 总纲**:职责边界、合规红线、风控、操作节奏与完整闭环 3. [`ACCOUNT_MANAGER.md`](ACCOUNT_MANAGER.md) — **浏览器账号标准**:一账号一 Profile、租约、多账号、数据查看、验收与排障 4. [`SHARED_REPOS.md`](SHARED_REPOS.md) — **公共仓库目录**(模板 / 账号管理 / platform-kit 等,可扩展) diff --git a/development/REQUIREMENTS.md b/development/REQUIREMENTS.md index e617471..9ee3cf9 100644 --- a/development/REQUIREMENTS.md +++ b/development/REQUIREMENTS.md @@ -33,19 +33,26 @@ ## 0.1 宿主入口定位(必填) -先读 [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md)。对话任务 / 数据管理 / 定时任务在宿主上**平级**;挂哪些由本技能业务与 UX 定,模板不规定「必须三入口都挂满」。 +先读 [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md)。对话任务 / 数据管理 / 定时任务在宿主上**平级**;挂哪些由本技能业务与 UX 定,模板不规定「必须三入口都挂满」。用户文档如何写见 [`TUTORIAL_SURFACES.md`](TUTORIAL_SURFACES.md)。 对每个主能力填一行(可增行;「不挂」必须写理由): -| 主能力(Action id / CLI) | 对话 | 数据管理 | 定时 | 不挂理由(若不挂) | -|---------------------------|------|----------|------|--------------------| -| (例)`` | 挂 / 不挂 | 不挂 / toolbar / row / batch / replaces / 仅展示表 | 挂 / 不挂 | | +| 主能力(Action id / CLI) | 对话 | 数据管理 | 定时 | 教程章节 | 不挂理由(若不挂) | +|---------------------------|------|----------|------|----------|--------------------| +| (例)`` | 挂 / 不挂 | 不挂 / toolbar / row / batch / replaces / 仅展示表 | 挂 / 不挂 | 入口一 / 入口二 / 入口三 / N/A | | 说明: - 数据管理列可写组合(如 `toolbar + row`) - 「仅展示表」= 用户要看过程/结果,但不在表上触发该能力 - 定位为「挂」的入口须在 `assets/actions.json`(及必要表元数据)中落实,并与对话路径共用同一 domain service +- **教程章节**:该能力在 `TUTORIAL.md` 中落在哪一节;整技能无定时时,入口三仍须写「不需要 + 理由」 + +### 0.1.1 用户市场文档(与入口表一致) + +- `README.md`(说明 Tab):只写能力 / 场景 / 边界;**禁止**分步操作与 Agent 话术(见 `TUTORIAL_SURFACES.md` §1) +- `TUTORIAL.md`(教程 Tab):覆盖 §0.1 中所有「挂」的入口;准备/载入类优先数据管理 toolbar,**不要**把 skill-detail 当主路径 +- 定位为「不挂」定时:教程须有 N/A 声明,与本表理由一致 ## 0.2 AI + RPA 协作与授权边界(涉及外部系统时必填) diff --git a/development/TUTORIAL_SURFACES.md b/development/TUTORIAL_SURFACES.md new file mode 100644 index 0000000..a60fc33 --- /dev/null +++ b/development/TUTORIAL_SURFACES.md @@ -0,0 +1,206 @@ +# 用户市场文档契约(说明 Tab / 教程 Tab) + +本文给**技能开发者**与 **AI 编程助手**读:规定根目录 `README.md`(技能说明)与 `TUTORIAL.md`(使用教程)**写什么、不写什么**,以及如何与宿主三入口、入口定位表、`actions.json` 对齐。 + +**单一真相链:** + +```text +REQUIREMENTS.md §0.1 入口定位表 + ↓ +assets/actions.json(placements / label / bind) + ↓ +TUTORIAL.md(用户可读、分入口操作) +README.md(能力与边界;不写步骤) +``` + +产品入口「挂什么」见 [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md)。本文只管**用户市场文档怎么投影**。 + +--- + +## 0. 与四 Tab 的关系 + +| 文件 | 市场 Tab | 本文角色 | +|------|----------|----------| +| `README.md` | **说明** | §1:是什么 / 给谁 / 边界 | +| `TUTORIAL.md` | **教程** | §2~§6:怎么用(分入口) | +| `DEMO.md` | 演示 | 观看顺序可对齐三入口,不另造标准 | +| `CHANGELOG.md` | 更新日志 | 与文档契约无关 | + +**铁律:** 说明就是说明,教程就是教程。**禁止**在 README 里写分步操作、Agent 话术块、排障「点哪里」;这些一律进 `TUTORIAL.md`。 + +--- + +## 1. README.md(说明 Tab) + +### 1.1 必须回答 + +| 问题 | 写法 | +|------|------| +| 这是什么 | 标题 + 一两句价值 | +| 能做什么 | 3~6 条能力 bullet(**无步骤编号**) | +| 适合谁 / 场景 | 业务语言 | +| 使用前准备 | 安装、账号、权限等**前置条件**;不写「去哪张表点哪」 | +| 执行后得到什么 | 结果形态(表、报告、文件),**不教怎么生成** | +| 注意事项 | 边界、风险、人工复核 | +| 怎么使用(可选,短) | **一两句**:支持哪些入口 + **「步骤见使用教程 Tab」** | + +### 1.2 禁止写入 README + +- `1.` `2.` `3.` 操作步骤(如「选品任务 → 点某某按钮」) +- 「你可以这样告诉 Agent」整节或大块 `>` 示例话术 +- 「入口一 / 入口二」专章结构 +- 逐步排障(「先点 A 再点 B」) +- frontmatter `description` 不要写「以及如何开始使用」类教程承诺 + +### 1.3 FAQ 边界 + +- README FAQ:**概念类**(和真实系统区别、从哪开始学、是否支持定时)→ 可指向「使用教程」Tab +- **操作类**排障 → 只写在 `TUTORIAL.md`「常见卡住怎么办」 + +### 1.4 建议章节(标题可微调) + +```text +# 【技能中文名】 +## 这个技能能帮你做什么 +## 适合什么场景 +## 使用前需要准备什么 +## 怎么使用 ← 仅短摘要 + 指向教程 +## 执行后你会得到什么 +## 注意事项 +## 常见问题 ← 概念类 +``` + +--- + +## 2. TUTORIAL.md(教程 Tab)必选结构 + +复制业务技能后,**验收章节(标题可微调,语义不可缺)**: + +```text +# 【技能中文名】· 使用教程 +(开篇:数据管理与对话平行;定时有/无一句) + +## 本技能做什么 ← 业务数据流(非 CLI/SQL) +## 开始前 +## 入口一:数据管理 ← §0.1 有 toolbar/row/batch/replaces 则必填 +## 入口二:对话任务 ← §0.1 有 agent 则必填 +## 入口三:定时任务 ← 有 cron:怎么用;无 cron:不需要 + 理由 +## 主能力对照表 ← 双挂能力必填 +## 常见卡住怎么办 +``` + +### 2.1 与入口定位表的关系 + +| §0.1 / actions | TUTORIAL 要求 | +|----------------|---------------| +| 任一能力挂数据管理(toolbar/row/batch/replaces) | **必须**有「入口一:数据管理」专章 | +| 任一能力挂 `agent` | **必须**有「入口二:对话任务」专章(**禁止**用「或者你也可以说…」替代专章) | +| 任一能力挂 `cron` | **必须**有「入口三」且写清如何建定时、选哪个 Action | +| **无** `cron` | **必须**写「本技能不需要定时」+ 一句理由(与 §0.1 一致) | +| 某能力只挂一端 | 对照表注明「仅数据管理」或「仅对话」 | + +### 2.2 平行关系(数据管理 ∥ 对话) + +- 两者是**平行主入口**,不是「对话主、点表辅」或反过来 +- 禁止全文只有「第一步:打开对话说清目标」且无数据管理专章 +- 允许用户只用其中一种;教程须写清「两种效果一致,选你习惯的」 + +--- + +## 3. 入口一:数据管理(怎么写) + +适合写: + +1. 左侧有哪些业务表、各干什么(业务语言) +2. 首次准备(载入/导入/新建)——**按钮必须出现在用户能看到的地方**(见 §5) +3. 主流程:进哪张表 → 是否要选中行 → 点哪个 `label` → 去哪看结果 +4. 行级 / 批量操作(若有 `row` / `batch`) + +按钮中文名 = `assets/actions.json` 的 `label`,不要另造别名。 + +--- + +## 4. 入口二:对话任务(怎么写) + +适合写: + +1. 什么时候用对话(与数据管理互补) +2. 示例话术表(与主能力一一对应) +3. Agent 可能追问什么、用户怎么答 + +**不要**把对话写成唯一路径;也不要把话术塞进 README。 + +--- + +## 5. 准确性铁律(用户看得见才写) + +教程步骤必须对应 **宿主真实 UI**,不是只对应 manifest 声明。 + +| 规则 | 说明 | +|------|------| +| **placements 对齐** | 教程写的入口 ∈ `actions.json` 已挂 placement | +| **skill-detail 不可作主路径** | 声明了 `skill-detail` **不等于**详情页一定渲染出按钮;当前宿主常只显示四 Tab。**禁止**把「技能详情 → 点某某」写成用户主路径 | +| **准备/载入/导入类** | 优先挂 **数据管理 `toolbar`** + `bind.tables`(空表也可点的能力尤其如此);可保留 `agent`;`skill-detail` 仅作可选兼容 | +| **写入后刷新** | 建议 `steps: [{ "when": "afterSuccess", "do": "refreshCatalog" }]`,避免用户点完表不刷新 | +| **对照表** | 「想做什么 \| 数据管理 \| 对话」覆盖双挂主能力;`label` 与界面一致 | + +字段细节见 [`../references/ACTIONS.md`](../references/ACTIONS.md)「用户可见性与教程准确性」。 + +--- + +## 6. 入口三:定时任务(可选) + +### 6.1 何时挂 `cron`(教程教「怎么用定时」) + +- 参数相对固定、按节奏重复(同步、巡检、消费队列) +- 无人值守仍安全、可幂等或可失败重试 +- 与数据管理是「白天点选 + 夜里自动啃」组合 + +教程写:侧栏定时任务 → 新建 → 选本技能 → 选 Action → 填参 → 保存/立即运行。 + +### 6.2 何时不挂(教程写「不需要」) + +- 一次性 / 课堂演示,对象当场指定 +- 强不可逆、须当场确认 +- 每次对象不同、须人指定链接或范围 +- 主路径依赖验证码 / 人工介入 + +决策三问(与 [`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md) §2.3 一致): + +1. 明天人不盯着、同样参数再跑一遍,业务上合理吗? +2. 失败后自动重跑可接受吗? +3. 有没有须人当场确认或动态指定的输入? + +①是 且 ②可接受 且 ③否 → 可挂 cron;否则不挂,§0.1 写理由,教程声明 N/A。 + +--- + +## 7. 复制后自检(发布前) + +- [ ] README 无分步操作、无 Agent 话术块 +- [ ] TUTORIAL 有三入口结构(定时有或 N/A) +- [ ] 对照表与 `actions.json` `label` 一致 +- [ ] 准备类按钮在数据管理表顶可见(若技能有数据管理主路径) +- [ ] 教程未把 skill-detail 当唯一载入/主流程入口 +- [ ] §0.1「挂」的入口在 TUTORIAL 有专章;「不挂」有理由且教程一致 + +交活清单见 [`CHECKLIST.md`](CHECKLIST.md) A2-7~A2-11。 + +--- + +## 8. 参考样例 + +已验收参考(业务技能,非模板正文):`client-gdcm/review-sku-selection-default` +(说明 Tab 瘦身 + 教程三入口 + 「载入课堂样例」挂 `selection_cases` toolbar。) + +复制模板后**不要**照搬选品表名与样例编码;只复用**章节结构与职责划分**。 + +--- + +## 9. 相关文档 + +- 宿主入口何时挂:[`PRODUCT_SURFACES.md`](PRODUCT_SURFACES.md) +- 入口定位表:[`REQUIREMENTS.md`](REQUIREMENTS.md) §0.1 +- Action placements / 可见性:[`../references/ACTIONS.md`](../references/ACTIONS.md) +- 市场四 Tab 同步:[`DEVELOPMENT.md`](DEVELOPMENT.md) §6 附近「用户市场四 Tab」 +- 交活验收:[`CHECKLIST.md`](CHECKLIST.md) §2 diff --git a/references/ACTIONS.md b/references/ACTIONS.md index 16763ea..8069b09 100644 --- a/references/ACTIONS.md +++ b/references/ACTIONS.md @@ -644,6 +644,18 @@ HINT: | `agent` | Agent 可直接调用(`run_skill_action`) | | `cron` | 定时任务「技能直调」 | +### 用户可见性与教程准确性 + +`placements` 决定 Action **可以**出现在哪;教程只能写用户在宿主里**实际能点到**的路径。完整用户文档契约见 [`../development/TUTORIAL_SURFACES.md`](../development/TUTORIAL_SURFACES.md)。 + +| 规则 | 说明 | +|------|------| +| **skill-detail ≠ 一定可见** | 声明 `skill-detail` 不保证详情页渲染出按钮;当前宿主详情常只有说明/教程/演示/更新日志四 Tab。**禁止**把「技能详情 → 点某某」写成用户主路径或教程唯一入口 | +| **准备 / 载入 / 导入 / 重置** | 若技能以数据管理为主路径,此类能力优先 `toolbar` + 非空 `bind.tables`(空表也可点的准备动作尤其如此);可同时挂 `agent`;`skill-detail` 仅作可选兼容 | +| **写入后刷新** | 会写入业务表的 Action 建议 `steps: [{ "when": "afterSuccess", "do": "refreshCatalog" }]`,避免用户点完表不更新 | +| **教程按钮名** | 必须等于本文件 `label`;对照表与数据管理顶栏/行内文案一致 | +| **README vs 教程** | 说明 Tab 不写分步;操作步骤只写 `TUTORIAL.md` | + ### 行级入口 `row`(宿主呈现契约) - **技能决定有什么**:`placements` 含 `"row"`(且通过 `bind.tables` / policy 等过滤)的 Action,构成该表行的技能菜单项集合;配什么就出现什么。 @@ -689,7 +701,7 @@ HINT: | 数据同步(外源 → 本地库) | sync 或 async | toolbar + cron + agent + skill-detail(按需) | 可选 | | 单行重试 / 行内处理 | sync 或 async | row(可加 agent) | 可选 | | 批量处理勾选行 | sync 或 async | batch(可加 toolbar/cron) | 可选 | -| 导入 / 重置 | sync 或 async | skill-detail, agent | 可选 | +| 导入 / 重置 | sync 或 async | **优先** toolbar(须 `bind.tables`)+ agent;skill-detail 仅可选 | 可选 | | 单条副作用 RPA | async(建议) | agent, skill-detail | **建议** | | 批量顺序 pick RPA | async(建议) | toolbar, cron, agent | **建议** | ## 同步数据 vs 导出当前表 vs 特殊业务报告 vs HTML 页面 diff --git a/scripts/util/constants.py b/scripts/util/constants.py index 2fcfc25..644a760 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.98" +SKILL_VERSION = "1.0.100" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" PLATFORM_KIT_MIN_VERSION = "1.2.11" diff --git a/tests/test_development_policy_guard.py b/tests/test_development_policy_guard.py index 0670948..e863fd3 100644 --- a/tests/test_development_policy_guard.py +++ b/tests/test_development_policy_guard.py @@ -57,6 +57,7 @@ POLICY_IDS = ( "POLICY-SKILL-ACTION-002", "POLICY-SKILL-ACTION-003", "POLICY-SKILL-ACTION-004", + "POLICY-TUTORIAL-001", "POLICY-ARCH-CORE-001", "POLICY-DATA-SYNC-001", ) diff --git a/tests/test_docs_standards.py b/tests/test_docs_standards.py index 88d9202..20b1b33 100644 --- a/tests/test_docs_standards.py +++ b/tests/test_docs_standards.py @@ -181,6 +181,17 @@ class TestDocsStandards(unittest.TestCase): self.assertIn("CHANGELOG.md", text) self.assertIn("demo_video_url", text) self.assertIn("tutorial_md", text) + self.assertIn("TUTORIAL_SURFACES.md", text) + + def test_tutorial_surfaces_contract_doc_and_checklist(self) -> None: + surfaces = self._read("development/TUTORIAL_SURFACES.md") + self.assertIn("说明", surfaces) + self.assertIn("教程", surfaces) + self.assertIn("skill-detail", surfaces) + checklist = self._read("development/CHECKLIST.md") + self.assertIn("A2-7", checklist) + self.assertIn("A2-8", checklist) + self.assertIn("TUTORIAL_SURFACES.md", checklist) def test_naming_md_exists_and_covers_standards(self) -> None: text = self._read("development/NAMING.md") diff --git a/tests/test_tutorial_surfaces.py b/tests/test_tutorial_surfaces.py new file mode 100644 index 0000000..3ace442 --- /dev/null +++ b/tests/test_tutorial_surfaces.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +"""用户市场文档契约:README≠教程、TUTORIAL 三入口结构、与 actions.json 对齐。 + +契约全文:development/TUTORIAL_SURFACES.md +- 模板占位 slug(your-skill-slug):只检查骨架与契约文档存在 +- 业务技能:按 actions.json placements 硬校验教程章节与 README 反模式 +""" +from __future__ import annotations + +import json +import os +import re +import unittest + +from _support import get_skill_root +from util.constants import SKILL_SLUG + +TEMPLATE_PLACEHOLDER_SLUG = "your-skill-slug" + +_SECTION_DATA_MGMT = re.compile(r"(?m)^##\s*.*数据管理") +_SECTION_DIALOG = re.compile(r"(?m)^##\s*.*(对话任务|对话)") +_SECTION_CRON = re.compile(r"(?m)^##\s*.*定时任务") +_SECTION_COMPARE = re.compile(r"(?m)^##\s*.*(对照表|主能力)") +_SECTION_STUCK = re.compile(r"(?m)^##\s*.*(常见卡住|卡住怎么办)") +_SECTION_WHAT = re.compile(r"(?m)^##\s*.*(本技能做什么|做什么)") +_NO_CRON_HINT = re.compile(r"不需要定时|未提供定时|不提供定时|不适用.*定时|定时.*不适用") + +# README 反模式:分步操作 / Agent 话术专章(说明 Tab 禁止) +_README_HOWTO_AGENT = re.compile(r"(?m)^##\s*.*告诉\s*Agent") +_README_ENTRY_CHAPTER = re.compile(r"(?m)^##\s*入口[一二三]") +_README_NUMBERED_DM_PATH = re.compile( + r"(?m)^\d+[\.、]\s*.*(数据管理|选品任务|表顶).{0,40}(点击|点\s*「)" +) + + +def _read(rel: str) -> str: + path = os.path.join(get_skill_root(), rel) + with open(path, encoding="utf-8") as f: + return f.read() + + +def _load_actions() -> list[dict]: + path = os.path.join(get_skill_root(), "assets", "actions.json") + if not os.path.isfile(path): + return [] + with open(path, encoding="utf-8") as f: + data = json.load(f) + return list(data.get("actions") or []) + + +def _placement_sets(actions: list[dict]) -> tuple[bool, bool, bool]: + has_dm = False + has_agent = False + has_cron = False + dm_tokens = frozenset({"toolbar", "row", "batch"}) + for action in actions: + placements = set(action.get("placements") or []) + if placements & dm_tokens: + has_dm = True + if "agent" in placements and str(action.get("id") or "") not in ( + "health", + "version", + "config-path", + ): + has_agent = True + if "cron" in placements: + has_cron = True + return has_dm, has_agent, has_cron + + +class TestTutorialSurfacesContract(unittest.TestCase): + def test_tutorial_surfaces_doc_exists(self) -> None: + path = os.path.join(get_skill_root(), "development", "TUTORIAL_SURFACES.md") + self.assertTrue( + os.path.isfile(path), + msg="development/TUTORIAL_SURFACES.md must exist (user market doc contract)", + ) + text = _read("development/TUTORIAL_SURFACES.md") + for marker in ( + "README.md", + "TUTORIAL.md", + "数据管理", + "对话", + "定时", + "skill-detail", + "对照表", + ): + self.assertIn(marker, text, msg=f"TUTORIAL_SURFACES.md missing {marker!r}") + + def test_development_md_points_to_tutorial_surfaces(self) -> None: + text = _read("development/DEVELOPMENT.md") + self.assertIn( + "TUTORIAL_SURFACES.md", + text, + msg="DEVELOPMENT.md must reference TUTORIAL_SURFACES.md for market docs", + ) + + def test_template_or_business_tutorial_has_required_skeleton(self) -> None: + tutorial = _read("TUTORIAL.md") + self.assertRegex( + tutorial, + _SECTION_WHAT, + msg="TUTORIAL.md must have a '本技能做什么' (or equivalent) section", + ) + self.assertRegex( + tutorial, + _SECTION_DATA_MGMT, + msg="TUTORIAL.md must have a 数据管理 section (or 不适用 placeholder)", + ) + self.assertRegex( + tutorial, + _SECTION_DIALOG, + msg="TUTORIAL.md must have a 对话 section (or 不适用 placeholder)", + ) + self.assertRegex( + tutorial, + _SECTION_CRON, + msg="TUTORIAL.md must have a 定时任务 section (teach or N/A)", + ) + self.assertRegex( + tutorial, + _SECTION_COMPARE, + msg="TUTORIAL.md must have a 主能力对照表 section", + ) + self.assertRegex( + tutorial, + _SECTION_STUCK, + msg="TUTORIAL.md must have a 常见卡住 section", + ) + # 禁止旧「对话单线」作为唯一主结构标题 + self.assertNotRegex( + tutorial, + r"(?m)^##\s*第一步:打开技能并说清目标\s*$", + msg="TUTORIAL.md must not use legacy dialogue-only first step as sole structure", + ) + + def test_readme_has_no_legacy_agent_howto_chapter(self) -> None: + readme = _read("README.md") + self.assertIsNone( + _README_HOWTO_AGENT.search(readme), + msg="README.md must not have '告诉 Agent' howto chapter (belongs in TUTORIAL)", + ) + self.assertIsNone( + _README_ENTRY_CHAPTER.search(readme), + msg="README.md must not have 入口一/二/三 chapters (belongs in TUTORIAL)", + ) + + def test_business_skill_tutorial_aligns_with_actions(self) -> None: + if SKILL_SLUG == TEMPLATE_PLACEHOLDER_SLUG: + self.skipTest("template placeholder slug: skip actions alignment hard checks") + + actions = _load_actions() + has_dm, has_agent, has_cron = _placement_sets(actions) + tutorial = _read("TUTORIAL.md") + readme = _read("README.md") + + if has_dm: + self.assertRegex( + tutorial, + _SECTION_DATA_MGMT, + msg="business skill with data-management placements must document 数据管理 in TUTORIAL", + ) + # 准备类若挂了 toolbar,教程不应把 skill-detail 当唯一主路径 + self.assertNotRegex( + tutorial, + r"(?m)^\d+[\.、]\s*.*技能详情.{0,30}点击", + msg="TUTORIAL must not use 技能详情点击 as primary numbered path when DM exists", + ) + + if has_agent: + self.assertRegex( + tutorial, + _SECTION_DIALOG, + msg="business skill with agent placements must document 对话 in TUTORIAL", + ) + + if has_cron: + self.assertRegex( + tutorial, + _SECTION_CRON, + msg="with cron placements, TUTORIAL must have 定时任务 section", + ) + else: + self.assertRegex( + tutorial, + _NO_CRON_HINT, + msg="without cron placements, TUTORIAL must declare 不需要/未提供定时", + ) + + # README 不应出现「数据管理 → … → 点击」式编号步骤 + self.assertIsNone( + _README_NUMBERED_DM_PATH.search(readme), + msg="README.md must not contain numbered data-management click steps", + ) + + +if __name__ == "__main__": + unittest.main()