@hunter-harness/workflow-harness 0.2.67 → 0.2.69

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 (146) hide show
  1. package/harness/bundles/general/claude-code/.harness-build.json +1 -1
  2. package/harness/bundles/general/claude-code/contracts/workflow-policy.json +401 -88
  3. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
  4. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  5. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  6. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  7. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +2 -2
  8. package/harness/bundles/general/claude-code/harness-plan/reference.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-pull/SKILL.md +73 -0
  10. package/harness/bundles/general/claude-code/harness-push/SKILL.md +73 -0
  11. package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
  12. package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
  13. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  14. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
  16. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +119 -0
  17. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  18. package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +401 -88
  19. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  20. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  21. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  22. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  23. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +2 -2
  24. package/harness/bundles/general/codebuddy/harness-plan/reference.md +1 -1
  25. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +55 -0
  26. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +55 -0
  27. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  28. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
  29. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  30. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  31. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
  32. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +119 -0
  33. package/harness/bundles/general/codex/.harness-build.json +1 -1
  34. package/harness/bundles/general/codex/contracts/workflow-policy.json +401 -88
  35. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  36. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  37. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  38. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  39. package/harness/bundles/general/codex/harness-plan/SKILL.md +2 -2
  40. package/harness/bundles/general/codex/harness-plan/reference.md +1 -1
  41. package/harness/bundles/general/codex/harness-pull/SKILL.md +55 -0
  42. package/harness/bundles/general/codex/harness-push/SKILL.md +55 -0
  43. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  44. package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
  45. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  46. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  47. package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
  48. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +119 -0
  49. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  50. package/harness/bundles/general/cursor/contracts/workflow-policy.json +401 -88
  51. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  52. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  53. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  54. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  55. package/harness/bundles/general/cursor/harness-plan/SKILL.md +2 -2
  56. package/harness/bundles/general/cursor/harness-plan/reference.md +1 -1
  57. package/harness/bundles/general/cursor/harness-pull/SKILL.md +55 -0
  58. package/harness/bundles/general/cursor/harness-push/SKILL.md +55 -0
  59. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  60. package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
  61. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  62. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  63. package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
  64. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +119 -0
  65. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  66. package/harness/bundles/java/claude-code/contracts/workflow-policy.json +401 -88
  67. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  68. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  69. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  70. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  71. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  72. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  73. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +2 -2
  74. package/harness/bundles/java/claude-code/harness-plan/reference.md +1 -1
  75. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +73 -0
  76. package/harness/bundles/java/claude-code/harness-push/SKILL.md +73 -0
  77. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  78. package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
  79. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  80. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  81. package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
  82. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +119 -0
  83. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  84. package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +401 -88
  85. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  86. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  87. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  88. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  89. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  90. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  91. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +2 -2
  92. package/harness/bundles/java/codebuddy/harness-plan/reference.md +1 -1
  93. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +55 -0
  94. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +55 -0
  95. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  96. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
  97. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  98. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  99. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
  100. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +119 -0
  101. package/harness/bundles/java/codex/.harness-build.json +1 -1
  102. package/harness/bundles/java/codex/contracts/workflow-policy.json +401 -88
  103. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  104. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  105. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  106. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  107. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  108. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  109. package/harness/bundles/java/codex/harness-plan/SKILL.md +2 -2
  110. package/harness/bundles/java/codex/harness-plan/reference.md +1 -1
  111. package/harness/bundles/java/codex/harness-pull/SKILL.md +55 -0
  112. package/harness/bundles/java/codex/harness-push/SKILL.md +55 -0
  113. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  114. package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
  115. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  116. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  117. package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
  118. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +119 -0
  119. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  120. package/harness/bundles/java/cursor/contracts/workflow-policy.json +401 -88
  121. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  122. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  123. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  124. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  125. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  126. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  127. package/harness/bundles/java/cursor/harness-plan/SKILL.md +2 -2
  128. package/harness/bundles/java/cursor/harness-plan/reference.md +1 -1
  129. package/harness/bundles/java/cursor/harness-pull/SKILL.md +55 -0
  130. package/harness/bundles/java/cursor/harness-push/SKILL.md +55 -0
  131. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  132. package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
  133. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  134. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  135. package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
  136. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +119 -0
  137. package/harness/manifests/general/claude-code.json +27 -17
  138. package/harness/manifests/general/codebuddy.json +27 -17
  139. package/harness/manifests/general/codex.json +27 -17
  140. package/harness/manifests/general/cursor.json +27 -17
  141. package/harness/manifests/java/claude-code.json +29 -19
  142. package/harness/manifests/java/codebuddy.json +29 -19
  143. package/harness/manifests/java/codex.json +29 -19
  144. package/harness/manifests/java/cursor.json +29 -19
  145. package/hunter-workflow-family.json +11 -7
  146. package/package.json +1 -1
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: harness-pull
3
+ description: 从 Hunter Platform 下拉配置/规则/架构/指令(及显式来源分支的分支文件恢复)。仅当用户显式调用
4
+ /harness-pull 或明确说'从平台拉取/恢复'时使用;不得自动触发。
5
+ ---
6
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
7
+ # harness-pull — 从 Hunter Platform 下拉与恢复
8
+
9
+ ## Purpose
10
+
11
+ 从 Hunter Platform 下拉远端配置、规则、架构和指令,或按显式来源分支恢复分支受管文件。本 skill 只负责理解意图、展示预览和收集确认;冲突判断、哈希校验与本地事务由 CLI/core 完成。
12
+
13
+ ## When to Use
14
+
15
+ 仅当用户显式调用 `/harness-pull`(或明确说"从平台拉取/恢复")时执行。日常刷新元数据请走 `harness-sync`,不是本 skill。
16
+
17
+ ## 前置条件
18
+
19
+ - 远端同步已配置:`HUNTER_REMOTE_SYNC_URL`、`HUNTER_REMOTE_SYNC_TOKEN`、`HUNTER_REMOTE_SYNC_ACTOR_ID`。缺失时 CLI 固定 fail closed(`PUSH_PULL_CLI_UNAVAILABLE`)。
20
+ - 分支文件恢复必须显式 `--scope branch_files --branch <来源分支>`;归档不出现在常规下拉范围(单个归档 ZIP 经 Platform 变更记录单独下载)。
21
+
22
+ ## 交互流程
23
+
24
+ 1. **选择范围**:常规范围 `config` / `rules` / `architecture` / `instructions`;分支恢复单独显式声明来源分支。
25
+ 2. **展示预览**:来源版本、提交、缺失文件与冲突;默认只读预览,未经确认不写入本地。
26
+ 3. **冲突决策**:本地有修改时逐项展示差异与决策(`keep-local` / `accept-remote` / `skip`);不得因关闭扫描直接覆盖本地有意修改。删除恢复只允许 `accept-remote` 并绑定 preview hash 与来源。
27
+ 4. **报告结果**:应用、跳过、冲突遗留分列;冲突未决项保持本地原样。
28
+
29
+ ## 命令
30
+
31
+ ```powershell
32
+ npx hunter-harness harness-pull --scope config,rules --json
33
+ npx hunter-harness harness-pull --scope branch_files --branch main --json
34
+ ```
35
+
36
+ ## 关键规则
37
+
38
+ | 规则 | 要点 |
39
+ |------|------|
40
+ | 只读默认 | 未确认的预览不得写本地;`--dry-run` 不产生任何写入 |
41
+ | 本地修改保护 | 有本地修改时必须显示差异与决策,不静默覆盖 |
42
+ | 删除恢复 | 只接受 `accept-remote`,并绑定 preview hash、artifact、version 与来源分支 |
43
+ | 归档边界 | 归档不属于常规下拉;需要历史归档时提示用户走 Platform 变更记录下载 |
44
+
45
+ ## P0 执行可信度规则
46
+
47
+ - 命令结果不得靠猜测;普通 Bash 被拒 → 立即改用等价 PowerShell 重试一次
48
+ - 仅 PowerShell 成功且有明确证据(构建/git/测试输出、文件存在、exit 0)时可标 ✅OK;否则 ❌FAIL 或 🟡WARN
49
+ - 禁止把 hook 拒绝、静态验证、无输出、用户跳过说成成功 → 详见 [[../protocols/powershell-protocol.md|powershell-protocol]]、[[../protocols/evidence-based-reporting-protocol.md|evidence-based-reporting-protocol]]
50
+
51
+ ## 生成内容语言约定
52
+
53
+ - sync/ingest 等生成的文档、规则、知识条目、架构说明一律**优先使用中文**撰写(标识符、命令、代码、API 字段名保持原文)
54
+ - 面向平台展示的标题/摘要/正文默认中文;仅当用户明确要求或目标系统强制时才用英文
55
+ > 片段:p0-trust
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: harness-push
3
+ description: 上传本地配置/规则/架构/指令(及显式归档)到 Hunter Platform。仅当用户显式调用 /harness-push
4
+ 或明确说'上传到平台'时使用;不得因存在本地修改就自动触发。
5
+ ---
6
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
7
+ # harness-push — 上传到 Hunter Platform
8
+
9
+ ## Purpose
10
+
11
+ 把本地受管内容(配置、规则、架构、指令,及显式选择的归档包)上传到 Hunter Platform。本 skill 只负责理解意图、展示预览和收集确认;三方 diff、敏感扫描、幂等与本地事务由 CLI/core 完成,不得绕过命令手工拼接上传。
12
+
13
+ ## When to Use
14
+
15
+ 仅当用户显式调用 `/harness-push`(或明确说"上传到 Hunter Platform")时执行。用户只是修改了配置/规则/指令时,**不得**自动建议上传。
16
+
17
+ ## 前置条件
18
+
19
+ - 远端同步已配置:`HUNTER_REMOTE_SYNC_URL`、`HUNTER_REMOTE_SYNC_TOKEN`、`HUNTER_REMOTE_SYNC_ACTOR_ID`。缺失时 CLI 固定 fail closed(`PUSH_PULL_CLI_UNAVAILABLE`),**不得**改用旧 HTTP fallback 或手工 API 调用。
20
+ - 归档上传只选择已写入 outbox 的确定性包(阶段 06 产物);不存在可复用包时提示先完成本地归档,**不得**在 Push 内隐式补做 finalize/重建 ZIP。
21
+
22
+ ## 交互流程
23
+
24
+ 1. **选择范围**:`config` / `rules` / `architecture` / `instructions` / `all`,或显式归档 `--scope archive --change <change-key>`。归档必须显式,避免误传历史包或无法确定目标 Change。
25
+ 2. **展示预览**:分支、提交、远端基线和实际变化文件;无变化时直接结束,不创建空版本。
26
+ 3. **用户确认**:可覆盖风险(hard 敏感阻断除外)必须显式确认后才上传;确认只对本次 preview 有效。
27
+ 4. **按内容分类报告**:成功、跳过、冲突和可重试项分开列出;冲突只投影机器决策,可附 `--resolve <path=keep-local|accept-remote|skip>` 逐项选择。
28
+
29
+ ## 命令
30
+
31
+ ```powershell
32
+ npx hunter-harness harness-push --scope config,rules --json
33
+ npx hunter-harness harness-push --scope archive --change <change-key> --json
34
+ ```
35
+
36
+ ## 关键规则
37
+
38
+ | 规则 | 要点 |
39
+ |------|------|
40
+ | 无变化不上传 | 预览无变化时返回 `no_changes`,不创建确认、不调用执行入口 |
41
+ | 敏感与凭据 | 不做内容遮盖;路径白名单、凭据文件排除、哈希校验保持启用;hard 敏感阻断不得生成确认 |
42
+ | 归档边界 | 归档不进普通范围;`--scope archive` 必须带 `--change`;只消费已有 outbox claim |
43
+ | 失败语义 | `PUSH_PULL_RECEIPT_INVALID`/远端未确认 = 未上传,不得宣称成功;可重试项标注后交还用户 |
44
+
45
+ ## P0 执行可信度规则
46
+
47
+ - 命令结果不得靠猜测;普通 Bash 被拒 → 立即改用等价 PowerShell 重试一次
48
+ - 仅 PowerShell 成功且有明确证据(构建/git/测试输出、文件存在、exit 0)时可标 ✅OK;否则 ❌FAIL 或 🟡WARN
49
+ - 禁止把 hook 拒绝、静态验证、无输出、用户跳过说成成功 → 详见 [[../protocols/powershell-protocol.md|powershell-protocol]]、[[../protocols/evidence-based-reporting-protocol.md|evidence-based-reporting-protocol]]
50
+
51
+ ## 生成内容语言约定
52
+
53
+ - sync/ingest 等生成的文档、规则、知识条目、架构说明一律**优先使用中文**撰写(标识符、命令、代码、API 字段名保持原文)
54
+ - 面向平台展示的标题/摘要/正文默认中文;仅当用户明确要求或目标系统强制时才用英文
55
+ > 片段:p0-trust
@@ -4,7 +4,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
4
4
  .harness/context-index.json)和测试场景表,在隔离上下文运行。仅当用户显式调用 /harness-review 时使用;不得在
5
5
  test 结束后自动接续执行。
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=45223df609c53354; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
8
8
  # harness-review — 代码审查
9
9
 
10
10
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-run
3
3
  description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。仅当用户显式调用
4
4
  /harness-run 时使用;不得因用户提到编码/实现就自动触发,也不得被其他阶段 skill 自动接续。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=45223df609c53354; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
7
7
  # harness-run — 需求编码
8
8
 
9
9
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-submit
3
3
  description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff 合并回主分支。仅当用户显式调用
4
4
  /harness-submit(或 /harness-merge 重入合并段)时使用;用户口头说'提交/commit/push'时必须先确认,不得自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=45223df609c53354; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
7
7
  # harness-submit — 最终提交(含 worktree 合并)
8
8
 
9
9
  ## Purpose
@@ -4,7 +4,7 @@ description: Use when the user asks to synchronize, refresh, or validate Harness
4
4
  metadata, adapters, remote knowledge ownership, instruction entrypoints,
5
5
  config origins, or CodeGraph status.
6
6
  ---
7
- <!-- generated by harness_deploy.py; core=45223df609c53354; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
8
8
  # harness-sync
9
9
 
10
10
  ## Purpose
@@ -3,7 +3,7 @@ name: harness-test
3
3
  description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。仅当用户显式调用 /harness-test
4
4
  时使用;不得在 run 结束后自动接续执行。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=45223df609c53354; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=afd4467d2eb5fe62; overlay=none; agent=codex; do not edit -->
7
7
  # harness-test — 测试执行
8
8
 
9
9
  ## Purpose
@@ -796,6 +796,121 @@ def _receipt_artifact_targets(
796
796
  return targets, None
797
797
 
798
798
 
799
+ def _verify_plan_v2(change_dir: Path) -> dict[str, Any] | None:
800
+ """v2 finalizer(TS)证据的只读验收。
801
+
802
+ v2 不产生 meta/plan-finalization.json;canonical 证据是
803
+ meta/plan-finalization-transactions/ + meta/publication-journals/。
804
+ 本路径只做结构验收:最新事务终态、匹配 journal 的 committed/readback、
805
+ durable targets 存在且字节哈希与 binding 一致。内容质量由 TS 质量层裁决,
806
+ Python 不重复审判。无 v2 证据时返回 None(调用方回退 RECEIPT_MISSING)。
807
+ """
808
+ transactions_dir = change_dir / "meta" / "plan-finalization-transactions"
809
+ if not transactions_dir.is_dir():
810
+ return None
811
+ transactions = sorted(
812
+ transactions_dir.glob("*.json"),
813
+ key=lambda path: path.stat().st_mtime,
814
+ )
815
+ if not transactions:
816
+ return None
817
+ latest: dict[str, Any] | None = None
818
+ for candidate in reversed(transactions):
819
+ try:
820
+ payload = json.loads(candidate.read_text(encoding="utf-8-sig"))
821
+ except (OSError, json.JSONDecodeError):
822
+ continue
823
+ if isinstance(payload, dict):
824
+ latest = payload
825
+ break
826
+ if latest is None:
827
+ return None
828
+ if latest.get("change_key") != change_dir.name:
829
+ return _result_error(
830
+ "RECEIPT_CHANGE_NAME_INVALID",
831
+ "v2 transaction change_key does not match the change directory",
832
+ )
833
+ if latest.get("status") != "publication_committed_event_complete":
834
+ return _result_error(
835
+ "RECEIPT_NOT_FINALIZED",
836
+ f"v2 transaction status is {latest.get('status')!r}, "
837
+ "expected 'publication_committed_event_complete'",
838
+ )
839
+ operation_id = str(latest.get("operation_id") or "")
840
+ journals_dir = change_dir / "meta" / "publication-journals"
841
+ journal: dict[str, Any] | None = None
842
+ if journals_dir.is_dir():
843
+ for candidate in sorted(journals_dir.glob("*.json")):
844
+ try:
845
+ payload = json.loads(candidate.read_text(encoding="utf-8-sig"))
846
+ except (OSError, json.JSONDecodeError):
847
+ continue
848
+ if isinstance(payload, dict) and payload.get("operation_id") == operation_id:
849
+ journal = payload
850
+ break
851
+ if journal is None:
852
+ return _result_error(
853
+ "PUBLICATION_JOURNAL_MISSING",
854
+ f"no publication journal matches v2 operation {operation_id!r}",
855
+ )
856
+ if journal.get("state") != "committed" or journal.get("readback") != "verified":
857
+ return _result_error(
858
+ "PUBLICATION_JOURNAL_NOT_COMMITTED",
859
+ "v2 publication journal is not committed with verified readback",
860
+ )
861
+ binding = journal.get("binding")
862
+ if not isinstance(binding, dict):
863
+ return _result_error("PUBLICATION_BINDING_INVALID", "v2 journal binding missing")
864
+ payload_hashes = binding.get("expected_payload_hashes")
865
+ ownership_paths = binding.get("ownership_paths")
866
+ if not isinstance(payload_hashes, dict) or not isinstance(ownership_paths, list):
867
+ return _result_error("PUBLICATION_BINDING_INVALID", "v2 binding fields malformed")
868
+ required = {
869
+ f"plans/{change_dir.name}-design.md",
870
+ f"plans/{change_dir.name}-plan.md",
871
+ f"plans/{change_dir.name}-implementation-detail.md",
872
+ f"plans/{change_dir.name}-test-scenarios.md",
873
+ "meta/gate-policy.json",
874
+ "meta/worktree.json",
875
+ }
876
+ missing_required = sorted(required - set(ownership_paths))
877
+ if missing_required:
878
+ return _result_error(
879
+ "RECEIPT_FILES_INCOMPLETE",
880
+ "v2 journal omits required artifacts: " + ", ".join(missing_required),
881
+ )
882
+ for rel, expected_hash in payload_hashes.items():
883
+ if not isinstance(expected_hash, str) or not expected_hash.startswith("sha256:"):
884
+ return _result_error("PUBLICATION_BINDING_INVALID", f"{rel}: malformed hash")
885
+ target = change_dir / rel
886
+ try:
887
+ resolved = target.resolve()
888
+ resolved.relative_to(change_dir.resolve())
889
+ except (OSError, ValueError):
890
+ return _result_error(
891
+ "RECEIPT_FILE_PATH_INVALID",
892
+ f"v2 artifact path resolves outside the change directory: {rel}",
893
+ )
894
+ if not target.is_file():
895
+ return _result_error("ARTIFACT_MISSING", f"published artifact missing: {rel}")
896
+ actual_hash = "sha256:" + hashlib.sha256(target.read_bytes()).hexdigest()
897
+ if actual_hash != expected_hash:
898
+ return _result_error(
899
+ "ARTIFACT_HASH_DRIFT",
900
+ f"{rel}: content hash {actual_hash} != v2 binding {expected_hash}",
901
+ )
902
+ return {
903
+ "ok": True,
904
+ "code": "PLAN_V2_VERIFIED",
905
+ "v2": True,
906
+ "operationId": operation_id,
907
+ "runId": latest.get("run_id"),
908
+ "attempt": latest.get("attempt"),
909
+ "artifactCount": len(payload_hashes),
910
+ "status": latest.get("status"),
911
+ }
912
+
913
+
799
914
  def verify_plan(change_dir: Path) -> dict[str, Any]:
800
915
  """Read-only verification of a finalized plan (retro §5.8).
801
916
 
@@ -807,6 +922,10 @@ def verify_plan(change_dir: Path) -> dict[str, Any]:
807
922
  change_dir = change_dir.resolve()
808
923
  receipt_path = change_dir / "meta" / "plan-finalization.json"
809
924
  if not receipt_path.is_file():
925
+ # v2 finalizer 证据路径(canonical 事实源是 transactions + journals)
926
+ v2_result = _verify_plan_v2(change_dir)
927
+ if v2_result is not None:
928
+ return v2_result
810
929
  return _result_error("RECEIPT_MISSING", f"receipt not found: {receipt_path}")
811
930
 
812
931
  try:
@@ -2,5 +2,5 @@
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
4
  "agent": "cursor",
5
- "coreHash": "73d87792e98dd7dc"
5
+ "coreHash": "e4b3e064899ba4af"
6
6
  }