Skip to content

Commit c9060cb

Browse files
authored
Merge pull request #60 from stack-wuh/docs/20261012-docs-pr-message-norms
docs(norms): PR/commit message 结构真源进 norms,并对齐 release/publish/hotfix 三处调用面
2 parents 8db66b2 + e3ae24c commit c9060cb

9 files changed

Lines changed: 244 additions & 11 deletions

File tree

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
| `rules/behavior.md` | 行为准则 |
2020
| `rules/iron-laws.md` | 验证、调试、分支、deterministic CLI 和领域模型铁律 |
2121
| `norms/knowledge-cards.md` | Knowledge 卡片格式、生命周期和冲突处理 |
22-
| `norms/*.md` | UI、API、交互、代码风格、领域模型和验证规范 |
22+
| `norms/*.md` | UI、API、交互、代码风格、领域模型、验证规范与 PR/commit message 结构(`norms/pr-message.md`,模板在 `norms/templates/pr-body.md`) |
2323
| `knowledge/*.md` | 跨项目经验与协作知识 |
2424

2525
项目外 memory 不是工作流执行真相源。

‎docs/cli-guide.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ L 级变更或多变更并行时用 worktree 替代分支腾挪:apply §3 会
116116
|------|------|
117117
| `commit plan --name <n> --files <逗号分隔> --message <m>` | 预览提交 |
118118
| `commit execute --name <n> --files <f> --message <m> --confirm` | 精确 `git add -- <files>` + commit;拒绝 `.`、`-A`、越界路径(退出码 4) |
119-
| `publish plan --name <n> --title <t> --body <b>` | 预览 push + PR 载荷 |
119+
| `publish plan --name <n> --title <t> --body-file <f>` | 预览 push + PR 载荷;`--title` 与 `--body`/`--body-file` 必填,缺省即 `PR_PARAMS_REQUIRED` |
120120
| `publish execute --name <n> --title <t> --body <b> --confirm` | push 后查询/创建 PR;已存在的 open PR 会被复用 |
121-
| `release plan --name <n> --files <f> --message <m> --title <t>` | 一次返回 commit + PR 的完整计划;planHash 与执行参数写入 brief |
121+
| `release plan --name <n> --files <f> --message <m> --title <t> --body-file <f>` | 一次返回 commit + PR 的完整计划;`--title` 与 `--body`/`--body-file` 必填,缺省即 `PR_PARAMS_REQUIRED`;planHash 与执行参数写入 brief |
122122
| `release execute --name <n> --confirm` | 一次完成 commit → push → PR;幂等,可断点续跑 |
123123

124124
**命名消歧**:本表的 `publish` 命令键(=推分支 + 创建/复用 PR)与本仓的 **publish 阶段**(正式发版:bump + tag + Release)**无关**。发 PR 用 `release.*` 或 `publish.*` 命令键;发版走 `shadow-dev-publish` 技能。归属登记见 `shadow-docs/domain.md` 术语表。
@@ -165,7 +165,8 @@ shadow-dev review execute --name 20260906-feat-export \
165165
# release:计划 → 确认 → 一键发布(commit + push + PR)
166166
shadow-dev release plan --name 20260906-feat-export \
167167
--files src/export.ts,shadow-docs/changes/20260906-feat-export/brief.md \
168-
--message "feat(cli): 导出功能" --title "feat: 导出功能"
168+
--message "feat(cli): 导出功能" --title "feat(cli): 导出功能" --body-file .tmp/pr-body.md
169+
# (--title 与 --body/--body-file 必填:缺省时 CLI 直接 PR_PARAMS_REQUIRED 拒绝,不再回落变更名或代写一行 Closes)
169170
# (向用户确认后)
170171
shadow-dev release execute --name 20260906-feat-export --confirm
171172

‎norms/pr-message.md‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# PR 与 commit message 结构规范
2+
3+
> 按需加载:写 `release plan` 的 `--title` / `--message` / `--body` 前读本文件。不属于 `rules/`,不做全局注入。
4+
> 结构真源是本文件;**模板文件本体在 `norms/templates/pr-body.md`**,宿主 `.github/PULL_REQUEST_TEMPLATE.md` 由它生成。
5+
6+
## 1. 载荷参数必须显式传(CLI 已在 plan 侧设硬门)
7+
8+
- **防什么**:曾几何时 `release plan` / `publish plan` 不传 `--title` 会把 PR 标题回退成 brief slug(`20261011-fix-webhook-body-limit` 即由此上到 main),不传 `--body` 会把正文自动写成一行 `Closes #N` + `完整 brief:…`(#521 / #528 的薄正文同此)
9+
- **触发**:`release plan` / `publish plan` 组参数时
10+
- **做什么**:`--title` 与 `--body`(或多行正文用 `--body-file`)逐条显式给出。CLI 现于 plan 侧直接拒绝:`PR_PARAMS_REQUIRED`;`--body` 与 `--body-file` 同传即 `BODY_PARAM_CONFLICT`。`--message` 仍须显式给(commit 载荷,不属该门)。取值序「显式入参 > brief 持久化的上一次 plan 参数」——re-plan 免重传仍成立,被禁的是 **CLI 自行编造**,不是承认人上一次给过的值
11+
- **验收**:plan 成功即三者为人给值;缺任一则命令 exit 1 且 `code=PR_PARAMS_REQUIRED`(无载荷产出、无盘写)
12+
13+
## 2. 标题必须是 conventional-commits,词表由人维护
14+
15+
- **防什么**:同仓库并存 `[feature] …`、`chore(ci): …`、裸分支名三种标题,历史无法扫读、无法机器校验
16+
- **触发**:commit subject 与 PR 标题
17+
- **做什么**:`<type>(<scope>): <subject>`
18+
- `type` ∈ `build | feat | chore | style | docs | ui | fix | refactor | ci | test`,小写,取自宿主 `commitlint.config.js` 的 `type-enum`;表外禁用,不新造
19+
- `scope` ∈ `next | nest | components | hooks | config | shared-contracts | docs | ci`(2026-10-12 定表,8 项);表外一律不写,**禁止 AI 自行造词**,也不新增同义层级的词
20+
- `scope` 只表达**代码落点**(目录/包边界)。`deploy` 已删除——它不是落点而是动作,部署链的落点就是 `.github/workflows/**`,用 `ci` 表达,具体动作(restart-nginx、切流、发版)写进 subject
21+
- `docs` 作 scope 时只指 `packages/docs` 这个包;「只改文档」属意图,走 `type=docs` 且不写 scope
22+
- 同一维度不重复表达:`type` 已经说明意图时不再把它塞进 scope(避免 `ci(ci):`、`docs(docs):`)
23+
- 词表变更由人改本文件后重新生成模板
24+
- `subject` 单行、≤ 50 字、结尾不加句号,不强制动词开头;整条 ≤ 100 字符(`header-max-length`)
25+
- 标题内不写 PR 编号:PR 合并时 GitHub 自动把 `(#<PR>)` 追加进 subject,手写会产出 `(#512) (#515)` 双编号
26+
- 一个 PR 只装一种 `type`(`CONTRIBUTING.md`「禁止混交」)
27+
- **验收**:标题匹配 `^(build|feat|chore|style|docs|ui|fix|refactor|ci|test)(\([^)]+\))?!?: \S` 且不含 `(#<数字>)`
28+
29+
## 3. 正文只用固定四段,任何 type 都不得裁剪
30+
31+
- **防什么**:正文空、只有一行 `Closes`,或自造 `## 背景`、`## todo` 等一次性标题
32+
- **触发**:写 PR 正文时
33+
- **做什么**:段落白名单为 变更 / 影响 / 验证 / 关联 四段。**四段对所有 type 一律必需**(2026-10-12 裁定:保持风格一致,不做按 type 的裁剪),确无内容也保留段落并写 `- 无`
34+
35+
| type | 必需段落 |
36+
|------|----------|
37+
| `fix` `ci` `build` | 变更、影响、验证、关联 |
38+
| `feat` `refactor` `ui` `style` `test` | 变更、影响、关联 |
39+
| `docs` `chore` | 变更、关联 |
40+
41+
- **验收**:四段齐全、顺序一致、逐段非空;正文出现的段落标题全在白名单内(自造 `## 背景` 等即判红)
42+
43+
## 4. 每个 PR 必须可追溯到一个 issue
44+
45+
- **防什么**:改动无来源,事后答不出「为什么动这里」
46+
- **触发**:建 PR 前
47+
- **做什么**:`关联` 段必须有 `Closes #<issue>`;没有对应 issue 时**先建 issue 再提 PR**,不许拿 PR 当来源记录
48+
- **验收**:正文含 `Closes #<数字>`,且该 issue 存在
49+
50+
## 5. 禁止署名尾注
51+
52+
- **防什么**:AI 自发追加 `Co-authored-by: 吴红02 <596540@ky-tech.com.cn>`——该约定在仓库规则、shadow-dev CLI、git 全局配置中均不存在,纯属模型习惯
53+
- **触发**:生成 commit message 或 PR 正文时
54+
- **做什么**:不写 `Co-authored-by:` 等任何署名行;不在正文重复 PR 编号或标题
55+
- **验收**:对 `co-authored-by`(忽略大小写)零命中
56+
57+
## 6. 换行不许切断 token
58+
59+
- **防什么**:按 ~40 列硬折,产出 `完整\nbrief:…` 这类把词切碎的正文
60+
- **触发**:写正文时
61+
- **做什么**:单行 ≤ 100 字符;文件路径、issue 编号、反引号代码、`shadow-docs/…` 链接整行不折断——宁可一行长,不可中间断
62+
- **验收**:`brief:`、路径、`#<数字>` 不出现在行首
63+
64+
## 模板与生成
65+
66+
- 模板本体:`norms/templates/pr-body.md`(纯模板,不含规则文字)
67+
- 生成:执行脚本住在 **shadow-dev-cli 仓的 scripts 目录**下的 `gen-pr-template.mjs`(本插件只出规范与模板,不出脚本)。调用形态:`node <shadow-dev-cli 仓的 scripts 目录>/gen-pr-template.mjs --template norms/templates/pr-body.md --out <宿主>/.github/PULL_REQUEST_TEMPLATE.md`
68+
- 校验:同一脚本加 `--check`,只比对不落盘,不一致 exit 1(宿主手改即判红)
69+
- 宿主模板是生成物,手改会被覆盖;要改结构就改规范与模板文件
70+
- 落位约束:模板与规范不得放进 `skills/*/` 的子目录——`bind` 的 `dirHash` 对 skill 目录做 `readdirSync` + `readFileSync`,遇子目录即 `EISDIR` 崩(2026-10-12 实测复现)

‎norms/templates/pr-body.md‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!--
2+
标题:<type>(<scope>): <subject> —— PR 合并时 GitHub 会自动追加 (#<PR>),所以标题里不要自己写编号
3+
type ∈ build | feat | chore | style | docs | ui | fix | refactor | ci | test(小写,commitlint 枚举;表外类型禁止,也不新造)
4+
scope ∈ next | nest | components | hooks | config | shared-contracts | docs | ci(8 项,只表代码落点;deploy 已删——它是动作不是落点,部署链用 ci + subject 表达;表外一律不写,禁止 AI 自由推导)
5+
subject 单行 ≤ 50 字,不以句号结尾;整条标题 ≤ 100 字符;一个 PR 只装一种 type
6+
7+
正文:只允许 变更 / 影响 / 验证 / 关联 四段
8+
四段对所有 type 一律必需,不裁剪;确无内容也保留段落并写「- 无」
9+
单行 ≤ 100 字符;路径 / issue 编号 / 反引号代码 / brief 链接禁止被换行切断(反例:`完整\nbrief:…`)
10+
必须可追溯到 issue:无对应 issue 时先建 issue 再提 PR
11+
禁止 `Co-authored-by:` 等署名尾注;禁止在正文重复 PR 编号或标题
12+
13+
本文件由 shadow-dev-workflow 的 norms/pr-message.md + norms/templates/pr-body.md 生成,请勿手改。
14+
-->
15+
16+
## 变更
17+
-
18+
19+
## 影响
20+
- 行为/触发面变化:
21+
- 明确不受影响的范围:
22+
23+
## 验证
24+
- 命令与结果:
25+
- 未覆盖项(明说,不留空白):
26+
27+
## 关联
28+
- Closes #
29+
- brief: shadow-docs/changes/<slug>/brief.md

‎shadow-docs/INDEX.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@
2626
| 20261010-refactor-domain-driven-workflow | committed | shadow-docs/changes/20261010-refactor-domain-driven-workflow/brief.md |
2727
| 20261011-refactor-kill-tdd-verification | ✅ 完成 | shadow-docs/changes/archive/20261011-refactor-kill-tdd-verification/brief.md |
2828
| 20261012-build-gate-reproducibility | ✅ 完成 | shadow-docs/changes/archive/20261012-build-gate-reproducibility/brief.md |
29-
| 20261012-chore-release-v7.0.0 | published | shadow-docs/changes/20261012-chore-release-v7.0.0/brief.md |
30-
| 20261012-chore-release-v7.0.1 | published | shadow-docs/changes/20261012-chore-release-v7.0.1/brief.md |
31-
| 20261012-chore-release-v7.0.2 | branched | shadow-docs/changes/20261012-chore-release-v7.0.2/brief.md |
29+
| 20261012-chore-release-v7.0.0 | ✅ 完成 | shadow-docs/changes/archive/20261012-chore-release-v7.0.0/brief.md |
30+
| 20261012-chore-release-v7.0.1 | ✅ 完成 | shadow-docs/changes/archive/20261012-chore-release-v7.0.1/brief.md |
3231
| 20261012-fix-release-smoke-field-contract | ✅ 完成 | shadow-docs/changes/archive/20261012-fix-release-smoke-field-contract/brief.md |
3332
| 20261012-fix-smoke-failure-as-fixture | published | shadow-docs/changes/20261012-fix-smoke-failure-as-fixture/brief.md |
3433
| 20261012-refactor-evidence-gate-layers | ✅ 完成 | shadow-docs/changes/archive/20261012-refactor-evidence-gate-layers/brief.md |

0 commit comments

Comments
 (0)