@hunter-harness/workflow-harness 0.2.67 → 0.2.70

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 (210) 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 +4 -2
  8. package/harness/bundles/general/claude-code/harness-plan/reference.md +32 -2
  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 +3 -1
  13. package/harness/bundles/general/claude-code/harness-run/reference.md +20 -0
  14. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
  15. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  16. package/harness/bundles/general/claude-code/harness-test/SKILL.md +3 -1
  17. package/harness/bundles/general/claude-code/harness-test/pitfalls.md +34 -3
  18. package/harness/bundles/general/claude-code/harness-test/reference.md +9 -0
  19. package/harness/bundles/general/claude-code/scripts/harness_context.py +27 -7
  20. package/harness/bundles/general/claude-code/scripts/harness_events.py +12 -3
  21. package/harness/bundles/general/claude-code/scripts/harness_gate.py +127 -11
  22. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +244 -5
  23. package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +373 -19
  24. package/harness/bundles/general/claude-code/scripts/harness_service.py +69 -15
  25. package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +48 -1
  26. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  27. package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +401 -88
  28. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
  29. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  30. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  31. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  32. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +4 -2
  33. package/harness/bundles/general/codebuddy/harness-plan/reference.md +32 -2
  34. package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +55 -0
  35. package/harness/bundles/general/codebuddy/harness-push/SKILL.md +55 -0
  36. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
  37. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +3 -1
  38. package/harness/bundles/general/codebuddy/harness-run/reference.md +20 -0
  39. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
  40. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  41. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +3 -1
  42. package/harness/bundles/general/codebuddy/harness-test/pitfalls.md +34 -3
  43. package/harness/bundles/general/codebuddy/harness-test/reference.md +9 -0
  44. package/harness/bundles/general/codebuddy/scripts/harness_context.py +27 -7
  45. package/harness/bundles/general/codebuddy/scripts/harness_events.py +12 -3
  46. package/harness/bundles/general/codebuddy/scripts/harness_gate.py +127 -11
  47. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +244 -5
  48. package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +373 -19
  49. package/harness/bundles/general/codebuddy/scripts/harness_service.py +69 -15
  50. package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +48 -1
  51. package/harness/bundles/general/codex/.harness-build.json +1 -1
  52. package/harness/bundles/general/codex/contracts/workflow-policy.json +401 -88
  53. package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
  54. package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
  55. package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
  56. package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
  57. package/harness/bundles/general/codex/harness-plan/SKILL.md +4 -2
  58. package/harness/bundles/general/codex/harness-plan/reference.md +32 -2
  59. package/harness/bundles/general/codex/harness-pull/SKILL.md +55 -0
  60. package/harness/bundles/general/codex/harness-push/SKILL.md +55 -0
  61. package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
  62. package/harness/bundles/general/codex/harness-run/SKILL.md +3 -1
  63. package/harness/bundles/general/codex/harness-run/reference.md +20 -0
  64. package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
  65. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  66. package/harness/bundles/general/codex/harness-test/SKILL.md +3 -1
  67. package/harness/bundles/general/codex/harness-test/pitfalls.md +34 -3
  68. package/harness/bundles/general/codex/harness-test/reference.md +9 -0
  69. package/harness/bundles/general/codex/scripts/harness_context.py +27 -7
  70. package/harness/bundles/general/codex/scripts/harness_events.py +12 -3
  71. package/harness/bundles/general/codex/scripts/harness_gate.py +127 -11
  72. package/harness/bundles/general/codex/scripts/harness_ledger.py +244 -5
  73. package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +373 -19
  74. package/harness/bundles/general/codex/scripts/harness_service.py +69 -15
  75. package/harness/bundles/general/codex/scripts/harness_test_guard.py +48 -1
  76. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  77. package/harness/bundles/general/cursor/contracts/workflow-policy.json +401 -88
  78. package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
  79. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  80. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  81. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  82. package/harness/bundles/general/cursor/harness-plan/SKILL.md +4 -2
  83. package/harness/bundles/general/cursor/harness-plan/reference.md +32 -2
  84. package/harness/bundles/general/cursor/harness-pull/SKILL.md +55 -0
  85. package/harness/bundles/general/cursor/harness-push/SKILL.md +55 -0
  86. package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
  87. package/harness/bundles/general/cursor/harness-run/SKILL.md +3 -1
  88. package/harness/bundles/general/cursor/harness-run/reference.md +20 -0
  89. package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
  90. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  91. package/harness/bundles/general/cursor/harness-test/SKILL.md +3 -1
  92. package/harness/bundles/general/cursor/harness-test/pitfalls.md +34 -3
  93. package/harness/bundles/general/cursor/harness-test/reference.md +9 -0
  94. package/harness/bundles/general/cursor/scripts/harness_context.py +27 -7
  95. package/harness/bundles/general/cursor/scripts/harness_events.py +12 -3
  96. package/harness/bundles/general/cursor/scripts/harness_gate.py +127 -11
  97. package/harness/bundles/general/cursor/scripts/harness_ledger.py +244 -5
  98. package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +373 -19
  99. package/harness/bundles/general/cursor/scripts/harness_service.py +69 -15
  100. package/harness/bundles/general/cursor/scripts/harness_test_guard.py +48 -1
  101. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  102. package/harness/bundles/java/claude-code/contracts/workflow-policy.json +401 -88
  103. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
  104. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
  105. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  106. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  107. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  108. package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
  109. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +4 -2
  110. package/harness/bundles/java/claude-code/harness-plan/reference.md +32 -2
  111. package/harness/bundles/java/claude-code/harness-pull/SKILL.md +73 -0
  112. package/harness/bundles/java/claude-code/harness-push/SKILL.md +73 -0
  113. package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
  114. package/harness/bundles/java/claude-code/harness-run/SKILL.md +3 -1
  115. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
  116. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  117. package/harness/bundles/java/claude-code/harness-test/SKILL.md +3 -1
  118. package/harness/bundles/java/claude-code/harness-test/pitfalls.md +34 -3
  119. package/harness/bundles/java/claude-code/scripts/harness_context.py +27 -7
  120. package/harness/bundles/java/claude-code/scripts/harness_events.py +12 -3
  121. package/harness/bundles/java/claude-code/scripts/harness_gate.py +127 -11
  122. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +244 -5
  123. package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +373 -19
  124. package/harness/bundles/java/claude-code/scripts/harness_service.py +69 -15
  125. package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +48 -1
  126. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  127. package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +401 -88
  128. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
  129. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
  130. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  131. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  132. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  133. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
  134. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +4 -2
  135. package/harness/bundles/java/codebuddy/harness-plan/reference.md +32 -2
  136. package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +55 -0
  137. package/harness/bundles/java/codebuddy/harness-push/SKILL.md +55 -0
  138. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
  139. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +3 -1
  140. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
  141. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  142. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +3 -1
  143. package/harness/bundles/java/codebuddy/harness-test/pitfalls.md +34 -3
  144. package/harness/bundles/java/codebuddy/scripts/harness_context.py +27 -7
  145. package/harness/bundles/java/codebuddy/scripts/harness_events.py +12 -3
  146. package/harness/bundles/java/codebuddy/scripts/harness_gate.py +127 -11
  147. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +244 -5
  148. package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +373 -19
  149. package/harness/bundles/java/codebuddy/scripts/harness_service.py +69 -15
  150. package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +48 -1
  151. package/harness/bundles/java/codex/.harness-build.json +1 -1
  152. package/harness/bundles/java/codex/contracts/workflow-policy.json +401 -88
  153. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
  154. package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
  155. package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
  156. package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
  157. package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
  158. package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
  159. package/harness/bundles/java/codex/harness-plan/SKILL.md +4 -2
  160. package/harness/bundles/java/codex/harness-plan/reference.md +32 -2
  161. package/harness/bundles/java/codex/harness-pull/SKILL.md +55 -0
  162. package/harness/bundles/java/codex/harness-push/SKILL.md +55 -0
  163. package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
  164. package/harness/bundles/java/codex/harness-run/SKILL.md +3 -1
  165. package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
  166. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  167. package/harness/bundles/java/codex/harness-test/SKILL.md +3 -1
  168. package/harness/bundles/java/codex/harness-test/pitfalls.md +34 -3
  169. package/harness/bundles/java/codex/scripts/harness_context.py +27 -7
  170. package/harness/bundles/java/codex/scripts/harness_events.py +12 -3
  171. package/harness/bundles/java/codex/scripts/harness_gate.py +127 -11
  172. package/harness/bundles/java/codex/scripts/harness_ledger.py +244 -5
  173. package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +373 -19
  174. package/harness/bundles/java/codex/scripts/harness_service.py +69 -15
  175. package/harness/bundles/java/codex/scripts/harness_test_guard.py +48 -1
  176. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  177. package/harness/bundles/java/cursor/contracts/workflow-policy.json +401 -88
  178. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
  179. package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
  180. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  181. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  182. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  183. package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
  184. package/harness/bundles/java/cursor/harness-plan/SKILL.md +4 -2
  185. package/harness/bundles/java/cursor/harness-plan/reference.md +32 -2
  186. package/harness/bundles/java/cursor/harness-pull/SKILL.md +55 -0
  187. package/harness/bundles/java/cursor/harness-push/SKILL.md +55 -0
  188. package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
  189. package/harness/bundles/java/cursor/harness-run/SKILL.md +3 -1
  190. package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
  191. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  192. package/harness/bundles/java/cursor/harness-test/SKILL.md +3 -1
  193. package/harness/bundles/java/cursor/harness-test/pitfalls.md +34 -3
  194. package/harness/bundles/java/cursor/scripts/harness_context.py +27 -7
  195. package/harness/bundles/java/cursor/scripts/harness_events.py +12 -3
  196. package/harness/bundles/java/cursor/scripts/harness_gate.py +127 -11
  197. package/harness/bundles/java/cursor/scripts/harness_ledger.py +244 -5
  198. package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +373 -19
  199. package/harness/bundles/java/cursor/scripts/harness_service.py +69 -15
  200. package/harness/bundles/java/cursor/scripts/harness_test_guard.py +48 -1
  201. package/harness/manifests/general/claude-code.json +36 -26
  202. package/harness/manifests/general/codebuddy.json +36 -26
  203. package/harness/manifests/general/codex.json +36 -26
  204. package/harness/manifests/general/cursor.json +36 -26
  205. package/harness/manifests/java/claude-code.json +36 -26
  206. package/harness/manifests/java/codebuddy.json +36 -26
  207. package/harness/manifests/java/codex.json +36 -26
  208. package/harness/manifests/java/cursor.json +36 -26
  209. package/hunter-workflow-family.json +11 -7
  210. package/package.json +1 -1
@@ -299,7 +299,7 @@ status: approved
299
299
 
300
300
  > 如果 frontmatter 缺失,后续 run/test/review/submit/archive 不得依赖模型猜测 change-name。
301
301
 
302
- 4. **初始化结构化事件**:确定 change-name 后,立即生成稳定的 `<plan-run-id>`(同一次 plan 尝试内不得改变;首次 `<attempt>` 为 `1`),运行 `harness_events.py append --change-dir ... --phase plan --type phase.start --run-id <plan-run-id> --attempt <attempt>`。finalizer 必须复用完全相同的 `--run-id` / `--attempt`,否则 verify 会按生命周期身份 fail-closed。脚本负责建立父目录和 `events.ndjson`;执行日志在 `phase.end` 时由完整事件流渲染,任何阶段都不得直接用 Write/Edit 维护该投影。
302
+ 4. **初始化结构化事件**:确定 change-name 后,立即生成稳定的 `<plan-run-id>`(必须小写字母开头:v2 identity 规则,裸 UUID 有 10/16 概率数字开头被拒——统一用 `plan_<uuid>` 形状,contracts `createPlanRunId()`;同一次 plan 尝试内不得改变;首次 `<attempt>` 为 `1`),运行 `harness_events.py append --change-dir ... --phase plan --type phase.start --run-id <plan-run-id> --attempt <attempt>`。finalizer 必须复用完全相同的 `--run-id` / `--attempt`,否则 verify 会按生命周期身份 fail-closed。脚本负责建立父目录和 `events.ndjson`;执行日志在 `phase.end` 时由完整事件流渲染,任何阶段都不得直接用 Write/Edit 维护该投影。
303
303
 
304
304
  5. **保存计划文件**:计划文件包含 YAML frontmatter(含 change-name),保存到:
305
305
  - `.harness/changes/<change-name>/plans/<change-name>-plan.md`(简洁任务表)
@@ -357,7 +357,14 @@ npx hunter-harness plan evidence-pack --input .harness/changes/<cn>/meta/plan-ev
357
357
  npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidence.json
358
358
  ```
359
359
 
360
- **自然输入文件**(`meta/plan-evidence-input.json`,权威定义 `packages/cli/src/commands/plan-evidence-pack.ts` 的 `EvidencePackInputFile`)由规划阶段逐步沉淀,各字段定稿时点不得倒置:
360
+ **自然输入文件**(`meta/plan-evidence-input.json`,权威定义 `packages/cli/src/commands/plan-evidence-pack.ts` 的 `EvidencePackInputFile`)由规划阶段逐步沉淀,各字段定稿时点不得倒置。
361
+
362
+ > 📋 **先取骨架,别猜结构**:CLI 自带模板输出,不需要去找 TS 接口或翻 npx 缓存——
363
+ > ```bash
364
+ > npx hunter-harness plan evidence-pack --print-template > .harness/changes/<cn>/meta/plan-evidence-input.json
365
+ > ```
366
+ > 输出是带 `<...>` 占位符的完整骨架,逐项替换即可;替换完 grep 一次 `<` 自检有无遗漏。
367
+ > `--print-template` 不读写任何文件,只打到 stdout。
361
368
 
362
369
  | 字段 | 内容 | 定稿阶段 |
363
370
  |------|------|:---:|
@@ -379,6 +386,29 @@ npx hunter-harness plan finalize --input .harness/changes/<cn>/meta/plan-evidenc
379
386
  - **legacy 收据**:过渡期(阶段 14 前)v2 路径不写 `plan-finalization.json`;消费方若仍读 legacy receipt,由兼容投影单独提供,不得反向要求 v2 双写。
380
387
  - **回退**:v2 自然输入不完整(如缺真实审批记录)时才允许走 Python finalizer(legacy 路径);同一次发布不得两条路径混用。
381
388
 
389
+ ### 发布后修订计划(republish)
390
+
391
+ 计划发布后又要改产物,是**正常且高频**的情况——用户看完计划补一个回归场景、修正一条任务、调整验收标准。这时不要与哈希守卫搏斗:
392
+
393
+ ```bash
394
+ python <skills-root>/scripts/harness_plan_finalize.py republish \
395
+ --change-dir ".harness/changes/<cn>" --staging-dir ".harness/changes/<cn>/runtime/plan-staging" \
396
+ --change <cn> --run-id "plan_$(uuidgen)" --reason "<为什么要改>" --json
397
+ ```
398
+
399
+ 它一次性完成整套动作:校验 staging → 分配新 attempt(自动取已用最大值 +1)→ 追加 `phase.start` → 替换收据 → 重新派生 `scenario-manifest.json` 与 `implementation-checkpoints.json` → 写 `phase.end`。收据里保留 `supersedes`(被取代的 hash/runId/attempt)与 `amendReason`,修订全程可审计。
400
+
401
+ | 约束 | 说明 |
402
+ |------|------|
403
+ | `--reason` 必填 | 修订已发布计划必须留下理由,否则 `PLAN_AMEND_REASON_REQUIRED` |
404
+ | `--run-id` 必须全新 | 复用旧 run-id 报 `PLAN_AMEND_RUN_ID_IN_USE`(attempt 与 run-id 一一绑定) |
405
+ | 首次发布不能用它 | 无收据时报 `PLAN_NOT_FINALIZED`,首次发布走 `finalize` |
406
+ | 内容没变则空操作 | 返回 `idempotent:true`,不写事件、不消耗 attempt |
407
+
408
+ ⚠️ **绝对不要手改 `meta/scenario-manifest.json`**。它是 finalizer 从 `test-scenarios.md` 派生的产物,手改会造成真实漂移:`verify` 报 `ARTIFACT_HASH_DRIFT`,run 阶段 `validate_plan_handoff` 也会记 WARN。`republish` 会重新派生它,这才是唯一正确入口。
409
+
410
+ > 直接重跑 `finalize` 会报 `PLAN_FINALIZATION_HASH_CONFLICT`——这是守卫在防止发布后产物被悄悄改动,不是 bug。报错信息里已经给出 `republish` 命令行。
411
+
382
412
  ### Plan 结束行为规则
383
413
 
384
414
  - **禁止询问执行模式**:Subagent-Driven / Inline Execution 属于 /harness-run 阶段
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: harness-pull
3
+ description: 从 Hunter Platform 下拉配置/规则/架构/指令(及显式来源分支的分支文件恢复)。仅当用户显式调用
4
+ /harness-pull 或明确说'从平台拉取/恢复'时使用;不得自动触发。
5
+ ---
6
+ <!-- generated by harness_deploy.py; core=c79977f18734b7c0; 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=c79977f18734b7c0; 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=c79977f18734b7c0; 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=c79977f18734b7c0; overlay=none; agent=codex; do not edit -->
7
7
  # harness-run — 需求编码
8
8
 
9
9
  ## Purpose
@@ -68,6 +68,8 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
68
68
 
69
69
  **阶段归属规则**:只用 `ownerPhase=run` 的任务和场景判定编码阶段结果。`ownerPhase=test` 的任务或场景按计划留给测试阶段属于正常移交,必须记录为“待测试阶段执行”,不得将编码阶段降级为 WARN;只有 run 自身负责的工作未完成、验证降级或证据异常时才使用 WARN。
70
70
 
71
+ > 关门脚本与本规则一致:`harness_gate.py close --phase run` 的 C9 场景覆盖只要求 `ownerPhase` 为 `plan`/`run` 的必需场景有通过 receipt,`ownerPhase=test` 的场景出现在返回值的 `deferred` 里,不阻断 run。若 run 关门报 `REQUIRED_SCENARIO_NOT_EXECUTED` 且缺的是接口/端到端场景,那是 `meta/scenario-manifest.json` 里 `ownerPhase` 标错了(或老清单没声明),应当修清单——**不要**为了过门在 run 阶段起服务补跑本属 test 的验证。
72
+
71
73
  **Fixback**:入口只用 `launch-review`,后续问题处理通过 `resolve-issue/close` 驱动,不得把修复说明当成新的普通 Run。只读取返回的受影响问题和文件;验证仅失效与 `changedFiles` 相交的目标,其他 Test/Review 证据继续复用。RED 优先;`manual`、`workflow` 或未选用的建议不进入代码批次,使用中文记录处理结论。
72
74
 
73
75
  **执行器边界**:优先使用项目 build profile 和已有测试入口。禁止为了绕过 ESM、路径或参数问题临时生成 `.js`、`require` 脚本;需要文件式 runner 时使用项目已有入口,确需新增时遵循项目模块类型(例如 ESM 使用 `.mjs`)。runner 包装说明写入 `runnerCommand` 元数据,不得拼进账本的规范 `command`。
@@ -799,6 +799,8 @@ powershell.exe -Command "git -C '<project-path>' diff --check"
799
799
  > 可直接复制;替换 `<skills-root>` / `<cn>` / `<dir>` / `<project>`。`--task` **仅在该 change 启用 checkpoint 时必需**(checkpoints 文件缺失或不含 pending foundation-gate 时不要传)。
800
800
  > `ledger record` 必需:`--duration-ms`、`--evidence`,以及 `--files`(逗号分隔源文件)或 `--profile-input <verificationKey> --project <project>`(从 build-profile 展开)。`status` 枚举: `ok|fail|not_run`(没有 PASS)。
801
801
  > `--skills-root` 仅用于 `begin`(及 `lint-skills`):必须是 adapter 根(如 `.cursor/skills`),不是 `scripts/` 子目录。**`close` 不需要 `--skills-root`**(该子命令不接受此参数)。
802
+ >
803
+ > ⚠️ **`--project` 一律传路径,不传项目名。** 在项目根执行时就写 `--project .`。传项目名(`--project udp`)会被解析成 `<cwd>/udp`,然后以各不相同的下游错误暴露出来(`PROJECT_ROOT_INVALID` / `EXECUTION_ROOT_INVALID`)。
802
804
 
803
805
  ```powershell
804
806
  # gate begin/close(--task 仅在该 change 启用 checkpoint 时必需;close 不需要 --skills-root)
@@ -810,8 +812,21 @@ python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verif
810
812
 
811
813
  # 复用检查(--profile-input 取 verification key,不是文件路径;配合 --project)
812
814
  python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --verification unitTestFull --profile-input unitTestFull --project <project>
815
+
816
+ # scenario-manifest schemaVersion 2:record 绑定场景必须带 receipt,先生成骨架再记录
817
+ python <skills-root>/scripts/harness_ledger.py scenario-receipt-template --change-dir <dir> --scenario-ids "UT-001,UT-002" --runner <runner 名> --out runtime/scenario-receipt-unit.json --json
818
+ python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verification unitTest --status ok ... --scenario-ids "UT-001,UT-002" --scenario-receipt-file runtime/scenario-receipt-unit.json
813
819
  ```
814
820
 
821
+ ### run 阶段关门顺序
822
+
823
+ ```powershell
824
+ python <skills-root>/scripts/harness_test_guard.py close --project . --change-dir ".harness/changes/<cn>" --json
825
+ python <skills-root>/scripts/harness_gate.py close --change <cn> --phase run --status OK --to-phase <后继> --executor <tool>
826
+ ```
827
+
828
+ C9 场景覆盖按 `ownerPhase` **分阶段判定**:run 关门只要求 `ownerPhase` 为 `plan`/`run` 的必需场景有通过 receipt;`ownerPhase=test` 的场景计入返回值的 `deferred`,属正常移交,不阻断 run。反过来,test 关门时这些场景仍然必须有 receipt。未声明 `ownerPhase` 的老清单沿用旧语义(全部即时要求)。
829
+
815
830
  ### 常见报错对照
816
831
 
817
832
  | 报错 | 原因 | 处理 |
@@ -823,6 +838,11 @@ python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --ve
823
838
  | `record requires --files or a non-empty --profile-input file set` | 缺少输入文件集 | 补 `--files` 或 `--profile-input <key> --project <project>` |
824
839
  | `--profile-input requires --project` | can-reuse/record 展开 profile 需要项目根 | 补 `--project <project>` |
825
840
  | `record` 缺 `--duration-ms` / `--evidence` | 参数为必填 | 按模板补齐 |
841
+ | `PROJECT_ROOT_INVALID` | `--project` 传了项目名而不是路径 | 在项目根执行并传 `--project .`;报错的 `resolvedProject` 字段显示了实际解析到哪 |
842
+ | `EXECUTION_ROOT_INVALID` | gate 的 `--project` 是**执行根路径**(worktree),同样不接受项目名 | 传 `.` 或 worktree 绝对路径 |
843
+ | `SCENARIO_RECEIPT_REQUIRED` | manifest 是 schemaVersion 2,`--scenario-ids` 必须配 receipt | 用上面的 `scenario-receipt-template` 生成骨架,再传 `--scenario-receipt-file` |
844
+ | `SCENARIO_RECEIPT_NOT_FOUND` | receipt 路径找不到 | 报错的 `triedPaths` 列出了尝试过的绝对路径;相对路径同时按 CWD 和 `--change-dir` 解析,二选一放对即可 |
845
+ | `REQUIRED_SCENARIO_NOT_EXECUTED`(run 关门,缺的是接口场景) | 这些场景 `ownerPhase` 应为 `test` 却写成了 run,或清单未声明 `ownerPhase` | 修 `meta/scenario-manifest.json` 的 `ownerPhase`;**不要**为了过门去跑本该属于 test 阶段的验证 |
826
846
 
827
847
  ## 关键原则
828
848
 
@@ -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=c79977f18734b7c0; 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=c79977f18734b7c0; 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=c79977f18734b7c0; overlay=none; agent=codex; do not edit -->
7
7
  # harness-test — 测试执行
8
8
 
9
9
  ## Purpose
@@ -181,6 +181,8 @@ python <skills-root>/scripts/harness_test_guard.py record --project . --change-d
181
181
 
182
182
  先读 `.harness/changes/<change-name>/runtime/credential-cache.json`(认证凭证缓存,按项目认证机制;token/SSO 为常见实现),本地轻量接口验证通过则复用,失败才走远程认证。接口测试执行器用 request context / 原生 HTTP 客户端直连本地 baseURL,**不得依赖浏览器当前页面 origin**。同一次流程内凭证刷新计数 > 1 → 🟡 WARN。**不得在报告/日志/对话总结中输出明文凭证**。详见 `reference.md`「认证凭证缓存与复用」。
183
183
 
184
+ > ⛔ **验证码 = 硬停,不是待解的技术问题。** 登录响应出现 `验证码` / `captcha` / `blockPuzzle` / `slider` / `geetest` 等特征时,**禁止**编写或运行任何求解代码(图像匹配、OCR、打码平台、反编译服务端找容差均在禁止之列)。立即记 `apiTest=BLOCKED`,请用户手工把凭证写入 `runtime/credential-cache.json` 或临时关闭测试环境验证码,并提示 `.harness/config/harness-test-config.md` 的认证方式已过期。详见 `pitfalls.md` 规则 31。
185
+
184
186
  ### 九、测试报告状态规则
185
187
 
186
188
  整体 ✅OK / 🟡WARN / ❌FAIL 三态;API 维度使用 `OK` / `PARTIAL` / `BLOCKED` / `NOT_RUN` / `FAIL` 五态。**不得把「5 PASS + 9 BLOCKED + 1 FAIL」写成 `apiTest=NOT_RUN`**,正确为 `apiTest=PARTIAL`。P0 场景 BLOCKED 不得仍 OK。详见 `reference.md`「结果分级规则」。
@@ -2,16 +2,16 @@
2
2
  description: harness-test 的踩坑规则(来自真实对话日志)。执行测试前必须通读,遇到测试失败时重新参考。
3
3
  ---
4
4
 
5
- # 避坑规则(30 条)
5
+ # 避坑规则(32 条)
6
6
 
7
- > 以下规则来自真实对话日志(2026-06-12 ~ 06-24),每个都有明确的现象和根因。
7
+ > 以下规则来自真实对话日志(2026-06-12 ~ 08-17),每个都有明确的现象和根因。
8
8
  > 执行 `harness-test` 时必须通读,避免重蹈覆辙。
9
9
 
10
10
  | # | 规则 | 现象 | 根因 | 解法 |
11
11
  |:--:|------|------|------|------|
12
12
  | 1 | 凭证可展示但注意安全 | 对话日志记录了凭证 | Bearer 凭证被粘贴到聊天中 | 凭证可以在对话中展示用于调试,但不要写入持久化文件(测试报告、执行日志等) |
13
13
  | 2 | 不用 curl 发中文 | `Invalid UTF-8 start byte 0xb2` | Windows curl 默认 GBK 编码 | 用 PowerShell Invoke-WebRequest 或 Playwright fetch |
14
- | 3 | 必须带租户标识 | 所有接口 500 | 租户上下文为空 | 请求头加租户标识(见 build-profile.httpHeaders |
14
+ | 3 | 必须带租户标识 | 所有接口 500,或 200 但 `data:null` | 租户上下文为空,或请求头租户值与测试数据行的租户不一致 | 请求头加租户标识(见 build-profile.httpHeaders);**取值必须等于测试数据实际所属租户**,先查数据行的租户列再填,不要默认写 0 |
15
15
  | 4 | 认证降级 | 接口 401 "账号未登录" | 本地无认证服务 | 应用配置放行(如 permit-all-urls)或权限降级 |
16
16
  | 5 | 错误码双格式兼容 | 测试断言失败 | 预期 `1_003_002_009`,实际 `1003002009` | 自动兼容下划线和数字两种格式 |
17
17
  | 6 | 编译产物确认 | 修改代码后测试结果不变 | IDE 热重载未触发 | 测试前编译(如 `mvn compile -o`) |
@@ -39,6 +39,8 @@ description: harness-test 的踩坑规则(来自真实对话日志)。执行
39
39
  | 28 | **已有服务未先决策就跑业务接口** | 旧服务不含新代码,接口 500 后才发现版本不匹配 | 检测到已有应用服务(端口被占)后,未先展示服务决策门就跑业务接口 | 先展示服务决策门,询问复用/重启/跳过/停止;询问前只允许 health/meta 检查 |
40
40
  | 29 | **启动命令反复试相对配置叠加 / 中文路径** | 相对路径或中文路径导致应用读不到运行时配置叠加,启动失败 | 默认只用 `C:/temp/harness-test-overlay/<change>/application-harness-test.yml` ASCII 绝对路径,并固化已知良好测试配置 |
41
41
  | 30 | **伪 diffHash 导致错误复用** | `3files-84plus-5minus` 不能证明代码未变 | 用 `git diff --binary` 生成 patch 并计算 SHA-256,ledger 只认 `sha256:<hash>` |
42
+ | 31 | **尝试破解验证码 / 人机验证** | 认证服务返回"验证码不能为空",AI 写图像识别脚本反复试坐标(实测 28 次 / 4 种算法全失败) | 测试配置里的账号密码登录流程被加了滑块/图形验证码,配置未同步 | **硬停**。禁止编写或运行任何验证码求解代码;立即标 `apiTest=BLOCKED`,请用户手工提供凭证写入 `runtime/credential-cache.json`,或临时关闭测试环境验证码 |
43
+ | 32 | **预存环境缺陷被当成本次变更的阻塞** | `unitTestFull` 144 个测试挂 1 个,根因是别人提交引入的缺省配置缺失 | 全量验证把预存缺陷和本次 diff 混在一起判定 | 先用 `git log -S` + diff 触点证明与本次变更无关,再按「预存缺陷」上报并请用户决策(修根因 / 记 🟡WARN / 停止),**不得**擅自改 tracked 配置或排除测试 |
42
44
 
43
45
 
44
46
  ## 详细说明
@@ -257,6 +259,35 @@ $cred = $resp.data.accessToken
257
259
  **后果**:代码已变却复用旧测试结果,掩盖回归问题
258
260
  **正确做法**:用 `git diff --binary` 生成 patch 并计算 SHA-256,ledger 只认 `sha256:<hash>` 格式。diffHash 不一致时必须重跑相关测试。
259
261
 
262
+ ### 规则 31:尝试破解验证码 / 人机验证
263
+ **严重度**:❌FAIL(硬性禁止)
264
+ **场景**:本地服务已起来,但业务接口需要真实登录上下文;认证服务登录接口返回 `验证码不能为空` / 返回滑块拼图(如 AJ-Captcha `blockPuzzle`)/ 图形验证码 / 短信验证码
265
+ **后果**:真实日志里 AI 连续写了 4 版求解算法(边缘求和 → NCC 模板匹配 → 二维边缘匹配 → 一维归一化互相关)、反编译服务端 jar 找容差、共 28 次尝试全部失败,烧掉整段会话,最终仍然 BLOCKED
266
+ **正确做法**:**验证码是安全控制,不是待调试的技术问题。** 一旦识别到验证码/人机验证:
267
+
268
+ 1. **立即停止**,不得编写、下载或运行任何验证码求解代码(图像匹配、OCR、打码平台、反编译校验逻辑找容差都在禁止之列)
269
+ 2. 该维度记 `apiTest=BLOCKED`,受影响场景标 🟡 BLOCKED,**不得**伪造 receipt 或把未执行写成通过
270
+ 3. 向用户输出原文,并给出三条可选出路:
271
+ - 用户手工登录后把凭证写入 `.harness/changes/<change-name>/runtime/credential-cache.json`(推荐,执行器本来就先读 cache)
272
+ - 用户在测试环境临时关闭验证码开关,AI 重跑
273
+ - 本轮跳过接口验证,按 BLOCKED 归档,留待环境就绪
274
+ 4. 顺手提示更新 `.harness/config/harness-test-config.md`——认证方式已变更,配置已过期
275
+
276
+ > 判据:只要登录响应里出现 `验证码` / `captcha` / `blockPuzzle` / `slider` / `geetest` / `secretKey + originalImageBase64` 任一特征,即触发本规则。
277
+
278
+ ### 规则 32:预存环境缺陷被当成本次变更的阻塞
279
+ **严重度**:🟡WARN
280
+ **场景**:模块全量验证(如 `unitTestFull`)失败,但失败点在本次 diff 之外——例如别人提交的类新增了无默认值的配置占位符,导致某个全上下文测试无法加载
281
+ **后果**:要么误判成自己的回归浪费时间排查,要么擅自改 tracked 配置 / 排除测试把问题掩盖掉,污染 diff
282
+ **正确做法**:
283
+
284
+ 1. **先证明无关**:`git log -S '<符号>' --oneline` 定位引入提交 + `git diff` 证明本次触点不涉及该文件/目录,两条证据都要有
285
+ 2. **再上报**:按「预存缺陷」向用户说明——受影响模块、失败文件、引入提交、不阻断依据
286
+ 3. **由用户决策**:修根因(会扩大 diff,需明确点头)/ 记 🟡WARN 继续 / 停止本轮
287
+ 4. **禁止自作主张**:不得改 tracked 配置文件、不得 `-Dtest=` 排除失败测试、不得偏离 build-profile 的规范命令——这些都会让证据与 ledger 命令不一致
288
+
289
+ > 用户批准修复后,该修复文件即进入本次 diff:必须在提交前复核它仍在工作区(真实日志里用户中途手工还原了该修复,AI 直到 staging 才发现)。
290
+
260
291
  > diffHash/ledger 复用规则遵循 `../protocols/ledger-protocol.md`;结果证据要求遵循 `../protocols/evidence-based-reporting-protocol.md`。
261
292
 
262
293
  ## BOM-safe JSON / identifier sanitizer / 凭据扫描 (runtime-helpers.mjs)
@@ -736,6 +736,8 @@ TEST_<change-name>_<timestamp>_<short-random>
736
736
 
737
737
  > test 阶段常用子集。`--task` **仅在该 change 启用 checkpoint 时必需**。ledger status 枚举: ok|fail|not_run(没有 PASS)。`record` 还需 `--duration-ms`、`--evidence`,以及 `--files` 或 `--profile-input`+`--project`。
738
738
  > `--skills-root` 仅用于 `begin`(及 `lint-skills`):必须是 adapter 根(如 `.cursor/skills`),不是 `scripts/` 子目录。**`close` 不需要 `--skills-root`**(该子命令不接受此参数)。
739
+ >
740
+ > ⚠️ **`--project` 一律传路径,不传项目名**(在项目根就写 `--project .`)。传项目名会解析成 `<cwd>/<名字>` 并报 `PROJECT_ROOT_INVALID` / `EXECUTION_ROOT_INVALID`。
739
741
 
740
742
  ```powershell
741
743
  # gate begin/close(phase=test;--task 仅 checkpoint 启用时必需;close 不需要 --skills-root)
@@ -746,6 +748,9 @@ python <skills-root>/scripts/harness_gate.py close --change <cn> --phase test --
746
748
  python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verification unitTestFull --status ok --command "<完整命令>" --exit-code 0 --duration-ms 120000 --evidence "Tests run: N, Failures: 0" --coverage full --files "packages/core/src/index.ts"
747
749
  python <skills-root>/scripts/harness_ledger.py record --change-dir <dir> --verification browserTest --status ok --command "<真实栈 Playwright 命令>" --exit-code 0 --duration-ms 120000 --evidence "Browser E2E: N passed, 0 failed" --coverage module --files "<Playwright 配置与受测 spec>"
748
750
  python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --verification unitTestFull --profile-input unitTestFull --project <project>
751
+
752
+ # scenario-manifest schemaVersion 2:绑定场景必须带 receipt,先生成骨架再 record
753
+ python <skills-root>/scripts/harness_ledger.py scenario-receipt-template --change-dir <dir> --scenario-ids "API-001,API-002" --runner <runner 名> --out runtime/scenario-receipt-api.json --json
749
754
  ```
750
755
 
751
756
  > **Ledger v3(v2 契约 / split-v1 布局起)**:`record` 强制顶层身份(缺失非零退出、不写账本);`--metrics-json` 必须过 typed schema(unit/apiTest/browserTest/apiContract/dbCompatibility 各有不同必填键);`browserTest` 在报告中投影为 `browserE2E`;dbCompatibility 等不适用验证用 `--applicability NOT_APPLICABLE --applicability-reason "<scope 原因>"`(不计通过也不计失败)。legacy 契约行为不变。详见 `../protocols/ledger-protocol.md` 第十节。
@@ -761,6 +766,10 @@ python <skills-root>/scripts/harness_ledger.py can-reuse --change-dir <dir> --ve
761
766
  | `record requires --files or a non-empty --profile-input file set` | 缺少输入文件集 | 补 `--files` 或 `--profile-input <key> --project <project>` |
762
767
  | `--profile-input requires --project` | can-reuse/record 展开 profile 需要项目根 | 补 `--project <project>` |
763
768
  | `record` 缺 `--duration-ms` / `--evidence` | 参数为必填 | 按模板补齐 |
769
+ | `PROJECT_ROOT_INVALID` | `--project` 传了项目名而不是路径 | 传 `.` 或绝对路径;报错的 `resolvedProject` 显示实际解析结果 |
770
+ | `SCENARIO_RECEIPT_REQUIRED` | manifest 是 schemaVersion 2,`--scenario-ids` 必须配 receipt | 用 `scenario-receipt-template` 生成骨架 |
771
+ | `SCENARIO_RECEIPT_NOT_FOUND` | receipt 路径找不到 | 看报错的 `triedPaths`;相对路径同时按 CWD 与 `--change-dir` 解析 |
772
+ | `REQUIRED_SCENARIO_NOT_EXECUTED`(test 关门) | `ownerPhase=test` 的场景到 test 阶段仍无通过 receipt——这是真阻塞 | 补跑该场景;接口被验证码挡住时按 `pitfalls.md` 规则 31 记 BLOCKED,不得伪造 receipt |
764
773
 
765
774
  `can-reuse` 的主文案按 `executionNeed` 呈现,错误码仅放技术详情:`first-run` 表示首次执行,`rerun` 表示已有证据因输入或环境变化失效,`evidence-incomplete` 表示旧记录缺少当前契约字段,`reuse` 表示可直接复用。不得把 `VALIDATION_MISSING` 翻译成“强制重跑”。
766
775
 
@@ -219,10 +219,30 @@ def _same_repository(project: Path, candidate: Path) -> bool:
219
219
  return candidate.is_relative_to(project)
220
220
 
221
221
 
222
+ def _contract_error_code(exc: BaseException) -> str:
223
+ """Keep PROJECT_ROOT_INVALID distinct from a genuinely missing change."""
224
+ if str(exc).startswith("PROJECT_ROOT_INVALID"):
225
+ return "PROJECT_ROOT_INVALID"
226
+ return "CHANGE_NOT_FOUND"
227
+
228
+
222
229
  def _contract(project: Path, change: str) -> tuple[Path, dict[str, Any], Path]:
223
230
  root = project.resolve()
224
- contract_root = (root / ".harness" / "changes" / change).resolve()
231
+ # A bare project *name* (--project udp) resolves to <cwd>/udp and then
232
+ # reports CHANGE_NOT_FOUND, which sends the caller hunting for the change
233
+ # instead of fixing the argument. Separate the two failures.
234
+ if not root.is_dir():
235
+ raise ValueError(
236
+ f"PROJECT_ROOT_INVALID: {root} is not a directory — --project takes "
237
+ "a path to the project root (use '.'), not the project name"
238
+ )
225
239
  changes_root = (root / ".harness" / "changes").resolve()
240
+ if not changes_root.is_dir():
241
+ raise ValueError(
242
+ f"PROJECT_ROOT_INVALID: {changes_root} does not exist — "
243
+ f"{root} is not a harness project root"
244
+ )
245
+ contract_root = (root / ".harness" / "changes" / change).resolve()
226
246
  if not contract_root.is_relative_to(changes_root) or not contract_root.is_dir():
227
247
  raise ValueError(f"CHANGE_NOT_FOUND: {change}")
228
248
  context_path = contract_root / "meta" / "change-context.json"
@@ -296,7 +316,7 @@ def configure_phase_plan(
296
316
  try:
297
317
  contract_root, _contract_data, _state_root = _contract(project, change)
298
318
  except (OSError, ValueError, json.JSONDecodeError) as exc:
299
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
319
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
300
320
  normalized = [str(item).strip() for item in phases if str(item).strip()]
301
321
  if (
302
322
  not normalized
@@ -802,7 +822,7 @@ def close_transition(
802
822
  try:
803
823
  contract_root, _contract_data, state_root = _contract(project, change)
804
824
  except (OSError, ValueError, json.JSONDecodeError) as exc:
805
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
825
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
806
826
  allowed_next = _allowed_next_phases(contract_root, from_phase)
807
827
  if to_phase not in allowed_next:
808
828
  planned_phases, source = _phase_plan(contract_root)
@@ -962,7 +982,7 @@ def _begin_transition_unlocked(
962
982
  try:
963
983
  contract_root, _contract_data, state_root = _contract(project, change)
964
984
  except (OSError, ValueError, json.JSONDecodeError) as exc:
965
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
985
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
966
986
  paths = _paths(state_root)
967
987
  try:
968
988
  execution_root = _execution_root(project, contract_root, state_root)
@@ -1074,7 +1094,7 @@ def begin_transition(
1074
1094
  try:
1075
1095
  _contract_root, _contract_data, state_root = _contract(project, change)
1076
1096
  except (OSError, ValueError, json.JSONDecodeError) as exc:
1077
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
1097
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
1078
1098
  paths = _paths(state_root)
1079
1099
  with _exclusive_state_lock(paths["runtime"] / "branch-selection.lock"):
1080
1100
  return _begin_transition_unlocked(
@@ -1100,7 +1120,7 @@ def cancel_prepared_context(
1100
1120
  try:
1101
1121
  _contract_root, _contract_data, state_root = _contract(project, change)
1102
1122
  except (OSError, ValueError, json.JSONDecodeError) as exc:
1103
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
1123
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
1104
1124
  paths = _paths(state_root)
1105
1125
  removed: list[str] = []
1106
1126
  receipt_hash: str | None = None
@@ -1186,7 +1206,7 @@ def context_view(project: Path, change: str) -> dict[str, Any]:
1186
1206
  try:
1187
1207
  contract_root, contract, state_root = _contract(project, change)
1188
1208
  except (OSError, ValueError, json.JSONDecodeError) as exc:
1189
- return {"ok": False, "code": "CHANGE_NOT_FOUND", "error": str(exc)}
1209
+ return {"ok": False, "code": _contract_error_code(exc), "error": str(exc)}
1190
1210
  paths = _paths(state_root)
1191
1211
  transitions = _read_ndjson(paths["transitions"])
1192
1212
  begins = _read_ndjson(paths["begins"])
@@ -704,7 +704,11 @@ def append_event(
704
704
  return {
705
705
  "ok": False,
706
706
  "code": "PHASE_ALREADY_CLOSED",
707
- "message": "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same attempt",
707
+ "message": (
708
+ "PHASE_ALREADY_CLOSED: refusing a second phase.end for the "
709
+ "same attempt. A retry needs a fresh run id AND the next "
710
+ "attempt number (attempts are global per phase, not per run id)."
711
+ ),
708
712
  "event": event,
709
713
  "autoSealed": result.get("autoSealed") or [],
710
714
  }
@@ -1931,7 +1935,9 @@ def cmd_append(args: argparse.Namespace) -> int:
1931
1935
  ):
1932
1936
  attempt_error = (
1933
1937
  "EVENT_ATTEMPT_CONFLICT: run_id is already bound to "
1934
- f"attempt {expected_attempt}, received {explicit_attempt}"
1938
+ f"attempt {expected_attempt}, received "
1939
+ f"{explicit_attempt}. One run id maps to exactly one "
1940
+ "attempt — allocate a fresh run id for the next attempt."
1935
1941
  )
1936
1942
  else:
1937
1943
  event["attempt"] = expected_attempt
@@ -1972,7 +1978,10 @@ def cmd_append(args: argparse.Namespace) -> int:
1972
1978
  return emit_error(f"append failed: {exc}", as_json=as_json)
1973
1979
  if phase_closed:
1974
1980
  return emit_error(
1975
- "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same attempt",
1981
+ "PHASE_ALREADY_CLOSED: refusing a second phase.end for the same "
1982
+ "attempt. A retry needs a fresh run id AND the next attempt number "
1983
+ "(attempts are global per phase, not per run id). For a published "
1984
+ "plan use harness_plan_finalize.py republish, which allocates both.",
1976
1985
  as_json=as_json,
1977
1986
  error_code="PHASE_ALREADY_CLOSED",
1978
1987
  )