From 452fc8ea7eac270549afef4ad8526ce936a723dd Mon Sep 17 00:00:00 2001 From: host Date: Sat, 31 Oct 2026 01:23:38 +0000 Subject: [PATCH] docs(skill): add software-engineering README and same-dir reference test SKILL.md header pointed at same-dir README.md but the pack had no such file (CHANGELOG was supplied by #217, README was not). This track adds the README only: purpose, chapter map, appendix provenance entry point, seeding behaviour, and known boundaries. It states no rule, changes no SKILL.md semantics, and does not duplicate the CHANGELOG/script explanations -- overlapping parts are pointers, with an explicit division-of-labour section. Deletion retention is written only as the tombstone-design placeholder, deferring to the skill_seed implementation, because no tombstone implementation has landed. Test pins that every file SKILL.md cites as same-directory exists (CHANGELOG, README). --- cloud/skills/software-engineering/README.md | 134 ++++++++++++++++++++ cloud/tests/test_skill_readme_refs.py | 88 +++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 cloud/skills/software-engineering/README.md create mode 100644 cloud/tests/test_skill_readme_refs.py diff --git a/cloud/skills/software-engineering/README.md b/cloud/skills/software-engineering/README.md new file mode 100644 index 0000000..cbcd7cd --- /dev/null +++ b/cloud/skills/software-engineering/README.md @@ -0,0 +1,134 @@ +# software-engineering 技能包说明 + +本文件是源码包的导航与装机说明,不是技能规则本身。 +规则只在 `SKILL.md` 里,改规则只改 `SKILL.md`;本文件不复述 +门禁、角色细则与校验规则,复述就会漂移。 + +版本与状态以 `SKILL.md` 文首为准:v0.2,2026-10-30,草稿; +其文首自述未提 PR、未部署生产、未改任何生产代码。 +本文件只补说明,不改规则、不合并、不部署。 + +## 用途 + +面向“从零做一款软件、尤其是要同时做 web、macOS、Windows、 +iOS、Android 多个客户端”的任务:按产品规划、产品设计、架构契约、 +多客户端并行开发、联调测试、验收的顺序,用协调员任务派生角色线程 +推进,契约冻结后各客户端只在自己目录内开发,最后走评审合并与用户验收。 + +用与不用的判定只在 `SKILL.md` 的“何时用 / 何时不用”两节, +本文件不另立标准。只做单文件小改动、只问概念时不用本技能, +这是 `SKILL.md` 的原话指向,不是本文件的补充规则。 + +## 包内文件一览 + +源码位置是 `cloud/skills/software-engineering/`,包内文件如下: + +- `SKILL.md`:唯一规则权威,含正文与文末附录随附版。 +- `README.md`:本文件,导航与装机说明,不进入租户。 +- `CHANGELOG.md`:变更时间线,见下文“与 CHANGELOG 的分工”。 +- `templates/roles/`:附录的 9 个编辑源,见下文“附录溯源”。 +- `tools/check_appendix.py`:附录校验脚本,只校验,不改 `SKILL.md`。 + +## SKILL.md 章节结构(只列名,不复述规则) + +正文节: + +- 何时用 / 何时不用 +- 核心原则(协调员和所有角色线程都必须遵守) +- 阶段 DAG(不是所有线程同时开,是按门禁、分阶段开) +- 门禁表 +- 接单选型:协调员第一件事是问,不是写代码 +- 角色线程矩阵 +- 协调员派生子任务的必填字段 +- 待办机制(不依赖提示词注入) +- 契约冻结与变更协议 +- 目录与分支规则 +- GitHub 保证(每线程一分支、一 PR、一合并门),下含分支、 + PR 交付、合并门、合并方式与合并记录、发布与回滚、 + 安全六个小节 +- 测试与验收 +- 失败、续跑与成本 +- 诚实边界:原生客户端 +- 校验(协调员每过一个门禁自查一次) + +附录节: + +- 附录:角色模板(播种随附版),下含 9 章附录角色, + 每章标题注明其来源文件,见下文“附录溯源”。 + +## 附录溯源 + +附录 9 章的编辑源在 `templates/roles/` 下,文件与章一一对应: + +`00-coordinator.md`、`01-product-manager.md`、`02-architect.md`、 +`03-designer.md`、`04-client-developer.md`、`05-backend.md`、 +`06-qa.md`、`07-acceptance.md`、`08-shared-core.md`。 + +校验入口(仓库根目录执行): + +```sh +python3 cloud/skills/software-engineering/tools/check_appendix.py +``` + +该脚本默认校验模式逐章比对源与附录;`--emit` 模式只把拼接后 +的附录规范形式打印到标准输出,供编辑者起草参考,绝不是可直接 +覆盖 `SKILL.md` 的字节补丁。校验的规范形式、允许差异与失败条件 +以脚本注释为准,本文件不另抄一份;补齐的时间线见 `CHANGELOG.md`。 +任一侧改动都应先被校验拦住,而不是悄悄漂移:改附录先改源, +再按技能轨重发附录,脚本本身不替编辑者改 `SKILL.md`。 + +## 播种行为 + +装机路径(`SKILL.md` 文首原话指向): + +- 源码位置:`cloud/skills/software-engineering/SKILL.md` +- 播种后租户内位置:`/skills/software-engineering/SKILL.md` + +播种实现是 `cloud/skill_seed.py`,本段只是其摘要,冲突以实现 +与测试为准: + +- 只复制各技能的 `SKILL.md` 一个文件;`README.md`、`CHANGELOG.md`、 + `templates/`、`tools/` 均不进入租户目录。 +- 目标缺失则复制;目标与源码相同则不动;目标已被租户改动 + (内容不同)则绝不覆盖,记入 `skills_skipped_differ` 并原样保留。 +- 包内某技能目录缺 `SKILL.md` 时跳过,不凭空捏造。 +- 因此租户任务内只能看到 `SKILL.md` 随附的附录全文,附录仍是 + 租户侧的权威版本;本文件与附录源只在源码包一侧可见。 + +删除行为按现行实现写,不按设想写:租户已删 `SKILL.md` 会在 +下次播种时重建(missing 即复制)。删除保留机制见 tombstone 设计, +行为以 skill_seed 实现为准。 + +## 与 CHANGELOG 的分工 + +同一说明只落一处,本文件与 `CHANGELOG.md` 不复制粘贴同一份说明: + +- `README.md`(本文件):包是干什么的、文件都在哪、从哪验证、 + 播种后租户能看到什么。回答“怎么找到、怎么核对”。 +- `CHANGELOG.md`:何时补了什么、此前缺什么、缺的那段历史是否 + 可追溯。回答“这包经历了什么变化”。此前 v0.1 → v0.2 的 5 个 + P0 缺口明细因当时无此文件而无法从本文件追溯,`CHANGELOG.md` + 开篇已明说不编造那段历史;2026-10-31 起的附录溯源补齐条目 + 记在那里,不重复抄到本文件。 +- `SKILL.md`:规则本身,唯一权威。 +- `tools/check_appendix.py` 的注释:校验的规范形式权威。 +- `cloud/skill_seed.py` 及其测试:播种行为权威。 + +改规则改 `SKILL.md`,改溯源记录改 `CHANGELOG.md`,改校验规则 +改脚本注释与对应测试;本文件只在导航、路径或分工本身变化时才改。 + +## 已知边界 + +- 本轨只补说明:不改 `SKILL.md` 正文语义,不改任何规则, + 不改播种行为,不合并,不部署。 +- 租户已删 `SKILL.md` 会重播种,见上文“播种行为”;删除保留 + 未落地为实现,不以本文件声称已实现。 +- 附录分隔符不统一的现状与校验处理见 `CHANGELOG.md` 与脚本注释, + 本文件不另行规定、不强行统一 `SKILL.md`。 +- `SKILL.md` 正文还提到 `templates/artifacts/` 下的模板名 + (PRD 大纲、接单问卷、验收清单);本包 `templates/` 目前只有 + `roles/`,artifacts 模板不在本轨补齐范围,本文件不声称它们已存在, + 引用状态以 `SKILL.md` 原文为准。 +- 播种落盘与 `skills/list` 发现已验证;任务内模型实际调用技能 + 尚未验证,本文件不声称已验证(见 `cloud/skill_seed.py` 的 + honest boundary 注记)。 diff --git a/cloud/tests/test_skill_readme_refs.py b/cloud/tests/test_skill_readme_refs.py new file mode 100644 index 0000000..032ac40 --- /dev/null +++ b/cloud/tests/test_skill_readme_refs.py @@ -0,0 +1,88 @@ +"""Same-directory references of the software-engineering skill. + +Background: SKILL.md's header points at two same-directory files -- +``CHANGELOG.md`` ("see CHANGELOG") and ``README.md`` (install path). +CHANGELOG was supplied by the appendix-provenance track; README was +still missing, so the header's install-path pointer dangled. This file +pins the repair, and nothing else: + +- every file SKILL.md cites with the qualifier "same directory" + (``同目录 `X.md` ``) exists in the skill directory -- today exactly + CHANGELOG.md and README.md, so a future dangling same-directory + reference fails here instead of shipping silently; +- README covers the topics it is supposed to cover (package layout, + appendix provenance entry point, seeding behaviour, and the + tombstone-design placeholder for deletion retention) without + becoming a second copy of the appendix or of CHANGELOG: it must + not contain appendix chapter headings, and it must not invent a + tombstone marker filename. + +Scope guard: this test never edits SKILL.md. In particular it does +NOT assert on the templates/artifacts/ names mentioned in the SKILL +body (PRD outline, intake questions, acceptance checklist): those are +not same-directory references, they are not in this pack, and their +status is README's stated known boundary, not a dangling header +reference to pin here. +""" + +import re +import unittest +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +SKILL_DIR = REPO_ROOT / "cloud" / "skills" / "software-engineering" +SKILL_FILE = SKILL_DIR / "SKILL.md" +README_FILE = SKILL_DIR / "README.md" + +# ``同目录 `X.md` `` -- the header's same-directory citations only. +SAME_DIR_REF_RE = re.compile(r"同目录\s*`([^`]+\.md)`") + + +class SkillReadmeRefsTestCase(unittest.TestCase): + def test_same_dir_refs_all_exist(self): + skill_text = SKILL_FILE.read_text(encoding="utf-8") + refs = set(SAME_DIR_REF_RE.findall(skill_text)) + self.assertEqual( + refs, {"CHANGELOG.md", "README.md"}, + "SKILL.md 的同目录引用应恰为 CHANGELOG 与 README,实际:%s" + % sorted(refs)) + for name in sorted(refs): + self.assertTrue( + (SKILL_DIR / name).is_file(), + "SKILL.md 引用的同目录文件缺失:%s" % name) + + def test_readme_covers_required_topics(self): + self.assertTrue(README_FILE.is_file(), "README.md 缺失") + text = README_FILE.read_text(encoding="utf-8") + for fragment in ( + "templates/roles/", + "check_appendix.py", + "CHANGELOG.md", + "skill_seed", + "只复制各技能的 `SKILL.md` 一个文件", + # Deletion retention is NOT implemented: README must + # say so via the tombstone-design placeholder, and + # defer to the skill_seed implementation, verbatim. + "删除保留机制见 tombstone 设计", + "行为以 skill_seed 实现为准", + "分工", + ): + self.assertIn(fragment, text, "README.md 应提及:%s" % fragment) + + def test_readme_does_not_duplicate_appendix(self): + text = README_FILE.read_text(encoding="utf-8") + self.assertIsNone( + re.search(r"^## 附录角色:", text, flags=re.M), + "README 不应复制附录章节标题,否则会成为第二份会漂移的附录") + + def test_readme_invents_no_tombstone_marker(self): + text = README_FILE.read_text(encoding="utf-8") + self.assertIn("tombstone", text, "README 应提及 tombstone 设计占位") + self.assertIsNone( + re.search(r"tombstone\.[a-z]+", text), + "tombstone 未落地为实现,README 不得编造标记文件名") + self.assertNotIn(".tombstone", text) + + +if __name__ == "__main__": + unittest.main()