release-skill 0.1.3 → 0.1.5

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 (97) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +3 -3
  4. package/CHANGELOG.md +56 -0
  5. package/INSTALL.md +69 -32
  6. package/INSTALL.zh-CN.md +64 -26
  7. package/README.md +111 -37
  8. package/README.zh-CN.md +95 -28
  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 +79371 -0
  12. package/adapters/claude/bin/release-skill.mjs +34 -0
  13. package/adapters/claude/native/safe-write/binding.gyp +40 -0
  14. package/adapters/claude/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  15. package/adapters/claude/native/safe-write/prebuilds.json +24 -0
  16. package/adapters/claude/native/safe-write/src/safe_write.cc +2032 -0
  17. package/adapters/claude/schemas/.render-manifest.json +37 -0
  18. package/adapters/claude/schemas/approval-record.schema.json +115 -0
  19. package/adapters/claude/schemas/artifact-lock.schema.json +111 -0
  20. package/adapters/claude/schemas/artifact-plan.schema.json +52 -0
  21. package/adapters/claude/schemas/artifact-policy.schema.json +76 -0
  22. package/adapters/claude/schemas/evidence-event.schema.json +89 -0
  23. package/adapters/claude/schemas/release-plan.schema.json +860 -0
  24. package/adapters/claude/schemas/release-project.schema.json +759 -0
  25. package/adapters/claude/schemas/release-run.schema.json +342 -0
  26. package/adapters/claude/skills/release-assess/SKILL.md +5 -6
  27. package/adapters/claude/skills/release-help/SKILL.md +11 -18
  28. package/adapters/claude/skills/release-prepare/SKILL.md +6 -6
  29. package/adapters/claude/skills/release-publish/SKILL.md +1 -4
  30. package/adapters/claude/skills/release-reconcile/SKILL.md +5 -5
  31. package/adapters/claude/skills/release-setup/SKILL.md +64 -80
  32. package/adapters/claude/skills/release-verify/SKILL.md +4 -7
  33. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  34. package/adapters/codex/bin/release-skill.bundle.mjs +79371 -0
  35. package/adapters/codex/bin/release-skill.mjs +34 -0
  36. package/adapters/codex/native/safe-write/binding.gyp +40 -0
  37. package/adapters/codex/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
  38. package/adapters/codex/native/safe-write/prebuilds.json +24 -0
  39. package/adapters/codex/native/safe-write/src/safe_write.cc +2032 -0
  40. package/adapters/codex/schemas/.render-manifest.json +37 -0
  41. package/adapters/codex/schemas/approval-record.schema.json +115 -0
  42. package/adapters/codex/schemas/artifact-lock.schema.json +111 -0
  43. package/adapters/codex/schemas/artifact-plan.schema.json +52 -0
  44. package/adapters/codex/schemas/artifact-policy.schema.json +76 -0
  45. package/adapters/codex/schemas/evidence-event.schema.json +89 -0
  46. package/adapters/codex/schemas/release-plan.schema.json +860 -0
  47. package/adapters/codex/schemas/release-project.schema.json +759 -0
  48. package/adapters/codex/schemas/release-run.schema.json +342 -0
  49. package/adapters/codex/skills/release-assess/SKILL.md +12 -6
  50. package/adapters/codex/skills/release-help/SKILL.md +18 -18
  51. package/adapters/codex/skills/release-prepare/SKILL.md +13 -6
  52. package/adapters/codex/skills/release-publish/SKILL.md +8 -4
  53. package/adapters/codex/skills/release-reconcile/SKILL.md +12 -5
  54. package/adapters/codex/skills/release-setup/SKILL.md +71 -80
  55. package/adapters/codex/skills/release-verify/SKILL.md +11 -7
  56. package/bin/release-skill-cli.mjs +807 -0
  57. package/bin/release-skill.bundle.mjs +79371 -0
  58. package/bin/release-skill.mjs +23 -788
  59. package/package.json +6 -2
  60. package/references/02-project-config.md +2 -2
  61. package/schemas/release-plan.schema.json +7 -1
  62. package/schemas/release-project.schema.json +31 -0
  63. package/scripts/build-bundle.mjs +133 -0
  64. package/skills/release-assess/SKILL.md +5 -6
  65. package/skills/release-help/SKILL.md +11 -18
  66. package/skills/release-prepare/SKILL.md +6 -6
  67. package/skills/release-publish/SKILL.md +1 -4
  68. package/skills/release-reconcile/SKILL.md +5 -5
  69. package/skills/release-setup/SKILL.md +64 -80
  70. package/skills/release-verify/SKILL.md +4 -7
  71. package/skills-src/release-assess/SKILL.md +5 -6
  72. package/skills-src/release-help/SKILL.md +11 -18
  73. package/skills-src/release-prepare/SKILL.md +6 -6
  74. package/skills-src/release-publish/SKILL.md +1 -4
  75. package/skills-src/release-reconcile/SKILL.md +5 -5
  76. package/skills-src/release-setup/SKILL.md +64 -80
  77. package/skills-src/release-verify/SKILL.md +4 -7
  78. package/src/adapters/npm.mjs +5 -13
  79. package/src/adapters/plugin-marketplace.mjs +137 -34
  80. package/src/artifacts/policy.mjs +4 -7
  81. package/src/artifacts/safe-fs-backend-internal.mjs +69 -21
  82. package/src/commands/prepare.mjs +30 -1
  83. package/src/commands/publish.mjs +1 -0
  84. package/src/commands/reconcile.mjs +4 -1
  85. package/src/commands/setup.mjs +667 -28
  86. package/src/commands/verify.mjs +4 -1
  87. package/src/core/approval.mjs +4 -6
  88. package/src/core/config.mjs +8 -8
  89. package/src/core/pkg-root.mjs +22 -0
  90. package/src/core/plan.mjs +68 -5
  91. package/src/core/run.mjs +4 -4
  92. package/src/core/trusted-resource.mjs +96 -0
  93. package/src/docs/version-gate.mjs +164 -0
  94. package/src/producers/build-adapters.mjs +512 -55
  95. package/src/snapshot/frozen.mjs +133 -3
  96. package/src/snapshot/public-map.mjs +7 -4
  97. package/src/snapshot/scan.mjs +2 -1
@@ -9,7 +9,7 @@
9
9
  {
10
10
  "name": "release-skill",
11
11
  "source": "./",
12
- "version": "0.1.3",
12
+ "version": "0.1.5",
13
13
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification"
14
14
  }
15
15
  ]
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "release-skill contributors"
7
7
  },
8
8
  "license": "MIT",
9
- "skills": "./skills/"
9
+ "skills": "./adapters/claude/skills/"
10
10
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "release-skill contributors"
7
7
  },
8
8
  "license": "MIT",
9
- "skills": "./skills/",
9
+ "skills": "./adapters/codex/skills/",
10
10
  "interface": {
11
11
  "displayName": "Release Skill",
12
12
  "shortDescription": "Safe preparation and frozen GitHub/npm publishing",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "defaultPrompt": [
21
21
  "Assess this project for release readiness.",
22
- "Prepare a release plan for version 0.1.3.",
22
+ "Prepare a release plan for version 0.1.5.",
23
23
  "Help me understand the release workflow."
24
24
  ]
25
25
  }
package/CHANGELOG.md CHANGED
@@ -3,6 +3,62 @@
3
3
  All notable changes to the `release-skill` plugin will be documented in this
4
4
  file. The format is based on [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
+ ## [0.1.5] - 2026-07-21
7
+
8
+ ### Added
9
+
10
+ - **Explicit marketplace install timeout (`timeoutMs`)**: Claude and Codex
11
+ plugin marketplace distributions now accept an optional `timeoutMs` integer
12
+ field (range 30,000--900,000 ms; default 300,000 ms). The resolved value is
13
+ frozen into each marketplace install action's `parameters.timeoutMs` during
14
+ `prepare`, making it part of the plan digest, approval binding, and plan
15
+ integrity. The `plugin-marketplace` adapter's marketplace add, plugin install,
16
+ and plugin list commands all use the same frozen timeout, replacing the
17
+ previous hardcoded 30-second limit that caused `PARTIAL` failures on real
18
+ network installations requiring 40--105 seconds.
19
+ - **Old plan backward compatibility**: plans created before v0.1.5 that lack
20
+ `parameters.timeoutMs` on marketplace install actions default to 300,000 ms,
21
+ so existing `PARTIAL` runs can be reconciled without upgrading the plan.
22
+
23
+ ### Fixed
24
+
25
+ - **Marketplace consumer install timeout**: v0.1.4 production releases hit
26
+ `PARTIAL` because Claude Code and Codex plugin marketplace add commands
27
+ required 40--105 seconds on real networks, while the adapter hardcoded a
28
+ 30-second subprocess timeout. The timeout is now explicitly configurable per
29
+ distribution and verified through injected-executor tests. Invalid values
30
+ (non-integer, non-finite, out-of-range) fail closed rather than being
31
+ silently clamped.
32
+
33
+ ## [0.1.4] - 2026-07-19
34
+
35
+ ### Added
36
+
37
+ - **Docs version hard gate**: the English and Chinese README and INSTALL each
38
+ carry a machine-readable `release-skill:release-version` marker that must
39
+ equal the `package.json` version, and the CHANGELOG must carry a formal
40
+ heading for the current version. Any drift fails closed in
41
+ `pnpm test:release` before prepare. A release freezes only the current
42
+ truth: human docs are never auto-refreshed and must be updated, reviewed,
43
+ and approved first.
44
+ - **Auditable frozen commit timestamps**: production `prepare` samples the
45
+ plan freeze time once, validates it before any Git write, and binds it to
46
+ `GIT_AUTHOR_DATE`/`GIT_COMMITTER_DATE` and each unit's
47
+ `frozenSnapshot.commitTimestamp` (schema-required); plans missing the field
48
+ are rejected instead of silently rebuilt.
49
+
50
+ ### Fixed
51
+
52
+ - **Self-contained installed CLI smoke**: the v0.1.3 self-release selected
53
+ `help --json`, which correctly treats Git as a required environment
54
+ dependency, while npm smoke intentionally exposes only the Node runtime.
55
+ The CLI now supports `--version --json`, and self-release verification uses
56
+ that dependency-free entry to bind the installed CLI name and exact version
57
+ without widening the isolated process `PATH`.
58
+ - Added a real subprocess regression that runs the installed-style version
59
+ entry with the Node-only `PATH` and proves an unrelated injected path is not
60
+ inherited.
61
+
6
62
  ## [0.1.3] - 2026-07-19
7
63
 
8
64
  > `0.1.2` was an internal release candidate and was never published to npm or
package/INSTALL.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [简体中文](INSTALL.zh-CN.md)
4
4
 
5
+ <!-- release-skill:release-version: 0.1.5 -->
5
6
  ## Prerequisites
6
7
 
7
8
  - Node.js 22.0.0 or later
@@ -9,8 +10,9 @@
9
10
 
10
11
  ## Install from npm (recommended)
11
12
 
12
- v0.1.1 is published and verified. For a newer source candidate such as v0.1.3,
13
- use the npm path only after `npm view release-skill version` returns that exact
13
+ A public version is complete only after its immutable production plan has been
14
+ approved, published, and reached `VERIFIED`. For a newer source checkout, use
15
+ the npm path only after `npm view release-skill version` returns that exact
14
16
  version; before then, use the source checkout instructions below.
15
17
 
16
18
  ```bash
@@ -63,22 +65,71 @@ instructions above; do not mix the two entry paths in one run.
63
65
  "${CLI[@]}" help
64
66
  ```
65
67
 
66
- If `.release-skill/project.yaml` is absent, discover first-use facts and
67
- candidates without writing files:
68
+ If `.release-skill/project.yaml` is absent, keep the full read-only report in a
69
+ temporary file and inspect only its deterministic `compactSummary`:
68
70
 
69
71
  ```bash
70
- "${CLI[@]}" setup --root /path/to/your/project --json
72
+ PROJECT=/path/to/your/project
73
+ SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")"
74
+ REPORT="$SETUP_SESSION/discovery.json"
75
+ ANSWERS="$SETUP_SESSION/answers.json"
76
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
77
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
78
+
79
+ "${CLI[@]}" setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2
80
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary){console.error("compactSummary missing");process.exit(2)}process.stdout.write(JSON.stringify(r.compactSummary,null,2)+"\n")' "$REPORT"
71
81
  ```
72
82
 
73
- `NEEDS_INPUT` and `LOCAL_ONLY_DETECTED` intentionally return exit code 2. They
74
- are decision states, not an internal crash; automation should inspect the JSON
75
- `status`.
83
+ The summary is a review view, not authorization; `setupDigest` binds the full
84
+ facts, candidates, and answers. If `proposalConflicts` is non-empty, a human
85
+ must correct the conflicting repository/mapping authority and rerun setup.
86
+ Do not guess. With no conflicts, mechanically extract the proposal:
76
87
 
77
- Review release units, legacy `public-release.json` migration hints, tags,
78
- branch strategies, previous-public-baseline decisions, and gate candidates.
79
- Setup never executes a discovered script automatically. Provide a complete
80
- answers JSON, dry-run again to obtain the digest that binds current facts and
81
- answers, then create the configuration exactly once:
88
+ ```bash
89
+ SETUP_SESSION='/session-directory-absolute-path-printed-above'
90
+ PROJECT='/project-absolute-path-printed-above'
91
+ REPORT="$SETUP_SESSION/discovery.json"
92
+ ANSWERS="$SETUP_SESSION/answers.json"
93
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
94
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if((r.proposalConflicts??[]).length){console.error("proposal conflicts require human resolution");process.exit(2)}if(!r.recommendedAnswers){console.error("recommendedAnswers missing");process.exit(2)}fs.writeFileSync(process.argv[2],JSON.stringify(r.recommendedAnswers,null,2)+"\n",{flag:"wx",mode:0o600})' "$REPORT" "$ANSWERS"
95
+ "${CLI[@]}" setup --root "$PROJECT" --answers "$ANSWERS" --json > "$BOUND_REPORT"
96
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary||!r.setupDigest){console.error("bound setup report incomplete");process.exit(2)}process.stdout.write(JSON.stringify({compactSummary:r.compactSummary,setupDigest:r.setupDigest},null,2)+"\n")' "$BOUND_REPORT"
97
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
98
+ ```
99
+
100
+ After one explicit human confirmation of the bound summary and exact digest,
101
+ create once with that confirmed digest literal. The result must be
102
+ `CONFIG_CREATED`; a second setup must be `ALREADY_CONFIGURED`, then run assess.
103
+
104
+ ```bash
105
+ SETUP_SESSION=<session-directory-absolute-path-printed-above>
106
+ PROJECT=<project-absolute-path-printed-above>
107
+ ANSWERS="$SETUP_SESSION/answers.json"
108
+ CREATED_REPORT="$SETUP_SESSION/created.json"
109
+ POST_REPORT="$SETUP_SESSION/post-setup.json"
110
+ ASSESS_REPORT="$SETUP_SESSION/assess.json"
111
+ "${CLI[@]}" setup --root "$PROJECT" --answers "$ANSWERS" \
112
+ --write --confirm-setup <confirmed-setupDigest> --json > "$CREATED_REPORT"
113
+ "${CLI[@]}" setup --root "$PROJECT" --json > "$POST_REPORT"
114
+ set +e
115
+ "${CLI[@]}" assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT"
116
+ ASSESS_EXIT=$?
117
+ set -e
118
+ [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT"
119
+ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>JSON.parse(fs.readFileSync(x,"utf8")));if(c.status!=="CONFIG_CREATED"||p.status!=="ALREADY_CONFIGURED"||!["ASSESSED","NEEDS_INPUT","BLOCKED"].includes(a.status)){process.exit(2)}process.stdout.write(JSON.stringify({created:c.status,postSetup:p.status,assessment:{status:a.status,summary:a.summary,gapCount:(a.gaps??[]).length,blockingCodes:(a.gaps??[]).filter(g=>g.severity==="error").map(g=>g.code)}},null,2)+"\n")' "$CREATED_REPORT" "$POST_REPORT" "$ASSESS_REPORT"
120
+ node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
121
+ ```
122
+
123
+ Indirect interpreter/package-manager scripts are `SIDE_EFFECTS_UNPROVEN` and
124
+ remain unselected. Register a project-specific hook/gate only through a reviewed
125
+ incremental edit: edit `projectConfig.hooks`, or edit `verificationGates` and
126
+ add the same id to `selectedGateIds`, then rerun the bound dry-run. Human files use `mode: preserve`; explicit cross-unit shared
127
+ sources use `sourceScope: workspace`.
128
+
129
+ ### Advanced schema reference—not the first-run path
130
+
131
+ The following wrapper documents the schema. Do not hand-write it during normal
132
+ setup; use the mechanically extracted `recommendedAnswers`.
82
133
 
83
134
  ```json
84
135
  {
@@ -116,25 +167,11 @@ answers, then create the configuration exactly once:
116
167
  }
117
168
  ```
118
169
 
119
- The wrapper is complete, but its values are examples. Replace every repository,
120
- channel, baseline, and public-file decision with reviewed project facts; use
121
- `mode: none` only when no public version exists.
122
-
123
- When selecting a reported gate, add the complete gate definition to
124
- `projectConfig.verificationGates` and copy its id into `selectedGateIds`. The
125
- id must come from the current `gateCandidates`. A snapshot-gate command and all
126
- of its dependencies must be present in `publicFiles`; it cannot see tests,
127
- development dependencies, or `node_modules` that exist only in the parent
128
- workspace. The full [README setup section](README.md#first-use-discover-then-let-a-human-finalize)
129
- contains complete no-gate and one-gate answers examples.
130
-
131
- ```bash
132
- "${CLI[@]}" setup --root /path/to/your/project \
133
- --answers /path/to/setup-answers.json --json
134
- "${CLI[@]}" setup --root /path/to/your/project \
135
- --answers /path/to/setup-answers.json \
136
- --write --confirm-setup <setupDigest> --json
137
- ```
170
+ This wrapper is reference data only. Normal setup uses the machine proposal;
171
+ `mode: none` is valid only when no public version exists. When a reviewed gate
172
+ is added incrementally, its id must exactly match `selectedGateIds`, and a
173
+ snapshot gate plus its dependencies must be present in `publicFiles`. See the
174
+ [README setup section](README.md#first-use-deterministic-setup-without-loading-the-full-report).
138
175
 
139
176
  An existing config is never regenerated or overwritten. A project with no
140
177
  discoverable GitHub/npm channel reports `LOCAL_ONLY_DETECTED` rather than
package/INSTALL.zh-CN.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [English](INSTALL.md)
4
4
 
5
+ <!-- release-skill:release-version: 0.1.5 -->
5
6
  ## 前置条件
6
7
 
7
8
  - Node.js 22.0.0 或更高版本
@@ -9,9 +10,9 @@
9
10
 
10
11
  ## 从 npm 安装(推荐)
11
12
 
12
- v0.1.1 已公开发布并完成验证。对于 v0.1.3 这样的更新源码候选,只有当
13
- `npm view release-skill version` 返回该精确版本后才使用 npm 安装;在此之前请使用
14
- 下文的源码检出方式。
13
+ 公开版本只有在不可变生产计划经过批准、发布并达到 `VERIFIED` 后才算完整。对于更新的
14
+ 源码检出,只有当 `npm view release-skill version` 返回该精确版本后才使用 npm 安装;
15
+ 在此之前请使用下文的源码检出方式。
15
16
 
16
17
  ```bash
17
18
  npm install -g release-skill
@@ -61,18 +62,67 @@ CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
61
62
  "${CLI[@]}" help
62
63
  ```
63
64
 
64
- 如果项目尚无 `.release-skill/project.yaml`,先只读发现事实和候选:
65
+ 如果项目尚无 `.release-skill/project.yaml`,把完整只读报告写入临时文件,只查看其中
66
+ 确定性的 `compactSummary`(紧凑摘要):
65
67
 
66
68
  ```bash
67
- "${CLI[@]}" setup --root /path/to/your/project --json
69
+ PROJECT=/path/to/your/project
70
+ SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")"
71
+ REPORT="$SETUP_SESSION/discovery.json"
72
+ ANSWERS="$SETUP_SESSION/answers.json"
73
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
74
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
75
+
76
+ "${CLI[@]}" setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2
77
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary){console.error("compactSummary missing");process.exit(2)}process.stdout.write(JSON.stringify(r.compactSummary,null,2)+"\n")' "$REPORT"
68
78
  ```
69
79
 
70
- `NEEDS_INPUT` `LOCAL_ONLY_DETECTED` 按设计返回退出码 2。它们表示待人工决策,不是
71
- 内部崩溃;自动化应读取 JSON 的 `status`。
80
+ 紧凑摘要只是审阅视图,不是授权;`setupDigest` 仍绑定完整事实、候选和 answers。若
81
+ `proposalConflicts` 非空,必须由人工修正冲突的仓库/映射权威事实后重跑 setup,不能
82
+ 猜测选边。没有冲突时,机械提取机器提案:
72
83
 
73
- 人工审阅发布单元、旧 `public-release.json` 迁移提示、tag、分支策略、前序公开基线
74
- 和 gate 候选。setup 不会自动执行发现到的脚本。建立完整 answers JSON,再运行一次
75
- dry-run(只读试运行),取得同时绑定当前事实与人工答案的摘要,然后只创建一次配置:
84
+ ```bash
85
+ SETUP_SESSION='/上一步打印的会话目录绝对路径'
86
+ PROJECT='/上一步打印的项目绝对路径'
87
+ REPORT="$SETUP_SESSION/discovery.json"
88
+ ANSWERS="$SETUP_SESSION/answers.json"
89
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
90
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if((r.proposalConflicts??[]).length){console.error("proposal conflicts require human resolution");process.exit(2)}if(!r.recommendedAnswers){console.error("recommendedAnswers missing");process.exit(2)}fs.writeFileSync(process.argv[2],JSON.stringify(r.recommendedAnswers,null,2)+"\n",{flag:"wx",mode:0o600})' "$REPORT" "$ANSWERS"
91
+ "${CLI[@]}" setup --root "$PROJECT" --answers "$ANSWERS" --json > "$BOUND_REPORT"
92
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary||!r.setupDigest){console.error("bound setup report incomplete");process.exit(2)}process.stdout.write(JSON.stringify({compactSummary:r.compactSummary,setupDigest:r.setupDigest},null,2)+"\n")' "$BOUND_REPORT"
93
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
94
+ ```
95
+
96
+ 人工只确认一次绑定摘要和精确摘要值,然后使用已确认字面量首次创建。结果必须是
97
+ `CONFIG_CREATED`;第二次 setup 必须是 `ALREADY_CONFIGURED`,随后运行 assess。
98
+
99
+ ```bash
100
+ SETUP_SESSION=<上一步打印的会话目录绝对路径>
101
+ PROJECT=<上一步打印的项目绝对路径>
102
+ ANSWERS="$SETUP_SESSION/answers.json"
103
+ CREATED_REPORT="$SETUP_SESSION/created.json"
104
+ POST_REPORT="$SETUP_SESSION/post-setup.json"
105
+ ASSESS_REPORT="$SETUP_SESSION/assess.json"
106
+ "${CLI[@]}" setup --root "$PROJECT" --answers "$ANSWERS" \
107
+ --write --confirm-setup <已确认的 setupDigest> --json > "$CREATED_REPORT"
108
+ "${CLI[@]}" setup --root "$PROJECT" --json > "$POST_REPORT"
109
+ set +e
110
+ "${CLI[@]}" assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT"
111
+ ASSESS_EXIT=$?
112
+ set -e
113
+ [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT"
114
+ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>JSON.parse(fs.readFileSync(x,"utf8")));if(c.status!=="CONFIG_CREATED"||p.status!=="ALREADY_CONFIGURED"||!["ASSESSED","NEEDS_INPUT","BLOCKED"].includes(a.status)){process.exit(2)}process.stdout.write(JSON.stringify({created:c.status,postSetup:p.status,assessment:{status:a.status,summary:a.summary,gapCount:(a.gaps??[]).length,blockingCodes:(a.gaps??[]).filter(g=>g.severity==="error").map(g=>g.code)}},null,2)+"\n")' "$CREATED_REPORT" "$POST_REPORT" "$ASSESS_REPORT"
115
+ node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
116
+ ```
117
+
118
+ 解释器/包管理器间接脚本以 `SIDE_EFFECTS_UNPROVEN` 排除且默认不选。项目特有 hook/gate
119
+ 只能经人工审阅后增量注册:hook 编辑 `projectConfig.hooks`;gate 编辑
120
+ `verificationGates` 并将同一 id 加入 `selectedGateIds`,然后重跑绑定 dry-run。人工文件使用 `mode: preserve`;显式跨单元共享源使用
121
+ `sourceScope: workspace`。
122
+
123
+ ### 进阶 schema 参考——不是首次运行主路径
124
+
125
+ 下面的外壳只说明 schema。正常 setup 不应手写,而应机械提取 `recommendedAnswers`。
76
126
 
77
127
  ```json
78
128
  {
@@ -110,22 +160,10 @@ dry-run(只读试运行),取得同时绑定当前事实与人工答案的
110
160
  }
111
161
  ```
112
162
 
113
- 这个外壳完整,但其中的值只是示例。仓库、渠道、基线与公开文件都必须替换为本项目
114
- 经审阅的事实;只有确认不存在历史公开版本时才可使用 `mode: none`。
115
-
116
- 选择已报告的 gate 时,把完整定义加入 `projectConfig.verificationGates`,并把同一 id
117
- 复制进 `selectedGateIds`。id 必须来自当前 `gateCandidates`。snapshot gate 命令及
118
- 全部依赖必须包含在 `publicFiles`;它看不到仅存在于父工作空间的测试、开发依赖或
119
- `node_modules`。完整的无 gate 与单 gate answers 示例见
120
- [README 首次接入章节](README.zh-CN.md#首次接入先发现再由人工定稿)。
121
-
122
- ```bash
123
- "${CLI[@]}" setup --root /path/to/your/project \
124
- --answers /path/to/setup-answers.json --json
125
- "${CLI[@]}" setup --root /path/to/your/project \
126
- --answers /path/to/setup-answers.json \
127
- --write --confirm-setup <setupDigest> --json
128
- ```
163
+ 这个外壳仅供参考。正常 setup 必须使用机器提案;只有确认不存在历史公开版本时才可
164
+ 使用 `mode: none`。人工增量加入 gate 时,其 id 必须与 `selectedGateIds` 精确一致,
165
+ snapshot gate 及其依赖必须存在于 `publicFiles`。详见
166
+ [README 首次接入章节](README.zh-CN.md#首次接入不加载完整报告的确定性流程)。
129
167
 
130
168
  已有配置永远不会被重新生成或覆盖。无法发现 GitHub/npm 渠道的项目返回
131
169
  `LOCAL_ONLY_DETECTED`,不会冒充生产就绪。
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [简体中文](README.zh-CN.md) · Installation: [English](INSTALL.md) / [简体中文](INSTALL.zh-CN.md)
4
4
 
5
+ <!-- release-skill:release-version: 0.1.5 -->
5
6
  Release preparation for Claude Code and Codex, with human-edited files kept intact.
6
7
 
7
8
  release-skill helps a maintainer answer three questions: what will be released,
@@ -10,21 +11,25 @@ reviewed artifacts first and publishes those same artifacts later; it does not
10
11
  regenerate a README or re-pack the live workspace at the last step.
11
12
 
12
13
  <!-- release-skill:capability:external-write-boundary -->
13
- > **Current boundary:** v0.1.1 completed a real production release to GitHub and
14
- > npm, followed by exact npm installation and Claude/Codex consumer installation
15
- > verification from the frozen Git ref. The same workflow also has a local
16
- > production-equivalent protocol suite using the real release-skill CLI and
17
- > frozen artifacts, local bare Git remotes, and protocol fakes for `gh`, `npm`,
18
- > Claude, and Codex. The suite does not provide OS-level network isolation, and
19
- > it does not prove that another project's credentials, permissions, rate limits,
20
- > or eventual-consistency behavior will match this release. Treat each project's
21
- > first production run as a monitored canary. `prepare --online` observes bound
22
- > previous-public baselines and fails closed on drift; remote uniqueness checks
23
- > run during publish global preflight.
14
+ > **Current boundary:** v0.1.5 is the current release. v0.1.1 completed a real production release
15
+ > to GitHub and npm the first production-verified milestone followed by
16
+ > exact npm installation and Claude/Codex consumer installation verification
17
+ > from the frozen Git ref; "current release" and "first production-verified
18
+ > milestone" are two distinct facts and must not be conflated. The same
19
+ > workflow also has a local production-equivalent protocol suite using the
20
+ > real release-skill CLI and frozen artifacts, local bare Git remotes, and
21
+ > protocol fakes for `gh`, `npm`, Claude, and Codex. The suite does not
22
+ > provide OS-level network isolation, and it does not prove that another
23
+ > project's credentials, permissions, rate limits, or eventual-consistency
24
+ > behavior will match this release. Treat each project's first production run
25
+ > as a monitored canary. `prepare --online` observes bound previous-public
26
+ > baselines and fails closed on drift; remote uniqueness checks run during
27
+ > publish global preflight.
24
28
 
25
29
  <!-- release-skill:capability:safe-first-command -->
26
- > **v0.1.1 production verified.** The npm-installed CLI is the supported user
27
- > entry. Source checkout is the development/contributor fallback.
30
+ > **Production path verified since the v0.1.1 milestone; v0.1.5 is the current
31
+ > release.** The npm-installed CLI is the supported user entry. Source checkout
32
+ > is the development/contributor fallback.
28
33
  >
29
34
  > **Start here:**
30
35
  > - npm install: `npm install -g release-skill` → `release-skill help`
@@ -55,6 +60,13 @@ slogans, examples, prose, formatting, and later human edits.
55
60
  intervention; the tool does not force or overwrite them.
56
61
  - Only files listed in `publicFiles` are copied. Add translated READMEs, images,
57
62
  demos, and linked documents explicitly when they belong in the release.
63
+ - A release freezes only the current truth: `prepare` never refreshes or
64
+ rewrites human docs. Maintainers update README, INSTALL, and CHANGELOG first
65
+ — including the machine-readable `release-skill:release-version` markers,
66
+ which must equal the `package.json` version, and the formal CHANGELOG
67
+ heading for the current version — then prepare, review, and approve. A
68
+ pre-release gate fails closed when any doc version marker or the CHANGELOG
69
+ current-version entry drifts.
58
70
 
59
71
  This is the preservation contract: **copy current truth, freeze reviewed
60
72
  truth, and never rewrite human truth.**
@@ -104,26 +116,81 @@ First keep local plans, approvals, and frozen artifacts out of Git:
104
116
  !.release-skill/project.yaml
105
117
  ```
106
118
 
107
- ### First use: discover, then let a human finalize
119
+ ### First use: deterministic setup without loading the full report
108
120
 
109
- When the project has no configuration, start with read-only setup. It scans
110
- packages, plugin manifests, Git remotes, legacy `public-release.json`, and
111
- quality scripts. It reports release-unit, tag, branch, previous-baseline, and
112
- verification-gate candidates, but never treats README prose as instructions or
113
- automatically selects or executes a script:
121
+ Setup is read-only by default. Keep its potentially large report in temporary
122
+ files; show the user or Agent only the deterministic `compactSummary` review
123
+ view. The summary does not replace authorization: `setupDigest` still binds the
124
+ complete facts, candidates, and answers.
114
125
 
115
126
  ```bash
116
- release-skill setup --root /absolute/path/to/my-project --json
127
+ PROJECT=/absolute/path/to/my-project
128
+ SETUP_SESSION="$(mktemp -d "${TMPDIR:-/tmp}/release-setup.XXXXXX")"
129
+ REPORT="$SETUP_SESSION/discovery.json"
130
+ ANSWERS="$SETUP_SESSION/answers.json"
131
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
132
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
133
+
134
+ release-skill setup --root "$PROJECT" --json > "$REPORT" || test "$?" -eq 2
135
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary){console.error("compactSummary missing");process.exit(2)}process.stdout.write(JSON.stringify(r.compactSummary,null,2)+"\n")' "$REPORT"
136
+ ```
137
+
138
+ `NEEDS_INPUT` and `LOCAL_ONLY_DETECTED` intentionally exit with code 2. If
139
+ `proposalConflicts` is non-empty—including `PUBLIC_REPO_AUTHORITY_CONFLICT` or
140
+ a public-file mapping conflict—stop and let a human correct the conflicting
141
+ repository or mapping authority, then rerun setup. Do not guess a winner.
142
+
143
+ With no conflicts, copy the machine proposal mechanically. The Agent must not
144
+ rewrite or transcribe it:
145
+
146
+ ```bash
147
+ SETUP_SESSION='/session-directory-absolute-path-printed-above'
148
+ PROJECT='/project-absolute-path-printed-above'
149
+ REPORT="$SETUP_SESSION/discovery.json"
150
+ ANSWERS="$SETUP_SESSION/answers.json"
151
+ BOUND_REPORT="$SETUP_SESSION/bound.json"
152
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if((r.proposalConflicts??[]).length){console.error("proposal conflicts require human resolution");process.exit(2)}if(!r.recommendedAnswers){console.error("recommendedAnswers missing");process.exit(2)}fs.writeFileSync(process.argv[2],JSON.stringify(r.recommendedAnswers,null,2)+"\n",{flag:"wx",mode:0o600})' "$REPORT" "$ANSWERS"
153
+
154
+ release-skill setup --root "$PROJECT" --answers "$ANSWERS" --json > "$BOUND_REPORT"
155
+ node -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(!r.compactSummary||!r.setupDigest){console.error("bound setup report incomplete");process.exit(2)}process.stdout.write(JSON.stringify({compactSummary:r.compactSummary,setupDigest:r.setupDigest},null,2)+"\n")' "$BOUND_REPORT"
156
+ printf 'SETUP_SESSION=%s\nPROJECT=%s\n' "$SETUP_SESSION" "$PROJECT"
117
157
  ```
118
158
 
119
- `NEEDS_INPUT` and `LOCAL_ONLY_DETECTED` intentionally exit with code 2. They
120
- are unresolved decision states, not a crash. Automation should inspect the JSON
121
- `status` instead of treating every nonzero setup result as an internal failure.
159
+ Review that bound summary and exact digest once. After explicit human
160
+ confirmation, use the confirmed digest literal to create the configuration:
122
161
 
123
- After review, create an answers JSON containing the complete `projectConfig`.
124
- Its `selectedGateIds` must exactly match `projectConfig.verificationGates[].id`;
125
- use an explicit empty array when no project-specific gate is selected. Dry-run
126
- again with those answers, review the new `setupDigest`, then create the config:
162
+ ```bash
163
+ SETUP_SESSION=<session-directory-absolute-path-printed-above>
164
+ PROJECT=<project-absolute-path-printed-above>
165
+ ANSWERS="$SETUP_SESSION/answers.json"
166
+ CREATED_REPORT="$SETUP_SESSION/created.json"
167
+ POST_REPORT="$SETUP_SESSION/post-setup.json"
168
+ ASSESS_REPORT="$SETUP_SESSION/assess.json"
169
+ release-skill setup --root "$PROJECT" --answers "$ANSWERS" \
170
+ --write --confirm-setup <confirmed-setupDigest> --json > "$CREATED_REPORT"
171
+ release-skill setup --root "$PROJECT" --json > "$POST_REPORT"
172
+ set +e
173
+ release-skill assess --root "$PROJECT" --offline --json > "$ASSESS_REPORT"
174
+ ASSESS_EXIT=$?
175
+ set -e
176
+ [ "$ASSESS_EXIT" -eq 0 ] || [ "$ASSESS_EXIT" -eq 1 ] || exit "$ASSESS_EXIT"
177
+ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>JSON.parse(fs.readFileSync(x,"utf8")));if(c.status!=="CONFIG_CREATED"||p.status!=="ALREADY_CONFIGURED"||!["ASSESSED","NEEDS_INPUT","BLOCKED"].includes(a.status)){process.exit(2)}process.stdout.write(JSON.stringify({created:c.status,postSetup:p.status,assessment:{status:a.status,summary:a.summary,gapCount:(a.gaps??[]).length,blockingCodes:(a.gaps??[]).filter(g=>g.severity==="error").map(g=>g.code)}},null,2)+"\n")' "$CREATED_REPORT" "$POST_REPORT" "$ASSESS_REPORT"
178
+ node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
179
+ ```
180
+
181
+ The write must return `CONFIG_CREATED`; the next setup must return
182
+ `ALREADY_CONFIGURED`. Existing configuration is never regenerated—make only
183
+ reviewed incremental edits. Discovered interpreter/package-manager scripts are
184
+ `SIDE_EFFECTS_UNPROVEN` and are not selected automatically. Add a project-specific
185
+ hook or gate only after human review: edit `projectConfig.hooks`, or edit
186
+ `verificationGates` and add the same id to `selectedGateIds`, then rerun the
187
+ bound dry-run. Keep human files at `mode: preserve`, and
188
+ use `sourceScope: workspace` only for explicit cross-unit shared sources.
189
+
190
+ #### Advanced schema reference—not the first-use path
191
+
192
+ The wrapper below illustrates the schema only. Do not hand-write it during the
193
+ normal setup path; mechanically extract `recommendedAnswers` as shown above.
127
194
 
128
195
  ```json
129
196
  {
@@ -161,15 +228,12 @@ again with those answers, review the new `setupDigest`, then create the config:
161
228
  }
162
229
  ```
163
230
 
164
- This is a complete schema-shaped wrapper, not authoritative project data.
165
- Replace every repository, channel, baseline, and public-file decision with the
166
- reviewed facts from your project; use `mode: none` only when no public version
167
- exists.
231
+ This is a schema reference, not an onboarding template. Normal setup must use
232
+ the machine proposal. `mode: none` is valid only when no public version exists.
168
233
 
169
- To select one reported gate, keep the same complete `projectConfig`, add a
170
- top-level `verificationGates` array inside it, and make the wrapper's
171
- `selectedGateIds` identical. For example, when dry-run reported
172
- `my-project-script-test`:
234
+ The following reference shows the exact relationship between a manually
235
+ reviewed gate and `selectedGateIds`. Apply that relationship only as an
236
+ incremental edit to the extracted machine proposal:
173
237
 
174
238
  ```json
175
239
  {
@@ -407,8 +471,8 @@ publication. The source checkout remains the development/contributor fallback.
407
471
  ```bash
408
472
  "${CLI[@]}" setup --root "$PROJECT" --json
409
473
  ```
410
- Complete the answers and exact `setupDigest` confirmation described above;
411
- skip this step when configuration already exists.
474
+ Follow the mechanical `compactSummary` and `recommendedAnswers` path above;
475
+ confirm the bound `setupDigest` once, and skip this step when configuration exists.
412
476
  3. **Readiness assessment (read-only):**
413
477
  ```bash
414
478
  "${CLI[@]}" assess --root "$PROJECT" --offline --json
@@ -562,10 +626,12 @@ releaseUnits:
562
626
  plugin: my-plugin
563
627
  marketplace: my-plugin
564
628
  entrySkill: my-plugin-help
629
+ timeoutMs: 300000 # optional; range 30000-900000; default 300000
565
630
  - type: codex-plugin
566
631
  plugin: my-plugin
567
632
  marketplace: my-plugin
568
633
  entrySkill: my-plugin-help
634
+ timeoutMs: 300000 # optional; range 30000-900000; default 300000
569
635
  publicFiles:
570
636
  - from: packages/plugin/.claude-plugin/plugin.json
571
637
  to: .claude-plugin/plugin.json
@@ -612,6 +678,14 @@ the entry Skill, and all required public files. A CLI smoke (`smokeBin`) is
612
678
  optional for plugin units and only applies when the published npm package
613
679
  exposes a CLI binary.
614
680
 
681
+ Plugin distributions may declare `timeoutMs` (range 30,000--900,000 ms; default
682
+ 300,000 ms). This sets the subprocess timeout for the marketplace add, plugin
683
+ install, and plugin list commands. On real networks these commands can take
684
+ 40--105 seconds; the default 300-second timeout avoids false `PARTIAL` failures.
685
+ The resolved value is frozen into the plan and approved along with all other
686
+ action parameters. Old plans without `timeoutMs` default to 300,000 ms at
687
+ execution time for backward compatibility.
688
+
615
689
  ### PARTIAL recovery and reconcile
616
690
 
617
691
  When `publish` succeeds at some checkpoints but fails at others, the run enters