release-skill 0.1.9 → 0.2.0

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 (59) 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/.kimi-plugin/plugin.json +1 -1
  5. package/CHANGELOG.md +53 -0
  6. package/INSTALL.md +4 -4
  7. package/INSTALL.zh-CN.md +4 -4
  8. package/README.md +18 -33
  9. package/README.zh-CN.md +17 -23
  10. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  11. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  12. package/adapters/claude/bin/release-skill.bundle.mjs +2740 -1844
  13. package/adapters/claude/schemas/.render-manifest.json +8 -8
  14. package/adapters/claude/schemas/approval-record.schema.json +1 -1
  15. package/adapters/claude/schemas/release-plan.schema.json +6 -2
  16. package/adapters/claude/schemas/release-project.schema.json +14 -0
  17. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  18. package/adapters/codex/bin/release-skill.bundle.mjs +2740 -1844
  19. package/adapters/codex/schemas/.render-manifest.json +8 -8
  20. package/adapters/codex/schemas/approval-record.schema.json +1 -1
  21. package/adapters/codex/schemas/release-plan.schema.json +6 -2
  22. package/adapters/codex/schemas/release-project.schema.json +14 -0
  23. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  24. package/adapters/kimi/bin/release-skill.bundle.mjs +2740 -1844
  25. package/adapters/kimi/schemas/.render-manifest.json +8 -8
  26. package/adapters/kimi/schemas/approval-record.schema.json +1 -1
  27. package/adapters/kimi/schemas/release-plan.schema.json +6 -2
  28. package/adapters/kimi/schemas/release-project.schema.json +14 -0
  29. package/bin/release-skill-cli.mjs +3 -0
  30. package/bin/release-skill.bundle.mjs +2740 -1844
  31. package/package.json +8 -2
  32. package/references/.render-manifest.json +8 -8
  33. package/references/01-state-machine.md +5 -5
  34. package/references/02-project-config.md +1 -1
  35. package/references/05-evidence-and-errors.md +1 -1
  36. package/references/06-adapter-contract.md +41 -1
  37. package/schemas/.render-manifest.json +8 -8
  38. package/schemas/approval-record.schema.json +1 -1
  39. package/schemas/release-plan.schema.json +6 -2
  40. package/schemas/release-project.schema.json +14 -0
  41. package/scripts/sync-public-files.mjs +462 -0
  42. package/src/adapters/contract.mjs +60 -0
  43. package/src/adapters/plugin-marketplace.mjs +289 -730
  44. package/src/commands/prepare.mjs +195 -182
  45. package/src/commands/publish.mjs +438 -122
  46. package/src/commands/reconcile.mjs +369 -191
  47. package/src/commands/verify.mjs +13 -2
  48. package/src/core/approval.mjs +72 -45
  49. package/src/core/baseline.mjs +16 -0
  50. package/src/core/checkpoints.mjs +143 -0
  51. package/src/core/evidence.mjs +30 -3
  52. package/src/core/hook-cache.mjs +254 -0
  53. package/src/core/hooks.mjs +37 -1
  54. package/src/core/observe-retry.mjs +223 -0
  55. package/src/core/plan.mjs +162 -253
  56. package/src/platforms/kimi.mjs +514 -0
  57. package/src/platforms/registry.mjs +393 -0
  58. package/src/producers/build-adapters.mjs +14 -22
  59. package/src/snapshot/frozen.mjs +29 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
@@ -37,6 +37,7 @@
37
37
  "native/",
38
38
  "!native/safe-write/build/",
39
39
  "scripts/build-bundle.mjs",
40
+ "scripts/sync-public-files.mjs",
40
41
  "README.md",
41
42
  "README.zh-CN.md",
42
43
  "CHANGELOG.md",
@@ -51,10 +52,15 @@
51
52
  "scripts": {
52
53
  "build": "node --check src/**/*.mjs && node scripts/build-bundle.mjs",
53
54
  "test": "node --test test/*.test.mjs",
54
- "test:no-dogfood": "node --test $(ls test/*.test.mjs | grep -v -E 'dogfood|artifacts-safe-fs-native-seam|setup-live-samples')",
55
+ "test:no-dogfood": "node --test $(ls test/*.test.mjs | grep -v -E 'dogfood|artifacts-safe-fs-native-seam|setup-live-samples|reference-configs')",
56
+ "test:cli-contract": "node --test test/cli-contract.test.mjs",
55
57
  "typecheck": "node --check src/**/*.mjs bin/*.mjs",
56
58
  "sync:skills": "node scripts/sync-skills.mjs",
57
59
  "sync:skills:check": "node scripts/sync-skills.mjs --check",
60
+ "sync:version": "node scripts/sync-version.mjs",
61
+ "sync:version:check": "node scripts/sync-version.mjs --check",
62
+ "sync:public-files": "node scripts/sync-public-files.mjs",
63
+ "sync:public-files:check": "node scripts/sync-public-files.mjs --check",
58
64
  "build:adapters": "node scripts/build-adapters.mjs",
59
65
  "build:adapters:check": "node scripts/build-adapters.mjs --check",
60
66
  "build:bundle": "node scripts/build-bundle.mjs",
@@ -6,12 +6,12 @@
6
6
  "bytes": 6335
7
7
  },
8
8
  "01-state-machine.md": {
9
- "digest": "4004aece5729059397ef5eeac554827afc16a468ae4c6180da12fdd29228ed95",
10
- "bytes": 7770
9
+ "digest": "f09a91b69cd7ba27bf5fe5d57deafb58aeb2edc3f69637020f573b06e190a0dd",
10
+ "bytes": 8840
11
11
  },
12
12
  "02-project-config.md": {
13
- "digest": "837f1643245f544011eddf336f31718fc05a1fe7dd4f181f627168a8ffcec183",
14
- "bytes": 16501
13
+ "digest": "1e3c10fb4598aa6406b9d7cb4fdab2f663c3dad017dce88098b9e79868796806",
14
+ "bytes": 16514
15
15
  },
16
16
  "03-readme-quality.md": {
17
17
  "digest": "d74e41052592443ebbd450d0ace5f9b9b19c7a94db50f952b17c1c5a4c5dc230",
@@ -22,12 +22,12 @@
22
22
  "bytes": 4682
23
23
  },
24
24
  "05-evidence-and-errors.md": {
25
- "digest": "244cd989dc86031e69fbdce8ec299359c3495c4f4dad4c903b119ef0e7a39569",
26
- "bytes": 8260
25
+ "digest": "00039b9bd3724f40ec084b366103f1b84a2b599c5e884f23904d337f4c193661",
26
+ "bytes": 8484
27
27
  },
28
28
  "06-adapter-contract.md": {
29
- "digest": "f727bfbdcd2bd6f95edc144a4fd61732ecaac2a49ffda296dbf6923f14a0c2f1",
30
- "bytes": 6890
29
+ "digest": "39f0271bc06eae38dade518ece8f7e16a18f53099a433e3ae07e9d20ff7a142e",
30
+ "bytes": 8716
31
31
  }
32
32
  }
33
33
  }
@@ -41,7 +41,7 @@
41
41
  | ASSESSED | PREPARED | `prepare` 所有门通过且计划冻结 |
42
42
  | ASSESSED | NEEDS_INPUT | 评估发现需要用户输入 |
43
43
  | ASSESSED | BLOCKED | 认证缺失或外部服务阻止 |
44
- | PREPARED | APPROVED | 人工批准记录创建,且计划 hash、tree hash、目标版本和远端冲突状态均未变化 |
44
+ | PREPARED | APPROVED | 人工批准记录创建,且计划 digest(v2:产物+动作+版本+配置绑定层)、目标版本均未变化;tree hash/workspace digest 漂移在 v2 下仅记录证据、不使批准失效(v1 计划仍按旧规则失效) |
45
45
  | PREPARED | NEEDS_INPUT | 计划需要补充信息 |
46
46
  | APPROVED | PUBLISHING | `publish` 读取未过期批准并开始执行 |
47
47
  | APPROVED | PREPARED | 批准过期或计划变更导致批准失效,回退到重新准备 |
@@ -63,7 +63,7 @@
63
63
  以下转换在条件满足时自动触发,无需人工干预:
64
64
 
65
65
  - APPROVED -> PREPARED:当批准记录的计划 hash 与当前冻结计划不匹配时,批准自动失效。
66
- - APPROVED -> PREPARED:当 plan digestGit tree hash、workspace digest、目标版本或已批准 action 列表变化时,批准自动失效。
66
+ - APPROVED -> PREPARED:当 plan digest、目标版本或已批准 action 列表变化时,批准自动失效。planVersion 2 计划的 digest 只绑定信任边界(冻结产物身份、动作列表、目标版本、配置摘要),工作区基线(Git tree hash、workspace digest)是记录层审计数据,不使批准失效;planVersion 1(旧版)计划维持旧规则,Git tree hash 与 workspace digest 变化同样使批准失效。
67
67
  - PUBLISHING -> PARTIAL:当任一检查点成功后下一检查点失败时,自动计算 PARTIAL 状态。
68
68
 
69
69
  ---
@@ -122,12 +122,12 @@
122
122
 
123
123
  批准记录绑定到发布计划 hash。以下任何变化均导致批准自动失效:
124
124
 
125
- - 发布计划内容变化(plan digest 不匹配)。
126
- - Git tree hash 变化。
127
- - 工作区 digest 变化。
125
+ - 发布计划绑定层内容变化(plan digest 不匹配)。planVersion 2 的 digest 只绑定信任边界:冻结产物身份(git commit/tree、manifestDigest、npm tarball 摘要)、外部动作列表(不含运行时 status)、目标版本、配置摘要、校验门。
128
126
  - 目标版本变化。
129
127
  - 已批准的 action 列表变化。
130
128
 
129
+ 工作区基线(Git tree hash、workspace digest、捕获时间)与生命周期状态(计划 status、createdAt、action 运行时 status)是**记录层审计数据**:继续写入计划与审批文件以备查,但不参与 planVersion 2 的 digest、不使批准失效。publish/reconcile 检测到基线漂移时记录 warning 级证据事件并继续执行,产物完整性由发布前的冻结产物重验兜底。planVersion 1(旧版)计划维持旧规则:Git tree hash 与 workspace digest 变化同样使批准失效,基线漂移硬失败(BASELINE_CHANGED)。
130
+
131
131
  远端冲突状态(如 tag 已存在、npm 版本冲突等)不在 approval 绑定字段中;它在 publish/reconcile 的 preflight 阶段阻断执行并要求人工决策。
132
132
 
133
133
  失效后系统回退到 PREPARED 状态,要求重新批准。
@@ -135,7 +135,7 @@ human-owned 权威源,然后重新 prepare、审阅并 approve;后续环节
135
135
  - 说明源的 `version` 必须与 `version.source` 解析结果精确一致,`date` 为 `YYYY-MM-DD`;每个配置语种恰好出现一次,`summary` 和变更项去除首尾空白后非空,`security`/`breaking`/`added`/`changed`/`deprecated`/`removed`/`fixed` 至少一个类别含条目。重复键、YAML alias/anchor、合并键、自定义标签、未知字段、缺少或多余语种、语种回退均失败关闭。
136
136
  - README 受管区域以 `<!-- release-skill:managed:start id=<region> -->` 与对应结束标记做字节偏移替换;版本标记必须唯一匹配且只替换机器值,零次或多次匹配拒绝写入。CHANGELOG 当前版本受管条目由含 `baseline` 摘要的注释包围,基线不符、非受管同版本条目或标记损坏返回冲突。
137
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,使旧批准失效。
138
+ - `prepare` 在 hook、基线、快照、远端检查和计划冻结前执行同一只读规划器:`clean` 继续,`changes` 以 `RELEASE_DOCS_STALE` 失败关闭并给出精确演练/写入参数。`prepare`、`publish`、`reconcile` 永不隐式刷新文档;刷新后重新 prepare 会自然改变快照与 plan digest(文档进入发布产物时),使旧批准失效。
139
139
 
140
140
  ---
141
141
 
@@ -11,7 +11,7 @@
11
11
  | 错误码 | 含义 | 触发场景 | 典型恢复 |
12
12
  |---|---|---|---|
13
13
  | `CONFIG_INVALID` | 配置文件格式或内容不合法 | YAML 语法错误、schema 校验失败、路径逃逸、hook 不合规 | 修正配置文件后重新运行 |
14
- | `BASELINE_CHANGED` | 发布计划冻结后 Git tree 发生变化 | 有文件被修改、新增或删除导致 tree hash 不匹配 | 重新运行 `prepare` 冻结新基线 |
14
+ | `BASELINE_CHANGED` | 发布计划冻结后 Git tree 发生变化(仅 planVersion 1 计划硬失败) | 有文件被修改、新增或删除导致 tree hash 不匹配。planVersion 2 计划的基线是记录层审计数据:漂移只记录 warning 级证据事件并继续执行,不抛此错误,产物完整性由发布前冻结产物重验兜底 | 重新运行 `prepare` 冻结新基线 |
15
15
  | `DIRTY_SCOPE_CONFLICT` | 工作目录存在未提交变更且可能影响发布范围 | dirty 文件与发布单元的源码路径重叠 | 提交或暂存变更后重试 |
16
16
  | `GATE_FAILED` | 发布门(构建、测试、lint、文档验证等)未通过 | hook 返回非零退出码 | 修复失败项后重新运行 `prepare` |
17
17
  | `AUTH_MISSING` | 缺少必要的认证凭据或权限 | GitHub token 未配置、npm 登录缺失、marketplace 凭据不足 | 配置凭据后重试 |
@@ -35,6 +35,30 @@
35
35
  - `MISSING`:远端资源尚未创建。
36
36
  - `CONFLICTING`:远端资源存在但与计划不匹配。
37
37
 
38
+ - **调用方退避策略(重要)**:`observe` 是只读查询,其契约状态只有
39
+ 上面三种(`CONSISTENT` / `MISSING` / `CONFLICTING`)。`PROPAGATING`
40
+ (“写入已发生但远端尚不可见”的暂态)**不是 adapter 契约状态**,
41
+ 而是调用方在 `observe` 之外的内部分类,由
42
+ `src/core/observe-retry.mjs` 的 `observeWithRetry` 统一实现:
43
+ - 当 `observe` 抛错、返回空观察、或显式缺失(`exists:false` 等)
44
+ 时,调用方视为“信息不足”,按固定退避策略(`maxAttempts:5`、
45
+ `delaysMs:[10_000,20_000,40_000,80_000]`,总窗口约 150s)重试只读
46
+ `observe`,以消化最终一致性传播延迟(如 `npm publish` 成功后
47
+ `npm view` 暂查不到新版本)。
48
+ - 一旦 `observe` 读到**具体且非空**的远端对象,立即停(无论是否匹配):
49
+ 匹配则 `CONSISTENT`,不匹配则 `CONFLICTING`——**绝不对
50
+ `CONFLICTING` 重试**,因为它已是权威冲突,必须失败关闭交人决策。
51
+ - 重试耗尽仍缺失,按现状语义判 `FAILED`/`UNCERTAIN`,终态不变。
52
+ - `preflight`(资源是否被占用)不存在传播暂态,保持单次调用,不接入
53
+ 退避。
54
+ - 退避策略常量集中在 `observe-retry.mjs`,不进冻结计划 schema、不做用户配置,
55
+ 因此改变退避参数不会使已批准发布的摘要失效。
56
+ - 测试逃生口:环境变量 `RELEASE_SKILL_OBSERVE_RETRY_NO_WAIT=1` 仅使
57
+ 重试间隔立即返回(跳过挂钟等待),供 spawn 真实 CLI 子进程的测试沙箱
58
+ 使用;尝试次数、顺序与 PROPAGATING/CONFLICTING 分类完全不变,
59
+ 因此它不可能弱化任何失败关闭决策。进程内测试应优先使用
60
+ `observeRetrySleep` 依赖注入而非该环境变量。
61
+
38
62
  ### 1.4 verify(action, context)
39
63
 
40
64
  - **职责**:对外部写操作的结果进行深度验证。
@@ -84,7 +108,23 @@
84
108
  | 查询插件状态 | observe | 检查插件是否可被发现 |
85
109
  | 验证安装性 | verify | 在全新环境中安装并验证插件可调用 |
86
110
 
87
- 支持目标:Claude Code plugin marketplace、Codex plugin manifest/marketplace
111
+ 支持目标:Claude Code plugin marketplace、Codex plugin manifest/marketplace、Kimi Code(人工 attestation 闭环)。
112
+
113
+ ### 2.4 安装载荷校验契约(declared-manifest-v1)
114
+
115
+ marketplace install action 的安装侧载荷校验以 action 参数 `payloadContract` 选择语义:
116
+
117
+ - **`payloadContract: 'declared-manifest-v1'`**(新计划由 prepare 写入):
118
+ - 权威方 = 冻结快照经摘要密封后、按 marketplace 清单声明的 `source` 子树过滤出的条目集合(子树解析路径与清单版本校验不变)。
119
+ - 安装侧 = 安装目录的完整文件遍历,**不做任何豁免**。
120
+ - 判定:**权威方每一条 entry 必须在安装侧存在**,且 `type/size/contentDigest/mode(忽略写位 `& ~0o222`)` 全部一致。缺失、内容篡改、非写位 mode 变化(如可执行位丢失)→ 失败关闭(CONFLICTING),错误信息列出具体冲突路径(前 10 条,超出记总数)。
121
+ - 安装侧多出的文件(宿主 CLI 副产物,如 claude `.in_use`、codex `.git` 检出与 `.codex-plugin/migrated-command-skills/`、以及任何未来新增物)**不视为失败**,收集为相对路径 `extraInstalledPaths`(上限 200 条,超出记录 `extraInstalledPathsTotal` 总数)写入 evidence/observation,供审计与宿主演进观察。
122
+ - 安全性质不降:供应链校验的对象是我方声明拥有的文件;宿主副产物不是我方责任,纳入全等比较只会把宿主行为风险转嫁为发布阻断。
123
+ - **缺失 `payloadContract` 字段**(存量已冻结计划):走 legacy 全等语义——安装侧整树(按 `consumerTransportExclusions` 豁免宿主传输元数据后)与权威方逐字节全等,任何差异失败关闭。行为逐字节保持,用于保护旧计划的恢复路径(reconcile/verify)。
124
+ - 未识别的 `payloadContract` 取值 → 失败关闭。
125
+ - `consumerTransportExclusions` 自本契约起 **deprecated**:仅 legacy 分支使用,待平台注册表任务(T2.2)迁入注册表后删除。
126
+
127
+ 校验强度红线:我方文件少一个、改一字节、非写位 mode 变化,都必须仍是 CONFLICTING。版本一致性另由 `plugin list`/install evidence 与已安装清单 name/version 校验分别把关,本契约不替代那些检查。
88
128
 
89
129
  ---
90
130
 
@@ -2,8 +2,8 @@
2
2
  "source": "schemas",
3
3
  "files": {
4
4
  "approval-record.schema.json": {
5
- "digest": "2ddac883435110e2c036cfadb00ce0c4d347a97d674b2c4ea9fcda3d72f81b8b",
6
- "bytes": 3135
5
+ "digest": "05b5a16d58372e4fdda625a472a4bb71439aa5db4e9813bf7f3d3cf2d39d144e",
6
+ "bytes": 3357
7
7
  },
8
8
  "artifact-lock.schema.json": {
9
9
  "digest": "6a5f4c826540b849571f173118bcbd778dfaceb4133034036ae565c3e9e353e5",
@@ -22,16 +22,16 @@
22
22
  "bytes": 2390
23
23
  },
24
24
  "release-plan.schema.json": {
25
- "digest": "69feff34b2fee517fd9ae89f576840c0abf331df9e9537f2798f62fab9a6720f",
26
- "bytes": 23091
25
+ "digest": "d7869cdb335618754eb787fe352c4c1775c799900f3bb926197c51359699a648",
26
+ "bytes": 24088
27
27
  },
28
28
  "release-project.schema.json": {
29
- "digest": "e9e3d090ee9c7a963e7346250328e30bf91a16a0962e5bc85deeab7c756c9e95",
30
- "bytes": 24329
29
+ "digest": "53efe202fc5e2bf63c8d846cf4d50648991163b270536c1450c4be44e45d4b93",
30
+ "bytes": 25761
31
31
  },
32
32
  "release-run.schema.json": {
33
- "digest": "74f087f8815f00997c6ad3bac033660b9b69de226a6f5c48a96194dba0fa872c",
34
- "bytes": 8917
33
+ "digest": "f453845fd2aab02c7b0812b1f62fd1ab7fa37104b8e752914906551c0e8c2637",
34
+ "bytes": 8959
35
35
  }
36
36
  }
37
37
  }
@@ -6,7 +6,6 @@
6
6
  "type": "object",
7
7
  "required": [
8
8
  "planDigest",
9
- "baseline",
10
9
  "approvedActions",
11
10
  "actor",
12
11
  "approvedAt",
@@ -25,6 +24,7 @@
25
24
  },
26
25
  "baseline": {
27
26
  "type": "object",
27
+ "description": "Workspace baseline captured at approval time. Optional record-layer audit data since planVersion 2 (validated structurally when present, never an approval binding); still required for v1 plans by validateApproval.",
28
28
  "required": ["gitTreeHash"],
29
29
  "additionalProperties": false,
30
30
  "properties": {
@@ -15,7 +15,11 @@
15
15
  "properties": {
16
16
  "planVersion": {
17
17
  "type": "integer",
18
- "const": 1
18
+ "enum": [
19
+ 1,
20
+ 2
21
+ ],
22
+ "description": "Plan format and digest-scoping version. 1: legacy (every field except the top-level digest binds; commitTimestamp must equal createdAt). 2: T1.2 digest layering (record-layer fields status/createdAt/baseline/per-action status are excluded from the plan digest; commitTimestamp is derived from the HEAD commit committer date)."
19
23
  },
20
24
  "status": {
21
25
  "type": "string",
@@ -242,7 +246,7 @@
242
246
  "commitTimestamp": {
243
247
  "type": "string",
244
248
  "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+00:00$",
245
- "description": "Plan freeze timestamp sampled exactly once during production prepare, in canonical UTC second precision. Written verbatim to GIT_AUTHOR_DATE and GIT_COMMITTER_DATE of the frozen release commit, so it always equals the commit's %aI and %cI byte-for-byte, and must equal the plan's createdAt."
249
+ "description": "Frozen release commit timestamp in canonical UTC second precision. Written verbatim to GIT_AUTHOR_DATE and GIT_COMMITTER_DATE of the frozen release commit, so it always equals the commit's %aI and %cI byte-for-byte. v1 plans: the freeze timestamp sampled exactly once during production prepare, equal to the plan's createdAt. planVersion 2 plans: derived deterministically from the baseline headCommit's committer date, so identical sources freeze identical commits (not equal to createdAt, which remains the record-layer real clock)."
246
250
  },
247
251
  "parentCommit": {
248
252
  "type": "string",
@@ -651,6 +651,20 @@
651
651
  "pattern": "^[A-Z_][A-Z0-9_]*$"
652
652
  },
653
653
  "uniqueItems": true
654
+ },
655
+ "cacheable": {
656
+ "type": "boolean",
657
+ "description": "Opt this hook into the incremental result cache: when its cacheInputs fingerprint is unchanged and the last run succeeded, prepare replays the cached outcome instead of re-executing. Absence means the hook always runs in full. cacheable=true requires a non-empty cacheInputs (enforced at runtime)."
658
+ },
659
+ "cacheInputs": {
660
+ "type": "array",
661
+ "minItems": 1,
662
+ "items": {
663
+ "type": "string",
664
+ "minLength": 1
665
+ },
666
+ "uniqueItems": true,
667
+ "description": "Root-relative globs declaring the hook's full input set (source, scripts, config, lockfiles). The cache key fingerprints the content of every matched file; a glob that matches nothing fails closed. Must cover ALL inputs or the cache can return a false hit."
654
668
  }
655
669
  }
656
670
  },