release-skill 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/CHANGELOG.md +104 -0
  5. package/INSTALL.md +81 -1
  6. package/INSTALL.zh-CN.md +69 -1
  7. package/README.md +233 -8
  8. package/README.zh-CN.md +188 -8
  9. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  10. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  11. package/adapters/claude/bin/release-skill.bundle.mjs +14164 -9912
  12. package/adapters/claude/bin/release-skill.mjs +24 -4
  13. package/adapters/claude/native/safe-write/binding.gyp +2 -1
  14. package/adapters/claude/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  15. package/adapters/claude/native/safe-write/prebuilds.json +1 -1
  16. package/adapters/claude/schemas/.render-manifest.json +10 -10
  17. package/adapters/claude/schemas/release-project.schema.json +141 -0
  18. package/adapters/claude/skills/release-help/SKILL.md +21 -0
  19. package/adapters/claude/skills/release-prepare/SKILL.md +17 -6
  20. package/adapters/claude/skills/release-publish/SKILL.md +3 -1
  21. package/adapters/claude/skills/release-reconcile/SKILL.md +1 -1
  22. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  23. package/adapters/codex/bin/release-skill.bundle.mjs +14164 -9912
  24. package/adapters/codex/bin/release-skill.mjs +24 -4
  25. package/adapters/codex/native/safe-write/binding.gyp +2 -1
  26. package/adapters/codex/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  27. package/adapters/codex/native/safe-write/prebuilds.json +1 -1
  28. package/adapters/codex/schemas/.render-manifest.json +10 -10
  29. package/adapters/codex/schemas/release-project.schema.json +141 -0
  30. package/adapters/codex/skills/release-help/SKILL.md +21 -0
  31. package/adapters/codex/skills/release-prepare/SKILL.md +17 -6
  32. package/adapters/codex/skills/release-publish/SKILL.md +3 -1
  33. package/adapters/codex/skills/release-reconcile/SKILL.md +1 -1
  34. package/bin/release-skill-cli.mjs +163 -4
  35. package/bin/release-skill.bundle.mjs +14164 -9912
  36. package/bin/release-skill.mjs +24 -4
  37. package/native/safe-write/binding.gyp +2 -1
  38. package/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  39. package/native/safe-write/prebuilds.json +1 -1
  40. package/package.json +2 -2
  41. package/references/.render-manifest.json +4 -4
  42. package/references/02-project-config.md +24 -0
  43. package/references/05-evidence-and-errors.md +5 -0
  44. package/schemas/.render-manifest.json +10 -10
  45. package/schemas/release-project.schema.json +141 -0
  46. package/scripts/build-bundle.mjs +15 -2
  47. package/skills/release-help/SKILL.md +21 -0
  48. package/skills/release-prepare/SKILL.md +17 -6
  49. package/skills/release-publish/SKILL.md +3 -1
  50. package/skills/release-reconcile/SKILL.md +1 -1
  51. package/skills-src/release-help/SKILL.md +21 -0
  52. package/skills-src/release-prepare/SKILL.md +17 -6
  53. package/skills-src/release-publish/SKILL.md +3 -1
  54. package/skills-src/release-reconcile/SKILL.md +1 -1
  55. package/src/adapters/plugin-marketplace.mjs +70 -3
  56. package/src/artifacts/transaction-journal.mjs +1126 -105
  57. package/src/artifacts/transaction.mjs +313 -130
  58. package/src/commands/docs.mjs +332 -0
  59. package/src/commands/prepare.mjs +324 -17
  60. package/src/commands/reconcile.mjs +4 -1
  61. package/src/commands/verify.mjs +4 -1
  62. package/src/core/errors.mjs +64 -2
  63. package/src/core/plan.mjs +59 -1
  64. package/src/core/redact.mjs +206 -0
  65. package/src/docs/changelog-renderer.mjs +853 -0
  66. package/src/docs/config.mjs +337 -0
  67. package/src/docs/notes-loader.mjs +432 -0
  68. package/src/docs/notes.mjs +553 -0
  69. package/src/docs/readme-renderer.mjs +647 -0
  70. package/src/docs/refresh-planner.mjs +542 -0
  71. package/src/docs/refresh-service.mjs +675 -0
@@ -23,12 +23,32 @@ try {
23
23
  }
24
24
 
25
25
  if (!bundleExists) {
26
+ // Fail closed with static text only: never interpolate bundlePath (or any
27
+ // other machine-specific value) so a copied/installed launcher cannot leak
28
+ // absolute paths, usernames, or host layout on stdout/stderr. This branch
29
+ // is self-contained by design — it must not import src/* or rely on the
30
+ // missing bundle's redaction helpers.
26
31
  console.error(
27
- `Error: release-skill bundle not found at:\n ${bundlePath}\n` +
28
- `The bundle is required for installed-plugin execution.\n` +
29
- `Reinstall the plugin or run 'node scripts/build-bundle.mjs' in the source checkout.`,
32
+ `Error: release-skill bundle not found (release-skill.bundle.mjs).\n` +
33
+ `The self-contained bundle is required for installed-plugin execution.\n` +
34
+ `Reinstall the plugin, or run 'node scripts/build-bundle.mjs' in a source checkout to rebuild it.`,
30
35
  );
31
36
  process.exit(1);
32
37
  }
33
38
 
34
- await import(bundlePath);
39
+ // The bundle owns the command lifecycle: its entry awaits command completion
40
+ // and exits with the real business exit code (success, business errors,
41
+ // handled async rejections, unknown commands). The launcher only guards the
42
+ // load itself: if the bundle cannot be evaluated (corrupt or incompatible
43
+ // build), fail closed with static text only — module-load failures carry
44
+ // absolute paths in their messages, so the failure is never interpolated.
45
+ try {
46
+ await import(bundlePath);
47
+ } catch {
48
+ console.error(
49
+ `Error: release-skill bundle failed to load (release-skill.bundle.mjs).\n` +
50
+ `The self-contained bundle is required for installed-plugin execution.\n` +
51
+ `Reinstall the plugin, or run 'node scripts/build-bundle.mjs' in a source checkout to rebuild it.`,
52
+ );
53
+ process.exit(1);
54
+ }
@@ -24,7 +24,8 @@
24
24
  "xcode_settings": {
25
25
  "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
26
26
  "CLANG_CXX_LANGUAGE_STANDARD": "c++20",
27
- "MACOSX_DEPLOYMENT_TARGET": "11.0"
27
+ "MACOSX_DEPLOYMENT_TARGET": "11.0",
28
+ "GCC_GENERATE_DEBUGGING_SYMBOLS": "NO"
28
29
  }
29
30
  }
30
31
  ],
@@ -3,7 +3,7 @@
3
3
  "prebuilds": {
4
4
  "darwin-arm64": {
5
5
  "path": "prebuilds/darwin-arm64/safe_write.node",
6
- "sha256": "a0e39732c359f3faa23715e6d17d8bc5fec5c5b86e0390220e1beb9f5def2d2e",
6
+ "sha256": "f5ffc367a1c49fed812dc430246cd8b07fb722653cc9738acabcedb17d6bab67",
7
7
  "exports": [
8
8
  "openRoot",
9
9
  "openDir",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,7 +42,7 @@
42
42
  "scripts": {
43
43
  "build": "node --check src/**/*.mjs && node scripts/build-bundle.mjs",
44
44
  "test": "node --test test/*.test.mjs",
45
- "test:no-dogfood": "node --test $(ls test/*.test.mjs | grep -v -E 'dogfood|artifacts-safe-fs-native-seam')",
45
+ "test:no-dogfood": "node --test $(ls test/*.test.mjs | grep -v -E 'dogfood|artifacts-safe-fs-native-seam|setup-live-samples')",
46
46
  "typecheck": "node --check src/**/*.mjs bin/*.mjs",
47
47
  "sync:skills": "node scripts/sync-skills.mjs",
48
48
  "sync:skills:check": "node scripts/sync-skills.mjs --check",
@@ -10,8 +10,8 @@
10
10
  "bytes": 7770
11
11
  },
12
12
  "02-project-config.md": {
13
- "digest": "6c8d6262e5fe9c034ad961b01b8fed684adb6272a7ce4c0e4dd87bfb061519ac",
14
- "bytes": 7017
13
+ "digest": "837f1643245f544011eddf336f31718fc05a1fe7dd4f181f627168a8ffcec183",
14
+ "bytes": 16501
15
15
  },
16
16
  "03-readme-quality.md": {
17
17
  "digest": "d74e41052592443ebbd450d0ace5f9b9b19c7a94db50f952b17c1c5a4c5dc230",
@@ -22,8 +22,8 @@
22
22
  "bytes": 4682
23
23
  },
24
24
  "05-evidence-and-errors.md": {
25
- "digest": "63987751b6d3a74f02033d4e23daa1f3c30d299827de9d8242b425887607b2ac",
26
- "bytes": 6658
25
+ "digest": "244cd989dc86031e69fbdce8ec299359c3495c4f4dad4c903b119ef0e7a39569",
26
+ "bytes": 8260
27
27
  },
28
28
  "06-adapter-contract.md": {
29
29
  "digest": "f727bfbdcd2bd6f95edc144a4fd61732ecaac2a49ffda296dbf6923f14a0c2f1",
@@ -49,6 +49,19 @@ releaseUnits: # 发布单元数组,至少 1 个
49
49
  previousPublicBaseline: # 必填;每个 unit 独立声明
50
50
  mode: none # none | bound
51
51
  # bound 时还必须提供 repo/ref/commit,可选 tree/manifestDigest
52
+ releaseDocuments: # 可选;结构化双语发布文档刷新,缺省保持既有行为
53
+ notesSource: <string> # 说明源相对路径,仅允许 {version} 占位符与 .yaml/.yml/.json 后缀
54
+ locales: [<string>] # 非空唯一语种数组,如 [en, zh-CN]
55
+ changelogs: # 非空数组
56
+ - path: <string>
57
+ locale: <string>
58
+ readmes: # 非空数组
59
+ - path: <string>
60
+ locale: <string>
61
+ regions: [<string>] # 非空唯一受管区域 id
62
+ versionMarkers: # 可选;id 唯一
63
+ - id: <string>
64
+ pattern: <string> # 含 {version} 占位符的精确标记模式
52
65
 
53
66
  verificationGates: # 可选;定义会进入冻结计划摘要
54
67
  - id: <stable-id>
@@ -113,6 +126,17 @@ human-owned 权威源,然后重新 prepare、审阅并 approve;后续环节
113
126
 
114
127
  已有公开仓不得使用 `mode: none` 伪装成首次发布。示例中的 commit 只是校准时点证据;每轮 production prepare 都必须在线重观测。
115
128
 
129
+ ### 3.3 发布文档刷新(releaseDocuments)
130
+
131
+ `releaseUnits[].releaseDocuments` 可选;未配置时保持既有行为。配置后,一份结构化说明源确定性刷新 README 受管区域、唯一版本标记的机器值和 CHANGELOG 当前版本受管条目;区域外字节、旧版本条目、前言、链接定义和人工说明逐字保留。核心命令不联网、不调用大模型、不自动翻译。
132
+
133
+ - `notesSource` 与各目标路径均为发布单元根相对安全路径,不得为绝对路径,不得包含 `..`、反斜杠、空段或 NUL;`notesSource` 只允许 `{version}` 占位符与 `.yaml`、`.yml`、`.json` 后缀。
134
+ - `locales` 非空且唯一;每个目标语种必须属于 `locales`,目标规范路径不得重复或发生大小写/Unicode 碰撞。
135
+ - 说明源的 `version` 必须与 `version.source` 解析结果精确一致,`date` 为 `YYYY-MM-DD`;每个配置语种恰好出现一次,`summary` 和变更项去除首尾空白后非空,`security`/`breaking`/`added`/`changed`/`deprecated`/`removed`/`fixed` 至少一个类别含条目。重复键、YAML alias/anchor、合并键、自定义标签、未知字段、缺少或多余语种、语种回退均失败关闭。
136
+ - README 受管区域以 `<!-- release-skill:managed:start id=<region> -->` 与对应结束标记做字节偏移替换;版本标记必须唯一匹配且只替换机器值,零次或多次匹配拒绝写入。CHANGELOG 当前版本受管条目由含 `baseline` 摘要的注释包围,基线不符、非受管同版本条目或标记损坏返回冲突。
137
+ - `docs refresh --unit <id>` 默认只读演练,输出逐文件相对路径、语种、新旧摘要和 `refreshDigest`;写入必须同时提供 `--write`、精确 `--confirm-refresh <refreshDigest>` 和 `--ack-local-document-write`,全部目标作为一个事务提交。该授权只覆盖声明的本地文档目标,不包含 hook、提交、推送、发布或安装。
138
+ - `prepare` 在 hook、基线、快照、远端检查和计划冻结前执行同一只读规划器:`clean` 继续,`changes` 以 `RELEASE_DOCS_STALE` 失败关闭并给出精确演练/写入参数。`prepare`、`publish`、`reconcile` 永不隐式刷新文档;刷新后重新 prepare 会自然改变快照、workspace digest 与 plan digest,使旧批准失效。
139
+
116
140
  ---
117
141
 
118
142
  ## 4. Hooks 参数约束
@@ -21,6 +21,11 @@
21
21
  | `POST_PUBLISH_VERIFY_FAILED` | 发布后验证未通过 | 安装测试失败、泄漏审计未通过、provenance 验证失败 | 检查失败原因,可能需要人工干预 |
22
22
  | `SETUP_DIGEST_MISMATCH` | setup 事实或答案已漂移 | dry-run 后 README/package/manifest/remote/answers 发生变化,或确认摘要错误 | 重新运行 setup dry-run、审阅并确认新摘要 |
23
23
  | `CONFIG_EXISTS` | setup 目标配置已经存在 | 写入模式试图创建已有 `.release-skill/project.yaml` | 不覆盖;运行 assess 并人工增量编辑 |
24
+ | `RELEASE_DOCS_INVALID` | 发布文档配置或说明数据语义非法 | `releaseDocuments` 配置不合规、说明源含重复键/alias/未知字段、版本漂移 | 修正配置或说明源后重新演练 |
25
+ | `RELEASE_DOCS_TRANSLATION_MISSING` | 说明源缺少或多余配置语种 | `locales` 声明多个语种但说明源只含其一 | 补齐全部配置语种,禁止语种回退 |
26
+ | `RELEASE_DOCS_CONFLICT` | 目标文档存在人工冲突 | 非受管同版本 CHANGELOG 条目、受管标记缺失/重复/损坏、版本标记非唯一 | 人工修复目标并保留人工修改后重新演练 |
27
+ | `RELEASE_DOCS_REFRESH_STALE` | 写入确认绑定的候选已变化 | 演练后说明源或目标发生变化,仍用旧 `refreshDigest` 写入 | 重新演练取得新的 `refreshDigest` 并重新确认 |
28
+ | `RELEASE_DOCS_STALE` | prepare 检测到发布文档未刷新 | 说明源已更新但 README/CHANGELOG 受管内容未同步 | `docs refresh` 演练 → 确认写入 → 审阅提交 → 重新 prepare |
24
29
 
25
30
  ---
26
31
 
@@ -2,8 +2,8 @@
2
2
  "source": "schemas",
3
3
  "files": {
4
4
  "approval-record.schema.json": {
5
- "digest": "38df0f095cc4b5be650a04513f8ea10cef5a6c3d0780ab38b0c5ee559d6ac7a5",
6
- "bytes": 2611
5
+ "digest": "2ddac883435110e2c036cfadb00ce0c4d347a97d674b2c4ea9fcda3d72f81b8b",
6
+ "bytes": 3135
7
7
  },
8
8
  "artifact-lock.schema.json": {
9
9
  "digest": "6a5f4c826540b849571f173118bcbd778dfaceb4133034036ae565c3e9e353e5",
@@ -14,24 +14,24 @@
14
14
  "bytes": 1546
15
15
  },
16
16
  "artifact-policy.schema.json": {
17
- "digest": "fa28b3856f2ed48b666458120c9b9d84d42c9ebfbec13bc866e8d71ecef6dea7",
18
- "bytes": 2117
17
+ "digest": "86c9cab024ebe9c7f8ec3358fcc12d05b8ed246ab59da2b0acc6a517f31274ea",
18
+ "bytes": 2488
19
19
  },
20
20
  "evidence-event.schema.json": {
21
21
  "digest": "b7e14522a5aba818cab191545f23d2323be0e4c80566e9d3e4da576103a00490",
22
22
  "bytes": 2390
23
23
  },
24
24
  "release-plan.schema.json": {
25
- "digest": "3647a554d7f8b1041078301b4de06c9aade0e1083ba8f6492cf4d3a825ea40ef",
26
- "bytes": 4084
25
+ "digest": "13baa16d9a762cc04c7f28fc9d31ed81d381008c71eb044f4e97de252b4cc324",
26
+ "bytes": 22942
27
27
  },
28
28
  "release-project.schema.json": {
29
- "digest": "6ae488bbad12563323892cc65f236a0ca3443aa6c0f4a17d69095a5c1af2574e",
30
- "bytes": 6218
29
+ "digest": "4e46d6b1956a14d01471e7cf1cde3c0a6971c4f8844603fedd2a52f3535a43b6",
30
+ "bytes": 24167
31
31
  },
32
32
  "release-run.schema.json": {
33
- "digest": "eeb16507f0d852ed1c53b0fbeeeda0ab1c15abea63554d6f4c127f75ca1dc673",
34
- "bytes": 4037
33
+ "digest": "74f087f8815f00997c6ad3bac033660b9b69de226a6f5c48a96194dba0fa872c",
34
+ "bytes": 8917
35
35
  }
36
36
  }
37
37
  }
@@ -200,6 +200,12 @@
200
200
  },
201
201
  "smokeExpectedJson": {
202
202
  "type": "object"
203
+ },
204
+ "timeoutMs": {
205
+ "type": "integer",
206
+ "minimum": 30000,
207
+ "maximum": 900000,
208
+ "description": "Timeout in milliseconds for plugin marketplace install commands. Only valid for claude-plugin and codex-plugin distributions."
203
209
  }
204
210
  },
205
211
  "allOf": [
@@ -219,6 +225,23 @@
219
225
  ]
220
226
  }
221
227
  },
228
+ {
229
+ "if": {
230
+ "required": [
231
+ "timeoutMs"
232
+ ]
233
+ },
234
+ "then": {
235
+ "properties": {
236
+ "type": {
237
+ "enum": [
238
+ "claude-plugin",
239
+ "codex-plugin"
240
+ ]
241
+ }
242
+ }
243
+ }
244
+ },
222
245
  {
223
246
  "if": {
224
247
  "properties": {
@@ -422,6 +445,9 @@
422
445
  }
423
446
  ]
424
447
  }
448
+ },
449
+ "releaseDocuments": {
450
+ "$ref": "#/definitions/releaseDocuments"
425
451
  }
426
452
  },
427
453
  "allOf": [
@@ -611,6 +637,121 @@
611
637
  }
612
638
  }
613
639
  },
640
+ "releaseDocuments": {
641
+ "type": "object",
642
+ "description": "Optional configuration-driven multilingual release-document refresh for one release unit. Absence preserves legacy behaviour. Lexical layer only; runtime path, locale-membership and collision checks apply additionally.",
643
+ "required": [
644
+ "notesSource",
645
+ "locales",
646
+ "changelogs",
647
+ "readmes"
648
+ ],
649
+ "additionalProperties": false,
650
+ "properties": {
651
+ "notesSource": {
652
+ "type": "string",
653
+ "minLength": 1,
654
+ "pattern": "^(?!/)(?!\\.\\/)(?!.*\\\\)(?!.*:)(?!\\.\\.(/|$))(?!\\.$)(?!.*\\/\\.\\.\\/)(?!.*\\/\\.\\.$)(?!.*\\/\\.\\/)(?!.*\\/\\/)(?!.*\\0)(?!.*\\/\\.$)(?!.*\\/$)(?!.*\\{(?!version\\}))(?!.*(?<!\\{version)\\}).+\\.(?:yaml|yml|json)$"
655
+ },
656
+ "locales": {
657
+ "type": "array",
658
+ "minItems": 1,
659
+ "uniqueItems": true,
660
+ "items": {
661
+ "type": "string",
662
+ "pattern": "^[a-zA-Z]{2,3}(?:-[A-Za-z0-9]{2,8})*$"
663
+ }
664
+ },
665
+ "changelogs": {
666
+ "type": "array",
667
+ "minItems": 1,
668
+ "items": {
669
+ "$ref": "#/definitions/releaseDocumentsChangelog"
670
+ }
671
+ },
672
+ "readmes": {
673
+ "type": "array",
674
+ "minItems": 1,
675
+ "items": {
676
+ "$ref": "#/definitions/releaseDocumentsReadme"
677
+ }
678
+ }
679
+ }
680
+ },
681
+ "releaseDocumentsChangelog": {
682
+ "type": "object",
683
+ "required": [
684
+ "path",
685
+ "locale"
686
+ ],
687
+ "additionalProperties": false,
688
+ "properties": {
689
+ "path": {
690
+ "type": "string",
691
+ "minLength": 1,
692
+ "pattern": "^(?!/)(?!\\.\\/)(?!.*\\\\)(?!.*:)(?!\\.\\.(/|$))(?!\\.$)(?!.*\\/\\.\\.\\/)(?!.*\\/\\.\\.$)(?!.*\\/\\.\\/)(?!.*\\/\\/)(?!.*\\0)(?!.*\\/\\.$)(?!.*\\/$).*$"
693
+ },
694
+ "locale": {
695
+ "type": "string",
696
+ "pattern": "^[a-zA-Z]{2,3}(?:-[A-Za-z0-9]{2,8})*$"
697
+ }
698
+ }
699
+ },
700
+ "releaseDocumentsReadme": {
701
+ "type": "object",
702
+ "required": [
703
+ "path",
704
+ "locale",
705
+ "regions"
706
+ ],
707
+ "additionalProperties": false,
708
+ "properties": {
709
+ "path": {
710
+ "type": "string",
711
+ "minLength": 1,
712
+ "pattern": "^(?!/)(?!\\.\\/)(?!.*\\\\)(?!.*:)(?!\\.\\.(/|$))(?!\\.$)(?!.*\\/\\.\\.\\/)(?!.*\\/\\.\\.$)(?!.*\\/\\.\\/)(?!.*\\/\\/)(?!.*\\0)(?!.*\\/\\.$)(?!.*\\/$).*$"
713
+ },
714
+ "locale": {
715
+ "type": "string",
716
+ "pattern": "^[a-zA-Z]{2,3}(?:-[A-Za-z0-9]{2,8})*$"
717
+ },
718
+ "regions": {
719
+ "type": "array",
720
+ "minItems": 1,
721
+ "uniqueItems": true,
722
+ "items": {
723
+ "type": "string",
724
+ "pattern": "^[a-z0-9][a-z0-9._-]*$"
725
+ }
726
+ },
727
+ "versionMarkers": {
728
+ "type": "array",
729
+ "minItems": 1,
730
+ "uniqueItems": true,
731
+ "items": {
732
+ "$ref": "#/definitions/releaseDocumentsVersionMarker"
733
+ }
734
+ }
735
+ }
736
+ },
737
+ "releaseDocumentsVersionMarker": {
738
+ "type": "object",
739
+ "required": [
740
+ "id",
741
+ "pattern"
742
+ ],
743
+ "additionalProperties": false,
744
+ "properties": {
745
+ "id": {
746
+ "type": "string",
747
+ "pattern": "^[a-z0-9][a-z0-9._-]*$"
748
+ },
749
+ "pattern": {
750
+ "type": "string",
751
+ "minLength": 1
752
+ }
753
+ }
754
+ },
614
755
  "verificationGate": {
615
756
  "type": "object",
616
757
  "required": [
@@ -28,7 +28,14 @@ const OUTFILE = join(PKG_ROOT, 'bin', 'release-skill.bundle.mjs');
28
28
  // Banner: compute PKG_ROOT deterministically from the bundle's own path.
29
29
  // No env-var override — callers cannot hijack schema/native resolution.
30
30
  // Uses import.meta.url only — process.argv[1] is not a reliable resource root.
31
- const BANNER = `\
31
+ //
32
+ // The banner also injects the package identity (__bundlePkg) as a build-time
33
+ // constant so the CLI --version probe carries no bundle-relative file
34
+ // dependency: the Claude and Codex adapter closures ship the bundle at a
35
+ // different depth with no package.json next to it, while the npm closure does.
36
+ // Reading package.json here (build input) keeps the output deterministic.
37
+ function buildBanner(pkgIdentity) {
38
+ return `\
32
39
  // --- release-skill bundle (deterministic build) ---
33
40
  // Compute package root from the bundle's own file location (import.meta.url).
34
41
  // The bundle lives at <PKG_ROOT>/bin/release-skill.bundle.mjs, so go up one level.
@@ -38,7 +45,10 @@ import { createRequire as __bundleCreateRequire } from 'node:module';
38
45
  const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(import.meta.url)), '..');
39
46
  // Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
40
47
  const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
48
+ // Package identity injected at build time — closure-independent --version probe.
49
+ const __bundlePkg = Object.freeze(${JSON.stringify(pkgIdentity)});
41
50
  `;
51
+ }
42
52
 
43
53
  // Pattern to replace esbuild's broken __require shim with a real require().
44
54
  const REQUIRE_SHIM_PATTERN = /var __require = \/\* @__PURE__ \*\/ \(\(x\) => typeof require !== "undefined" \? require : typeof Proxy !== "undefined" \? new Proxy\(x, \{[\s\S]*?\}\) : x\)\(function\(x\) \{[\s\S]*?\}\);/;
@@ -55,6 +65,9 @@ async function buildBundle() {
55
65
  process.exit(1);
56
66
  }
57
67
 
68
+ const pkgJson = JSON.parse(await readFile(join(PKG_ROOT, 'package.json'), 'utf-8'));
69
+ const banner = buildBanner({ name: pkgJson.name, version: pkgJson.version });
70
+
58
71
  const result = await esbuild.build({
59
72
  entryPoints: [ENTRY],
60
73
  absWorkingDir: PKG_ROOT,
@@ -63,7 +76,7 @@ async function buildBundle() {
63
76
  platform: 'node',
64
77
  target: 'node22',
65
78
  outfile: OUTFILE,
66
- banner: { js: BANNER },
79
+ banner: { js: banner },
67
80
  // External: Node.js builtins, native addon loader, and the native addon itself.
68
81
  external: [
69
82
  'node:*',
@@ -35,8 +35,24 @@ description: "Discoverable entry point for release-skill: dependency and environ
35
35
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" help --json
36
36
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" setup --root <path> --json
37
37
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" assess --root <path> --offline --json
38
+ # 发布文档刷新:默认只读演练
39
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> --json
40
+ # 摘要确认后的本地写入(三项绑定缺一不可)
41
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> \
42
+ --write --confirm-refresh <refreshDigest> --ack-local-document-write --json
38
43
  ```
39
44
 
45
+ ## 发布文档刷新(docs refresh)
46
+
47
+ 发布单元配置 `releaseDocuments` 后,一份结构化双语说明源可确定性刷新 README 受管区域、唯一版本标记的机器值和 CHANGELOG 当前版本受管条目。核心 CLI 不联网、不调用大模型、不自动翻译;只改写声明过的受管区域、版本标记机器值和当前受管条目,区域外字节逐字保留。`prepare` 只检查新鲜度,不写工作树。
48
+
49
+ - **配置**:`releaseDocuments.notesSource`(说明源路径,只允许 `{version}` 占位符与 `.yaml`/`.yml`/`.json` 后缀)、`locales`(如 `[en, zh-CN]`)、`changelogs`(path + locale)、`readmes`(path + locale + `regions` 受管区域 id + `versionMarkers` 版本标记模式)。版本标记模式必须与 README 现有唯一标记精确匹配,`{version}` 代表机器版本值,刷新只替换该值;零次或多次匹配失败关闭。
50
+ - **说明源**:`version` 必须与单元版本精确一致,`date` 为 `YYYY-MM-DD`,每个配置语种恰好出现一次且 `summary`、变更项非空,`security`/`breaking`/`added`/`changed`/`deprecated`/`removed`/`fixed` 至少一个类别含条目。YAML alias、重复键、未知字段和语种回退都失败关闭。
51
+ - **只读演练**:`docs refresh --unit <id> --json` 输出逐文件相对路径、locale、新旧摘要、`version`、`locales`、`inputDigest`、`refreshDigest` 和 `nextCommand.argv`;候选无变化时 `status: "clean"`。
52
+ - **确认写入**:必须同时提供 `--write`、精确 `--confirm-refresh <refreshDigest>` 和 `--ack-local-document-write`,全部目标作为一个事务提交;成功后立即复演必须为 `clean`。
53
+
54
+ **授权边界**:本地发布文档写入授权只覆盖声明的本地文档目标,不是 hook、Git 提交、push、publish 或安装的授权。写入后必须审阅、提交,再重新 prepare。
55
+
40
56
  ## 故障路由
41
57
 
42
58
  | 场景 | 处理 |
@@ -50,6 +66,11 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" assess --root <path> --offlin
50
66
  | 项目配置不存在 | 路由 `release-setup`,默认只读;不得直接生成或覆盖 README/配置 |
51
67
  | assess 失败 | 运行 `node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" assess --offline --json` 获取详情 |
52
68
  | 请求生产发布 | 已有公开版本先调用 `release-prepare --online --production` 观察 bound 基线;人工审阅后再路由 `release-publish` |
69
+ | RELEASE_DOCS_INVALID | 配置或说明源语义非法(重复键、alias、未知字段、版本漂移等);修正配置或说明源后重新演练 |
70
+ | RELEASE_DOCS_TRANSLATION_MISSING | 配置语种缺失或多余;补齐说明源语种,与 `releaseDocuments.locales` 完全一致,不得回退 |
71
+ | RELEASE_DOCS_CONFLICT | 目标含非受管同版本条目、受管标记损坏或人工冲突;人工修复目标并保留人工修改后重新演练 |
72
+ | RELEASE_DOCS_REFRESH_STALE | 确认绑定后候选已变化;重新演练取得新 `refreshDigest` 再确认写入 |
73
+ | RELEASE_DOCS_STALE | prepare 检测到文档未刷新;按 `docs refresh` → 审阅 → 提交 → 重新 prepare 恢复 |
53
74
 
54
75
  ## 后续引导
55
76
 
@@ -23,13 +23,16 @@ description: Freeze an immutable release plan with local configuration, document
23
23
 
24
24
  **不确定性停止**: 遇到无法确定的配置项或版本冲突时,Agent 必须停止并上报用户。
25
25
 
26
+ **发布文档新鲜度门**: 配置了 `releaseDocuments` 的单元在 hook 授权门前先执行同一只读规划器:`clean` 继续;`changes` 抛 `RELEASE_DOCS_STALE`,详情列出相对路径、语种、`refreshDigest` 和精确演练/写入参数数组。prepare 只检查、不写工作树。正式 prepare 前先运行只读演练;有变化时向用户展示文件/语种/版本/`refreshDigest`,只有在用户明确授权"本地发布文档写入"后,才执行带 `--write --confirm-refresh <refreshDigest> --ack-local-document-write` 三项绑定的写入,随后运行聚焦校验,要求维护者审阅并提交刷新结果,再重新 prepare。该授权不扩展为 hook、提交、push 或 publish 授权。
27
+
26
28
  ## 正向执行路径
27
29
 
28
30
  1. 使用插件根相对路径运行 CLI:`CLI="node ${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs"`
29
- 2. 运行 `${CLI} prepare --root <path> --offline --json`
30
- 3. 若遇到 hook/gate 授权门失败,分别展示命令和风险,获取授权后只增加实际需要的 `--acknowledge-hook-side-effects` / `--acknowledge-gate-side-effects`
31
- 4. 检查 exit code 0,读取 JSON 返回的 immutable `planPath=plans/<planDigest>.json`,再从该文件读取 `status`、`units`、`externalActions`
32
- 5. 向用户展示 targetVersion、externalActions、planDigest planPath;后续 approve/publish 只能使用该 immutable planPath,等待确认后再 approve
31
+ 2. 配置含 `releaseDocuments` 时,先运行只读演练 `${CLI} docs refresh --unit <id> --json`;`status: "changes"` 时展示逐文件路径/语种/版本/`refreshDigest`,取得"本地发布文档写入"明确授权后才执行 `nextCommand.argv` 写入,审阅并提交刷新结果后再继续;`status: "clean"` 时直接进入 prepare
32
+ 3. 运行 `${CLI} prepare --root <path> --offline --json`
33
+ 4. 若遇到 hook/gate 授权门失败,分别展示命令和风险,获取授权后只增加实际需要的 `--acknowledge-hook-side-effects` / `--acknowledge-gate-side-effects`
34
+ 5. 检查 exit code 0,读取 JSON 返回的 immutable `planPath=plans/<planDigest>.json`,再从该文件读取 `status`、`units`、`externalActions`
35
+ 6. 向用户展示 targetVersion、externalActions、planDigest 和 planPath;后续 approve/publish 只能使用该 immutable planPath,等待确认后再 approve
33
36
 
34
37
  若用户明确要求 GitHub+npm 生产发布,加入 `--production`。该模式还会封存独立
35
38
  Git commit/tree 和 npm tarball,并把路径、SHA/integrity、branch/tag 写入计划。
@@ -48,6 +51,11 @@ prepare 后若人工继续修改 README 或任何源文件,应保留修改并
48
51
  ## 确定性脚本调用
49
52
 
50
53
  ```bash
54
+ # 发布文档新鲜度:prepare 前只读演练(配置了 releaseDocuments 的单元)
55
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> --json
56
+ # 仅在用户明确授权“本地发布文档写入”后执行(三项绑定缺一不可)
57
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> \
58
+ --write --confirm-refresh <refreshDigest> --ack-local-document-write --json
51
59
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --offline --json
52
60
  # 生产 happy end:bound 基线必须 online;远端目标唯一性仍由 publish 全局预检
53
61
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --online --production --json
@@ -59,8 +67,9 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --offli
59
67
 
60
68
  ## 执行顺序
61
69
 
62
- 1. 校验配置 schema → 2. Hook 授权门 → 3. 运行 hooks → 4. 捕获 Git baseline →
63
- 5. unit 观察前序公开基线 6. 生成快照/扫描/README7. 版本解析 8. 原子写入 plan
70
+ 1. 校验配置 schema → 2. 版本解析与发布文档新鲜度门(只读,RELEASE_DOCS_STALE)→
71
+ 3. Hook 授权门4. 运行 hooks 并复检文档新鲜度 5. 捕获 Git baseline
72
+ 6. 逐 unit 观察前序公开基线 → 7. 生成快照/扫描/README → 8. 原子写入 plan
64
73
 
65
74
  ## 故障路由
66
75
 
@@ -71,6 +80,8 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --offli
71
80
  | GATE_FAILED (bound + offline) | 改用 `--online --production`,不得把 unobserved-offline plan 交给 publish |
72
81
  | GATE_FAILED (前序基线漂移) | 先取得并比较实际远端内容;人工选择 merge/adopt/reject。merge/adopt 都必须把接受内容落回 human-owned 权威源,并把 `previousPublicBaseline` 更新为接受状态的精确 repo/ref/commit 后重新 online production prepare;reject 停止调查,禁止改 `mode: none` 绕过 |
73
82
  | GATE_FAILED (其他) | 修复门失败原因后重试;以 CLI exit code 为准 |
83
+ | RELEASE_DOCS_STALE | 文档相对说明源已陈旧;按详情运行只读演练,展示文件/语种/版本/摘要,经用户授权“本地发布文档写入”后执行写入,审阅提交再重新 prepare |
84
+ | RELEASE_DOCS_INVALID / TRANSLATION_MISSING / CONFLICT / REFRESH_STALE | 修复配置/说明源/目标或重新演练取得新 `refreshDigest`;不得扩大写入范围绕过 |
74
85
  | SECRET_DETECTED | 移除密钥并更新 allowlist |
75
86
  | CONFIG_INVALID | 检查 version.source 和 package.json |
76
87
 
@@ -17,7 +17,9 @@ description: 从已批准且摘要确认的生产计划发布冻结 Git branch/t
17
17
  不得把沙箱通过描述成真实发布成功。
18
18
 
19
19
  只发布 `prepare --production` 封存的 Git object 和 npm tarball,不从活动工作区重新
20
- 打包,不生成或覆盖 README。远端 branch/tag/Release/npm version 已存在、查询不确定、
20
+ 打包,不生成或覆盖 README,也永不隐式刷新工作树中的发布文档;
21
+ 遇到 `RELEASE_DOCS_STALE` 或文档陈旧只能回到 `docs refresh` → 人工审阅 → 提交 →
22
+ 重新 prepare。远端 branch/tag/Release/npm version 已存在、查询不确定、
21
23
  认证失败或摘要漂移时,在全局预检阶段停止并交给人工。禁止覆盖、删除和自动回滚;
22
24
  新建 ref 的 create-only CAS(`--force-with-lease=<ref>:`)只断言目标不存在,不授权覆盖。
23
25
 
@@ -19,7 +19,7 @@ marketplace 隔离消费者 checkpoint,但只恢复到 `PUBLISHED`;最终 np
19
19
 
20
20
  ## 职责与边界
21
21
 
22
- 查询远端实际状态,对照冻结计划识别一致/不一致检查点。已成功的步骤幂等跳过,只重试安全且未完成的步骤。远端冲突时停止并要求人工决策。不删除远端资源。`--run` 必需;重试需 `--approval`。
22
+ 查询远端实际状态,对照冻结计划识别一致/不一致检查点。已成功的步骤幂等跳过,只重试安全且未完成的步骤。远端冲突时停止并要求人工决策。不删除远端资源。`--run` 必需;重试需 `--approval`。reconcile 永不隐式刷新工作树中的发布文档;陈旧文档只能回到 `docs refresh` → 人工审阅 → 提交 → 重新 prepare。
23
23
 
24
24
  **阶段通过规则**: 本阶段的通过只能由 CLI exit code 0 和结构化状态码 `PUBLISHED` 确认。随后必须以 reconcile 返回的新 `runPath` 执行 verify;只有 verify 的 `VERIFIED` 才是完整终态。
25
25
 
@@ -35,8 +35,24 @@ description: "Discoverable entry point for release-skill: dependency and environ
35
35
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" help --json
36
36
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" setup --root <path> --json
37
37
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" assess --root <path> --offline --json
38
+ # 发布文档刷新:默认只读演练
39
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> --json
40
+ # 摘要确认后的本地写入(三项绑定缺一不可)
41
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> \
42
+ --write --confirm-refresh <refreshDigest> --ack-local-document-write --json
38
43
  ```
39
44
 
45
+ ## 发布文档刷新(docs refresh)
46
+
47
+ 发布单元配置 `releaseDocuments` 后,一份结构化双语说明源可确定性刷新 README 受管区域、唯一版本标记的机器值和 CHANGELOG 当前版本受管条目。核心 CLI 不联网、不调用大模型、不自动翻译;只改写声明过的受管区域、版本标记机器值和当前受管条目,区域外字节逐字保留。`prepare` 只检查新鲜度,不写工作树。
48
+
49
+ - **配置**:`releaseDocuments.notesSource`(说明源路径,只允许 `{version}` 占位符与 `.yaml`/`.yml`/`.json` 后缀)、`locales`(如 `[en, zh-CN]`)、`changelogs`(path + locale)、`readmes`(path + locale + `regions` 受管区域 id + `versionMarkers` 版本标记模式)。版本标记模式必须与 README 现有唯一标记精确匹配,`{version}` 代表机器版本值,刷新只替换该值;零次或多次匹配失败关闭。
50
+ - **说明源**:`version` 必须与单元版本精确一致,`date` 为 `YYYY-MM-DD`,每个配置语种恰好出现一次且 `summary`、变更项非空,`security`/`breaking`/`added`/`changed`/`deprecated`/`removed`/`fixed` 至少一个类别含条目。YAML alias、重复键、未知字段和语种回退都失败关闭。
51
+ - **只读演练**:`docs refresh --unit <id> --json` 输出逐文件相对路径、locale、新旧摘要、`version`、`locales`、`inputDigest`、`refreshDigest` 和 `nextCommand.argv`;候选无变化时 `status: "clean"`。
52
+ - **确认写入**:必须同时提供 `--write`、精确 `--confirm-refresh <refreshDigest>` 和 `--ack-local-document-write`,全部目标作为一个事务提交;成功后立即复演必须为 `clean`。
53
+
54
+ **授权边界**:本地发布文档写入授权只覆盖声明的本地文档目标,不是 hook、Git 提交、push、publish 或安装的授权。写入后必须审阅、提交,再重新 prepare。
55
+
40
56
  ## 故障路由
41
57
 
42
58
  | 场景 | 处理 |
@@ -50,6 +66,11 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" assess --root <path> --offlin
50
66
  | 项目配置不存在 | 路由 `release-setup`,默认只读;不得直接生成或覆盖 README/配置 |
51
67
  | assess 失败 | 运行 `node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" assess --offline --json` 获取详情 |
52
68
  | 请求生产发布 | 已有公开版本先调用 `release-prepare --online --production` 观察 bound 基线;人工审阅后再路由 `release-publish` |
69
+ | RELEASE_DOCS_INVALID | 配置或说明源语义非法(重复键、alias、未知字段、版本漂移等);修正配置或说明源后重新演练 |
70
+ | RELEASE_DOCS_TRANSLATION_MISSING | 配置语种缺失或多余;补齐说明源语种,与 `releaseDocuments.locales` 完全一致,不得回退 |
71
+ | RELEASE_DOCS_CONFLICT | 目标含非受管同版本条目、受管标记损坏或人工冲突;人工修复目标并保留人工修改后重新演练 |
72
+ | RELEASE_DOCS_REFRESH_STALE | 确认绑定后候选已变化;重新演练取得新 `refreshDigest` 再确认写入 |
73
+ | RELEASE_DOCS_STALE | prepare 检测到文档未刷新;按 `docs refresh` → 审阅 → 提交 → 重新 prepare 恢复 |
53
74
 
54
75
  ## 后续引导
55
76
 
@@ -23,13 +23,16 @@ description: Freeze an immutable release plan with local configuration, document
23
23
 
24
24
  **不确定性停止**: 遇到无法确定的配置项或版本冲突时,Agent 必须停止并上报用户。
25
25
 
26
+ **发布文档新鲜度门**: 配置了 `releaseDocuments` 的单元在 hook 授权门前先执行同一只读规划器:`clean` 继续;`changes` 抛 `RELEASE_DOCS_STALE`,详情列出相对路径、语种、`refreshDigest` 和精确演练/写入参数数组。prepare 只检查、不写工作树。正式 prepare 前先运行只读演练;有变化时向用户展示文件/语种/版本/`refreshDigest`,只有在用户明确授权"本地发布文档写入"后,才执行带 `--write --confirm-refresh <refreshDigest> --ack-local-document-write` 三项绑定的写入,随后运行聚焦校验,要求维护者审阅并提交刷新结果,再重新 prepare。该授权不扩展为 hook、提交、push 或 publish 授权。
27
+
26
28
  ## 正向执行路径
27
29
 
28
30
  1. 使用插件根相对路径运行 CLI:`CLI="node ${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs"`
29
- 2. 运行 `${CLI} prepare --root <path> --offline --json`
30
- 3. 若遇到 hook/gate 授权门失败,分别展示命令和风险,获取授权后只增加实际需要的 `--acknowledge-hook-side-effects` / `--acknowledge-gate-side-effects`
31
- 4. 检查 exit code 0,读取 JSON 返回的 immutable `planPath=plans/<planDigest>.json`,再从该文件读取 `status`、`units`、`externalActions`
32
- 5. 向用户展示 targetVersion、externalActions、planDigest planPath;后续 approve/publish 只能使用该 immutable planPath,等待确认后再 approve
31
+ 2. 配置含 `releaseDocuments` 时,先运行只读演练 `${CLI} docs refresh --unit <id> --json`;`status: "changes"` 时展示逐文件路径/语种/版本/`refreshDigest`,取得"本地发布文档写入"明确授权后才执行 `nextCommand.argv` 写入,审阅并提交刷新结果后再继续;`status: "clean"` 时直接进入 prepare
32
+ 3. 运行 `${CLI} prepare --root <path> --offline --json`
33
+ 4. 若遇到 hook/gate 授权门失败,分别展示命令和风险,获取授权后只增加实际需要的 `--acknowledge-hook-side-effects` / `--acknowledge-gate-side-effects`
34
+ 5. 检查 exit code 0,读取 JSON 返回的 immutable `planPath=plans/<planDigest>.json`,再从该文件读取 `status`、`units`、`externalActions`
35
+ 6. 向用户展示 targetVersion、externalActions、planDigest 和 planPath;后续 approve/publish 只能使用该 immutable planPath,等待确认后再 approve
33
36
 
34
37
  若用户明确要求 GitHub+npm 生产发布,加入 `--production`。该模式还会封存独立
35
38
  Git commit/tree 和 npm tarball,并把路径、SHA/integrity、branch/tag 写入计划。
@@ -48,6 +51,11 @@ prepare 后若人工继续修改 README 或任何源文件,应保留修改并
48
51
  ## 确定性脚本调用
49
52
 
50
53
  ```bash
54
+ # 发布文档新鲜度:prepare 前只读演练(配置了 releaseDocuments 的单元)
55
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> --json
56
+ # 仅在用户明确授权“本地发布文档写入”后执行(三项绑定缺一不可)
57
+ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> \
58
+ --write --confirm-refresh <refreshDigest> --ack-local-document-write --json
51
59
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --offline --json
52
60
  # 生产 happy end:bound 基线必须 online;远端目标唯一性仍由 publish 全局预检
53
61
  node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --online --production --json
@@ -59,8 +67,9 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --offli
59
67
 
60
68
  ## 执行顺序
61
69
 
62
- 1. 校验配置 schema → 2. Hook 授权门 → 3. 运行 hooks → 4. 捕获 Git baseline →
63
- 5. unit 观察前序公开基线 6. 生成快照/扫描/README7. 版本解析 8. 原子写入 plan
70
+ 1. 校验配置 schema → 2. 版本解析与发布文档新鲜度门(只读,RELEASE_DOCS_STALE)→
71
+ 3. Hook 授权门4. 运行 hooks 并复检文档新鲜度 5. 捕获 Git baseline
72
+ 6. 逐 unit 观察前序公开基线 → 7. 生成快照/扫描/README → 8. 原子写入 plan
64
73
 
65
74
  ## 故障路由
66
75
 
@@ -71,6 +80,8 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" prepare --root <path> --offli
71
80
  | GATE_FAILED (bound + offline) | 改用 `--online --production`,不得把 unobserved-offline plan 交给 publish |
72
81
  | GATE_FAILED (前序基线漂移) | 先取得并比较实际远端内容;人工选择 merge/adopt/reject。merge/adopt 都必须把接受内容落回 human-owned 权威源,并把 `previousPublicBaseline` 更新为接受状态的精确 repo/ref/commit 后重新 online production prepare;reject 停止调查,禁止改 `mode: none` 绕过 |
73
82
  | GATE_FAILED (其他) | 修复门失败原因后重试;以 CLI exit code 为准 |
83
+ | RELEASE_DOCS_STALE | 文档相对说明源已陈旧;按详情运行只读演练,展示文件/语种/版本/摘要,经用户授权“本地发布文档写入”后执行写入,审阅提交再重新 prepare |
84
+ | RELEASE_DOCS_INVALID / TRANSLATION_MISSING / CONFLICT / REFRESH_STALE | 修复配置/说明源/目标或重新演练取得新 `refreshDigest`;不得扩大写入范围绕过 |
74
85
  | SECRET_DETECTED | 移除密钥并更新 allowlist |
75
86
  | CONFIG_INVALID | 检查 version.source 和 package.json |
76
87