@hunter-harness/workflow-harness 0.2.60 → 0.2.61

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/README.md +7 -7
  3. package/harness/bundles/general/claude-code/harness-archive/SKILL.md +35 -35
  4. package/harness/bundles/general/claude-code/harness-archive/checklist.md +5 -2
  5. package/harness/bundles/general/claude-code/harness-archive/reference.md +23 -7
  6. package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
  7. package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  8. package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
  9. package/harness/bundles/general/claude-code/harness-plan/SKILL.md +4 -4
  10. package/harness/bundles/general/claude-code/harness-review/SKILL.md +2 -2
  11. package/harness/bundles/general/claude-code/harness-run/SKILL.md +2 -2
  12. package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
  13. package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
  14. package/harness/bundles/general/claude-code/harness-test/SKILL.md +2 -2
  15. package/harness/bundles/general/claude-code/protocols/archive-report-protocol.md +7 -3
  16. package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +1 -1
  17. package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +6 -4
  18. package/harness/bundles/general/claude-code/protocols/state-layout-protocol.md +3 -2
  19. package/harness/bundles/general/claude-code/scripts/harness_archive.py +829 -68
  20. package/harness/bundles/general/claude-code/scripts/harness_context.py +8 -2
  21. package/harness/bundles/general/claude-code/scripts/harness_events.py +5 -0
  22. package/harness/bundles/general/claude-code/scripts/harness_ledger.py +79 -2
  23. package/harness/bundles/general/claude-code/scripts/harness_profile.py +7 -0
  24. package/harness/bundles/general/claude-code/scripts/harness_state.py +22 -3
  25. package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
  26. package/harness/bundles/general/codebuddy/README.md +7 -7
  27. package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +35 -35
  28. package/harness/bundles/general/codebuddy/harness-archive/checklist.md +5 -2
  29. package/harness/bundles/general/codebuddy/harness-archive/reference.md +23 -7
  30. package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
  31. package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  32. package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  33. package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +4 -4
  34. package/harness/bundles/general/codebuddy/harness-review/SKILL.md +2 -2
  35. package/harness/bundles/general/codebuddy/harness-run/SKILL.md +2 -2
  36. package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
  37. package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
  38. package/harness/bundles/general/codebuddy/harness-test/SKILL.md +2 -2
  39. package/harness/bundles/general/codebuddy/protocols/archive-report-protocol.md +7 -3
  40. package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +1 -1
  41. package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +6 -4
  42. package/harness/bundles/general/codebuddy/protocols/state-layout-protocol.md +3 -2
  43. package/harness/bundles/general/codebuddy/scripts/harness_archive.py +829 -68
  44. package/harness/bundles/general/codebuddy/scripts/harness_context.py +8 -2
  45. package/harness/bundles/general/codebuddy/scripts/harness_events.py +5 -0
  46. package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +79 -2
  47. package/harness/bundles/general/codebuddy/scripts/harness_profile.py +7 -0
  48. package/harness/bundles/general/codebuddy/scripts/harness_state.py +22 -3
  49. package/harness/bundles/general/codex/.harness-build.json +1 -1
  50. package/harness/bundles/general/codex/README.md +7 -7
  51. package/harness/bundles/general/codex/harness-archive/SKILL.md +35 -35
  52. package/harness/bundles/general/codex/harness-archive/checklist.md +5 -2
  53. package/harness/bundles/general/codex/harness-archive/reference.md +23 -7
  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 -4
  58. package/harness/bundles/general/codex/harness-review/SKILL.md +2 -2
  59. package/harness/bundles/general/codex/harness-run/SKILL.md +2 -2
  60. package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
  61. package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
  62. package/harness/bundles/general/codex/harness-test/SKILL.md +2 -2
  63. package/harness/bundles/general/codex/protocols/archive-report-protocol.md +7 -3
  64. package/harness/bundles/general/codex/protocols/ledger-protocol.md +1 -1
  65. package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +6 -4
  66. package/harness/bundles/general/codex/protocols/state-layout-protocol.md +3 -2
  67. package/harness/bundles/general/codex/scripts/harness_archive.py +829 -68
  68. package/harness/bundles/general/codex/scripts/harness_context.py +8 -2
  69. package/harness/bundles/general/codex/scripts/harness_events.py +5 -0
  70. package/harness/bundles/general/codex/scripts/harness_ledger.py +79 -2
  71. package/harness/bundles/general/codex/scripts/harness_profile.py +7 -0
  72. package/harness/bundles/general/codex/scripts/harness_state.py +22 -3
  73. package/harness/bundles/general/cursor/.harness-build.json +1 -1
  74. package/harness/bundles/general/cursor/README.md +7 -7
  75. package/harness/bundles/general/cursor/harness-archive/SKILL.md +35 -35
  76. package/harness/bundles/general/cursor/harness-archive/checklist.md +5 -2
  77. package/harness/bundles/general/cursor/harness-archive/reference.md +23 -7
  78. package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
  79. package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  80. package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
  81. package/harness/bundles/general/cursor/harness-plan/SKILL.md +4 -4
  82. package/harness/bundles/general/cursor/harness-review/SKILL.md +2 -2
  83. package/harness/bundles/general/cursor/harness-run/SKILL.md +2 -2
  84. package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
  85. package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
  86. package/harness/bundles/general/cursor/harness-test/SKILL.md +2 -2
  87. package/harness/bundles/general/cursor/protocols/archive-report-protocol.md +7 -3
  88. package/harness/bundles/general/cursor/protocols/ledger-protocol.md +1 -1
  89. package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +6 -4
  90. package/harness/bundles/general/cursor/protocols/state-layout-protocol.md +3 -2
  91. package/harness/bundles/general/cursor/scripts/harness_archive.py +829 -68
  92. package/harness/bundles/general/cursor/scripts/harness_context.py +8 -2
  93. package/harness/bundles/general/cursor/scripts/harness_events.py +5 -0
  94. package/harness/bundles/general/cursor/scripts/harness_ledger.py +79 -2
  95. package/harness/bundles/general/cursor/scripts/harness_profile.py +7 -0
  96. package/harness/bundles/general/cursor/scripts/harness_state.py +22 -3
  97. package/harness/bundles/java/claude-code/.harness-build.json +1 -1
  98. package/harness/bundles/java/claude-code/README.md +7 -7
  99. package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +2 -2
  100. package/harness/bundles/java/claude-code/harness-archive/SKILL.md +35 -35
  101. package/harness/bundles/java/claude-code/harness-archive/checklist.md +5 -2
  102. package/harness/bundles/java/claude-code/harness-archive/reference.md +23 -7
  103. package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
  104. package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
  105. package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
  106. package/harness/bundles/java/claude-code/harness-package/SKILL.md +2 -2
  107. package/harness/bundles/java/claude-code/harness-plan/SKILL.md +4 -4
  108. package/harness/bundles/java/claude-code/harness-review/SKILL.md +2 -2
  109. package/harness/bundles/java/claude-code/harness-run/SKILL.md +2 -2
  110. package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
  111. package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
  112. package/harness/bundles/java/claude-code/harness-test/SKILL.md +2 -2
  113. package/harness/bundles/java/claude-code/protocols/archive-report-protocol.md +7 -3
  114. package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +1 -1
  115. package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +6 -4
  116. package/harness/bundles/java/claude-code/protocols/state-layout-protocol.md +3 -2
  117. package/harness/bundles/java/claude-code/scripts/harness_archive.py +829 -68
  118. package/harness/bundles/java/claude-code/scripts/harness_context.py +8 -2
  119. package/harness/bundles/java/claude-code/scripts/harness_events.py +5 -0
  120. package/harness/bundles/java/claude-code/scripts/harness_ledger.py +79 -2
  121. package/harness/bundles/java/claude-code/scripts/harness_profile.py +7 -0
  122. package/harness/bundles/java/claude-code/scripts/harness_state.py +22 -3
  123. package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
  124. package/harness/bundles/java/codebuddy/README.md +7 -7
  125. package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +2 -2
  126. package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +35 -35
  127. package/harness/bundles/java/codebuddy/harness-archive/checklist.md +5 -2
  128. package/harness/bundles/java/codebuddy/harness-archive/reference.md +23 -7
  129. package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
  130. package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
  131. package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
  132. package/harness/bundles/java/codebuddy/harness-package/SKILL.md +2 -2
  133. package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +4 -4
  134. package/harness/bundles/java/codebuddy/harness-review/SKILL.md +2 -2
  135. package/harness/bundles/java/codebuddy/harness-run/SKILL.md +2 -2
  136. package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
  137. package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
  138. package/harness/bundles/java/codebuddy/harness-test/SKILL.md +2 -2
  139. package/harness/bundles/java/codebuddy/protocols/archive-report-protocol.md +7 -3
  140. package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +1 -1
  141. package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +6 -4
  142. package/harness/bundles/java/codebuddy/protocols/state-layout-protocol.md +3 -2
  143. package/harness/bundles/java/codebuddy/scripts/harness_archive.py +829 -68
  144. package/harness/bundles/java/codebuddy/scripts/harness_context.py +8 -2
  145. package/harness/bundles/java/codebuddy/scripts/harness_events.py +5 -0
  146. package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +79 -2
  147. package/harness/bundles/java/codebuddy/scripts/harness_profile.py +7 -0
  148. package/harness/bundles/java/codebuddy/scripts/harness_state.py +22 -3
  149. package/harness/bundles/java/codex/.harness-build.json +1 -1
  150. package/harness/bundles/java/codex/README.md +7 -7
  151. package/harness/bundles/java/codex/harness-apidoc/SKILL.md +2 -2
  152. package/harness/bundles/java/codex/harness-archive/SKILL.md +35 -35
  153. package/harness/bundles/java/codex/harness-archive/checklist.md +5 -2
  154. package/harness/bundles/java/codex/harness-archive/reference.md +23 -7
  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 +2 -2
  159. package/harness/bundles/java/codex/harness-plan/SKILL.md +4 -4
  160. package/harness/bundles/java/codex/harness-review/SKILL.md +2 -2
  161. package/harness/bundles/java/codex/harness-run/SKILL.md +2 -2
  162. package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
  163. package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
  164. package/harness/bundles/java/codex/harness-test/SKILL.md +2 -2
  165. package/harness/bundles/java/codex/protocols/archive-report-protocol.md +7 -3
  166. package/harness/bundles/java/codex/protocols/ledger-protocol.md +1 -1
  167. package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +6 -4
  168. package/harness/bundles/java/codex/protocols/state-layout-protocol.md +3 -2
  169. package/harness/bundles/java/codex/scripts/harness_archive.py +829 -68
  170. package/harness/bundles/java/codex/scripts/harness_context.py +8 -2
  171. package/harness/bundles/java/codex/scripts/harness_events.py +5 -0
  172. package/harness/bundles/java/codex/scripts/harness_ledger.py +79 -2
  173. package/harness/bundles/java/codex/scripts/harness_profile.py +7 -0
  174. package/harness/bundles/java/codex/scripts/harness_state.py +22 -3
  175. package/harness/bundles/java/cursor/.harness-build.json +1 -1
  176. package/harness/bundles/java/cursor/README.md +7 -7
  177. package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +2 -2
  178. package/harness/bundles/java/cursor/harness-archive/SKILL.md +35 -35
  179. package/harness/bundles/java/cursor/harness-archive/checklist.md +5 -2
  180. package/harness/bundles/java/cursor/harness-archive/reference.md +23 -7
  181. package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
  182. package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
  183. package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
  184. package/harness/bundles/java/cursor/harness-package/SKILL.md +2 -2
  185. package/harness/bundles/java/cursor/harness-plan/SKILL.md +4 -4
  186. package/harness/bundles/java/cursor/harness-review/SKILL.md +2 -2
  187. package/harness/bundles/java/cursor/harness-run/SKILL.md +2 -2
  188. package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
  189. package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
  190. package/harness/bundles/java/cursor/harness-test/SKILL.md +2 -2
  191. package/harness/bundles/java/cursor/protocols/archive-report-protocol.md +7 -3
  192. package/harness/bundles/java/cursor/protocols/ledger-protocol.md +1 -1
  193. package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +6 -4
  194. package/harness/bundles/java/cursor/protocols/state-layout-protocol.md +3 -2
  195. package/harness/bundles/java/cursor/scripts/harness_archive.py +829 -68
  196. package/harness/bundles/java/cursor/scripts/harness_context.py +8 -2
  197. package/harness/bundles/java/cursor/scripts/harness_events.py +5 -0
  198. package/harness/bundles/java/cursor/scripts/harness_ledger.py +79 -2
  199. package/harness/bundles/java/cursor/scripts/harness_profile.py +7 -0
  200. package/harness/bundles/java/cursor/scripts/harness_state.py +22 -3
  201. package/harness/manifests/general/claude-code.json +26 -26
  202. package/harness/manifests/general/codebuddy.json +26 -26
  203. package/harness/manifests/general/codex.json +26 -26
  204. package/harness/manifests/general/cursor.json +26 -26
  205. package/harness/manifests/java/claude-code.json +28 -28
  206. package/harness/manifests/java/codebuddy.json +28 -28
  207. package/harness/manifests/java/codex.json +28 -28
  208. package/harness/manifests/java/cursor.json +28 -28
  209. package/hunter-workflow-family.json +5 -5
  210. package/package.json +1 -1
@@ -810,10 +810,15 @@ def close_transition(
810
810
  artifact_entries: list[dict[str, Any]] = []
811
811
  for raw in artifacts or []:
812
812
  requested = Path(raw)
813
+ artifact_bases: list[Path] = []
814
+ for base in (project, contract_root, state_root):
815
+ resolved_base = base.resolve()
816
+ if resolved_base not in artifact_bases:
817
+ artifact_bases.append(resolved_base)
813
818
  candidates = (
814
819
  [requested]
815
820
  if requested.is_absolute()
816
- else [project / requested, contract_root / requested]
821
+ else [base / requested for base in artifact_bases]
817
822
  )
818
823
  valid: list[Path] = []
819
824
  for candidate in candidates:
@@ -838,10 +843,11 @@ def close_transition(
838
843
  "ok": False,
839
844
  "code": "TRANSITION_ARTIFACT_INVALID",
840
845
  "path": raw,
841
- "acceptedBases": [str(project), str(contract_root)],
846
+ "acceptedBases": [str(base) for base in artifact_bases],
842
847
  "examples": [
843
848
  str(contract_root / "meta" / "plan-finalization.json"),
844
849
  f".harness/changes/{change}/meta/plan-finalization.json",
850
+ f".harness/state/changes/{change}/evidence/verification-ledger.json",
845
851
  "meta/plan-finalization.json",
846
852
  ],
847
853
  }
@@ -1077,6 +1077,11 @@ def split_phase_attempts(phase_events: list[dict[str, Any]]) -> list[dict[str, A
1077
1077
  "external.wait",
1078
1078
  "issue",
1079
1079
  "ci.wait",
1080
+ # Preparation is intentionally outside the formal phase lifecycle.
1081
+ # A blocked preflight must remain visible in the raw timeline without
1082
+ # manufacturing an attempt or inflating the phase duration/count.
1083
+ "phase.prepare.start",
1084
+ "phase.prepare.end",
1080
1085
  }
1081
1086
  for event in phase_events:
1082
1087
  if event.get("type") == "phase.start":
@@ -1053,6 +1053,40 @@ def ledger_evidence_identity(ledger: dict[str, Any]) -> str:
1053
1053
  return "sha256:" + hashlib.sha256(canonical).hexdigest()
1054
1054
 
1055
1055
 
1056
+ def _immutable_change_base(
1057
+ change_dir: Path,
1058
+ project_root: Path,
1059
+ ) -> str:
1060
+ roots = [change_dir]
1061
+ try:
1062
+ state_root = harness_paths.resolve_state_dir_for_contract(
1063
+ change_dir,
1064
+ project_root,
1065
+ )
1066
+ if state_root.resolve() != change_dir.resolve():
1067
+ roots.insert(0, state_root)
1068
+ except (OSError, ValueError):
1069
+ pass
1070
+ for root in roots:
1071
+ for relative in (
1072
+ Path("meta") / "state-snapshot.json",
1073
+ Path("state-snapshot.json"),
1074
+ ):
1075
+ path = root / relative
1076
+ if not path.is_file():
1077
+ continue
1078
+ try:
1079
+ payload = json.loads(path.read_text(encoding="utf-8-sig"))
1080
+ except (OSError, json.JSONDecodeError, TypeError):
1081
+ continue
1082
+ if not isinstance(payload, dict):
1083
+ continue
1084
+ value = str(payload.get("changeBase") or "").strip()
1085
+ if value:
1086
+ return value
1087
+ return ""
1088
+
1089
+
1056
1090
  def migrate_ledger_for_write(
1057
1091
  change_dir: Path,
1058
1092
  ledger: dict[str, Any],
@@ -1089,6 +1123,15 @@ def migrate_ledger_for_write(
1089
1123
  if not _nonempty_str(resolved_base):
1090
1124
  resolved_base = ledger.get("baseCommit")
1091
1125
  if not _nonempty_str(resolved_base):
1126
+ resolved_base = _immutable_change_base(change_dir, repo_root)
1127
+ if not _nonempty_str(resolved_base):
1128
+ if record_migration:
1129
+ raise LedgerMigrationError(
1130
+ "immutable change base is missing; recapture the Plan boundary "
1131
+ "or pass an explicit --base-commit instead of using write-time HEAD"
1132
+ )
1133
+ # A genuinely new ledger establishes its identity at the first write.
1134
+ # Existing ledgers are handled above and may never take this fallback.
1092
1135
  resolved_base = current_head
1093
1136
  resolved_diff = diff_hash
1094
1137
  if not _nonempty_str(resolved_diff):
@@ -2795,9 +2838,43 @@ def cmd_diff_hash(args: argparse.Namespace) -> int:
2795
2838
  repo_raw = getattr(args, "repo", None)
2796
2839
  repo = Path(repo_raw).expanduser().resolve() if repo_raw else Path.cwd().resolve()
2797
2840
  base = getattr(args, "base", None)
2798
- change_dir = getattr(args, "change_dir", None)
2841
+ change_dir_raw = getattr(args, "change_dir", None)
2799
2842
  try:
2800
- diff_hash, meta = compute_diff_hash(repo, base=base, change_dir=change_dir)
2843
+ change_dir = resolve_path(change_dir_raw) if change_dir_raw else None
2844
+ if change_dir is not None and _contract_is_v2(change_dir):
2845
+ resolved_base = str(base or "").strip()
2846
+ if not resolved_base:
2847
+ existing, _ = load_ledger(change_dir)
2848
+ if isinstance(existing, dict):
2849
+ resolved_base = str(existing.get("baseCommit") or "").strip()
2850
+ if not resolved_base:
2851
+ resolved_base = _immutable_change_base(change_dir, repo)
2852
+ if not resolved_base:
2853
+ raise ValueError(
2854
+ "immutable change base is missing; capture the Plan boundary "
2855
+ "or pass --base"
2856
+ )
2857
+ detail = compute_ownership_diff(
2858
+ repo,
2859
+ base=resolved_base,
2860
+ change_dir=change_dir,
2861
+ )
2862
+ payload = {
2863
+ "ok": True,
2864
+ "action": "diff-hash",
2865
+ "identityScope": "change-ownership",
2866
+ "algorithmVersion": "ownership-content-changeset-1",
2867
+ "base": resolved_base,
2868
+ "head": _git_text(repo, "rev-parse", "--verify", "HEAD"),
2869
+ **detail,
2870
+ }
2871
+ emit_json(payload, as_json=as_json)
2872
+ return 0
2873
+ diff_hash, meta = compute_diff_hash(
2874
+ repo,
2875
+ base=base,
2876
+ change_dir=str(change_dir) if change_dir is not None else None,
2877
+ )
2801
2878
  except (OSError, RuntimeError, ValueError) as exc:
2802
2879
  return emit_error(str(exc), as_json=as_json)
2803
2880
  payload = {"ok": True, "action": "diff-hash", "diffHash": diff_hash}
@@ -838,13 +838,20 @@ def _node_commands(project: Path) -> dict[str, Any]:
838
838
  "src/**/*.tsx",
839
839
  "test/**/*.ts",
840
840
  "test/**/*.tsx",
841
+ "tests/**/*.ts",
842
+ "tests/**/*.tsx",
841
843
  "packages/*/src/**/*.ts",
842
844
  "packages/*/src/**/*.tsx",
843
845
  "packages/*/test/**/*.ts",
844
846
  "packages/*/test/**/*.tsx",
847
+ "packages/*/tests/**/*.ts",
848
+ "packages/*/tests/**/*.tsx",
845
849
  "apps/*/src/**/*.ts",
846
850
  "apps/*/src/**/*.tsx",
847
851
  "apps/*/test/**/*.ts",
852
+ "apps/*/test/**/*.tsx",
853
+ "apps/*/tests/**/*.ts",
854
+ "apps/*/tests/**/*.tsx",
848
855
  ]
849
856
  # harness dogfood: canonical Python + .mjs sources feed npm run check (vitest
850
857
  # imports harness-test/scripts; smoke:pack runs sync-harness.mjs).
@@ -119,6 +119,10 @@ def capture_snapshot(
119
119
  "schemaVersion": SNAPSHOT_SCHEMA_VERSION,
120
120
  "generatedAt": now_iso(),
121
121
  "changeName": change_name,
122
+ # The first captured Git boundary is the immutable identity of the
123
+ # change. Later phases may advance HEAD, but must never silently move
124
+ # the base and collapse the archive range.
125
+ "changeBase": base or "",
122
126
  "project": {"root": str(project.resolve())},
123
127
  "worktree": {"root": str(Path(worktree_root).resolve())},
124
128
  "git": {"base": base or "", "head": head or ""},
@@ -250,10 +254,25 @@ def capture_current_state(
250
254
  base: str = "",
251
255
  head: str = "HEAD",
252
256
  ) -> tuple[dict[str, Any], list[str]]:
253
- resolved_head = _git(project, "rev-parse", head) or head
254
- resolved_base = _git(project, "rev-parse", base) if base else ""
255
- files = discover_segment_files(project, change_dir, base=resolved_base, head=resolved_head)
257
+ git_head = _git(project, "rev-parse", head)
258
+ resolved_head = git_head or head
256
259
  previous = load_snapshot(change_dir)
260
+ previous_git = previous.get("git") if isinstance(previous, dict) else None
261
+ previous_base = (
262
+ str(previous.get("changeBase") or "").strip()
263
+ if isinstance(previous, dict)
264
+ else ""
265
+ )
266
+ if not previous_base and isinstance(previous_git, dict):
267
+ previous_base = str(
268
+ previous_git.get("base") or previous_git.get("baseCommit") or ""
269
+ ).strip()
270
+ requested_base = _git(project, "rev-parse", base) if base else ""
271
+ # First capture establishes the boundary. A later --base value is an
272
+ # observation hint only; changing the boundary requires the explicit
273
+ # archive range-adoption receipt.
274
+ resolved_base = previous_base or requested_base or git_head
275
+ files = discover_segment_files(project, change_dir, base=resolved_base, head=resolved_head)
257
276
  fresh = capture_snapshot(
258
277
  change_dir,
259
278
  change_name=change_name,
@@ -2,5 +2,5 @@
2
2
  "schemaVersion": 1,
3
3
  "overlay": "none",
4
4
  "agent": "codex",
5
- "coreHash": "af08899f70332067"
5
+ "coreHash": "af7bb8ab1a56ca6b"
6
6
  }
@@ -34,7 +34,7 @@
34
34
  | `protocols/state-layout-protocol.md` | 状态目录分层:`.harness/changes/<cn>/` 下按 `meta/`、`logs/`、`evidence/`、`reports/`、`scripts/`、`backups/` 子目录分层写入;读取时先读新路径再兼容旧路径 |
35
35
  | `protocols/submit-protocol.md` | 固定提交交互:提交方式与 commit message 确认使用固定选项模板,禁止 AI footer |
36
36
  | `protocols/archive-report-protocol.md` | 归档报告:`summary-data.json`、manifest/checksum 与真实性规则 |
37
- | `protocols/report-pipeline-protocol.md` | 报告流水线:`events.ndjson` + `harness_archive.py finalize/replay` + 数据一致性校验 |
37
+ | `protocols/report-pipeline-protocol.md` | 报告流水线:`events.ndjson` + `harness_archive.py execute/replay` + 数据一致性校验 |
38
38
 
39
39
  每个 skill 的 SKILL.md 关键规则章节都通过相对路径引用这些 protocol(`../protocols/powershell-protocol.md` 等)。
40
40
 
@@ -159,9 +159,9 @@
159
159
  - **Bash 拒绝、PowerShell 重试、降级、跳过、用户确认都必须记录**
160
160
  - 日志状态统一使用 `✅OK / 🟡WARN(原因) / ❌FAIL(原因)`
161
161
  - harness-archive 归档时从日志汇总:时间线、总用时、Skill 调用统计
162
- - `harness_archive.py finalize/replay` 优先从 `events.ndjson` 汇总命令、验证、artifact、问题和决策;旧 archive 缺少 events 时才回放 execution-log/ledger/manifest
162
+ - `harness_archive.py execute/replay` 优先从 `events.ndjson` 汇总命令、验证、artifact、问题和决策;旧 archive 缺少 events 时才回放 execution-log/ledger/manifest
163
163
  - sync 默认不关联具体变更目录,仅在已有变更目录时追加日志和 events
164
- - archive 是例外:finalize 内部负责且仅负责一次 `phase.start` / `phase.end`,调用者不得重复追加阶段边界
164
+ - archive 是例外:`execute` 负责一次准备记录和一次正式 `phase.start` / `phase.end`,调用者不得重复追加阶段边界,也不得串行运行 `auto-gate`、`status`、`finalize`
165
165
 
166
166
  ## Skill 目录结构
167
167
 
@@ -278,7 +278,7 @@ harness-skills/
278
278
  | **submit hash 记录** | pre-pull local hash + final pushed hash 双标注(主目录);worktree 模式 submit 段只本地 commit,合并段产生 `mergeFinalHash`,archive 以 `mergeFinalHash` 为准(无则回退 final pushed hash) |
279
279
  | **archive 阶段 1** | 归档前确认实际计划阶段的证据与结局;有 Git 时核对最终提交身份,无 Git、无 upstream 或跳过 submit 时使用内容清单身份。发布候选资格与本地封存分开判断;阶段边界由 finalize 单进程维护,调用者不额外 append |
280
280
  | **archive 文件移动** | 只用 PowerShell 或 Read+Write+验证,**禁止 Bash mv/cp/rm**;移动失败时不删除原目录 |
281
- | **archive 最终报告** | 必须运行 `harness_archive.py finalize` 生成权威 `summary-data.json` 并完成 validate。平台直接读取该 JSON;本地不生成重复的 HTML 报告。无测试或无 review 时必须标记 `NOT_RUN` / `ADVISORY_NOT_RUN`,禁止伪造 100% 通过率。状态演进须真实展示(✅OK / 🟡WARN / 🔁REUSED / 🔁RETESTED / 📝ADVISORY / 🧹NON_BEHAVIORAL_CLEANUP) |
281
+ | **archive 最终报告** | 必须运行 `harness_archive.py execute`,由内部 finalize 生成权威 `summary-data.json` 并完成 validate。平台直接读取该 JSON;本地不生成重复的 HTML 报告。无测试或无 review 时必须标记 `NOT_RUN` / `ADVISORY_NOT_RUN`,禁止伪造 100% 通过率。状态演进须真实展示(✅OK / 🟡WARN / 🔁REUSED / 🔁RETESTED / 📝ADVISORY / 🧹NON_BEHAVIORAL_CLEANUP) |
282
282
 
283
283
  ### 敏感信息脱敏
284
284
 
@@ -424,7 +424,7 @@ npx hunter-harness # 初始化阶段检查环境/CodeGraph/基础状态(非
424
424
  /harness-test # 运行测试(30条避坑规则)
425
425
  /harness-review # 代码审查
426
426
  /harness-submit # 提交代码(worktree 模式含合并;/harness-merge 为别名)
427
- /harness-archive # 归档产出(harness_archive.py finalize),释放工作区
427
+ /harness-archive # 归档产出(harness_archive.py execute),释放工作区
428
428
  ```
429
429
 
430
430
  ## 避坑知识来源
@@ -454,7 +454,7 @@ npx hunter-harness # 初始化阶段检查环境/CodeGraph/基础状态(非
454
454
  - `protocols/evidence-based-reporting-protocol.md`:证据化报告(✅/🟡/❌ 三态,已取代原 `harness-plan/evidence-based-reporting.md`,各 skill 引用此版本)。
455
455
  - `protocols/submit-protocol.md`:固定提交交互、中文 commit 文件、禁止 AI footer。
456
456
  - `protocols/archive-report-protocol.md`:summary-data、manifest/checksum 与平台归档展示合同。
457
- - `protocols/report-pipeline-protocol.md`:events.ndjson + `harness_archive.py finalize/replay` + summary-data 一致性校验。
457
+ - `protocols/report-pipeline-protocol.md`:events.ndjson + `harness_archive.py execute/replay` + summary-data 一致性校验。
458
458
  - `protocols/state-layout-protocol.md`:`.harness/changes/<cn>/` 子目录分层(meta/logs/evidence/reports/scripts/backups),读取先新后旧。
459
459
 
460
460
  各 skill 的 `SKILL.md` 只保留阶段目标和硬门禁,细节优先引用这些协议,避免在多个 skill 中复制并产生冲突。
@@ -475,4 +475,4 @@ backups/ 未提交但用于验证的测试文件等备份
475
475
 
476
476
  读取时先读新路径,再兼容旧路径。详见 `protocols/state-layout-protocol.md`。
477
477
 
478
- Archive 最终报告由 `harness_archive.py finalize` 生成 `summary-data.json`。Hunter Platform 直接读取该文件,避免本地重复渲染和两份报告数据不一致。
478
+ Archive 常规入口为 `harness_archive.py execute`,内部 finalize 生成 `summary-data.json`。Hunter Platform 直接读取该文件,避免本地重复渲染和两份报告数据不一致。
@@ -3,7 +3,7 @@ name: harness-archive
3
3
  description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到 .harness/archive/,含归档元数据。仅当用户显式调用
4
4
  /harness-archive 时使用;不得被其他阶段 skill 自动接续触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=af08899f70332067; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
7
7
  # harness-archive — 变更归档
8
8
 
9
9
  ## Purpose
@@ -29,7 +29,7 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
29
29
  - **产品候选验证**:
30
30
  - 有远端 CI:保留 `product-candidate-ci.json` 兼容证据;归档会把有效的 legacy schema v1 证据迁移为 `remote-claimed` 的 schema v2 `product-candidate-verification.json`,不会冒充 `remote-attested`。
31
31
  - `remote-attested` 必须同时带远端运行 URL 与 `verification.attestationDigest`;只有 URL、旧 JSON 哈希或 ledger 哈希只能达到 `remote-claimed`。
32
- - 无 CI:先运行 `harness_archive.py certify-local --change-dir ... --project . --json`,只复用身份一致且完整的 `unitTestFull` ledger 证据,不重复执行测试。
32
+ - 无 CI:`execute` 在候选收据缺失时自动尝试本地认证,只复用身份一致且完整的 `unitTestFull` ledger 证据,并在产品内容未变化时把提交前验证安全重绑定到当前 HEAD;认证不会执行测试。只有需要单独诊断认证结果时才手工运行 `certify-local`。
33
33
  - `release-candidate` 要求候选验证通过;正常本地完成或结束未完成变更使用 `--intent record-only`,固定 `releaseEligible=false`,但仍生成 ZIP 并上传平台。
34
34
 
35
35
  ## Inputs
@@ -46,61 +46,60 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
46
46
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
47
47
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
48
48
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
49
- 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
49
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --json`。首次 Plan 捕获时,脚本把当时的 Git HEAD 写入不可变 `changeBase`;后续阶段只刷新 `git.head` 与各段指纹,不得用当前 HEAD 覆盖 `changeBase`。只有迁移旧 change 且已能证明真实计划起点时,才在首次补录时传 `--base <baseCommit>`。`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
50
50
 
51
51
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
52
52
  > 片段:read-protocol
53
53
 
54
54
  ## Workflow
55
55
 
56
- 先用 `harness_change.py resolve [--change] --json` 解析 change;多个 active change 未显式选择时返回 `CHANGE_SELECTION_REQUIRED`,禁止按 Glob/mtime 自动选择。`harness_archive.py finalize` 内部负责且仅负责一次 `harness_gate.py begin` `harness_gate.py close`。调用者禁止重复调用阶段门禁,避免重复事件和幽灵 change 目录。
56
+ 先用 `harness_change.py resolve [--change] --json` 解析 change;多个 active change 未显式选择时返回 `CHANGE_SELECTION_REQUIRED`,禁止按 Glob/mtime 自动选择。常规路径只运行一次 `harness_archive.py execute`。该命令一次性完成状态采集、自动门禁和 finalize,并复用同一份预检结果。调用者不得再串行运行 `auto-gate`、`status` `finalize`,避免重复扫描、重复事件和幽灵 change 目录。
57
57
 
58
58
  ### Phase 0:读取规则和上下文
59
59
 
60
60
  1. 读取本文件。
61
- 2. 按需读取 `reference.md`(归档流程、manifest、summary-data、目录结构与最终状态规则)。
62
- 3. 读取共用约束:
63
- - `../protocols/archive-report-protocol.md`
64
- - `../protocols/state-layout-protocol.md`
65
- - `../protocols/powershell-protocol.md`
66
- - `../protocols/sensitive-info-protocol.md`
67
- - `../protocols/evidence-based-reporting-protocol.md`
68
- - `../protocols/report-pipeline-protocol.md`
69
- 4. 用 `harness_change.py resolve [--change] --json` 解析 `$ARGUMENTS`;多个 active change 时让用户选择。
61
+ 2. `harness_change.py resolve [--change] --json` 解析 `$ARGUMENTS`;多个 active change 时让用户选择。
62
+ 3. 按共享读取协议刷新一次状态快照。首次 Plan 已记录的 `changeBase` 必须保持不变,归档阶段不得把当前 HEAD 写回为基线。
63
+ 4. 常规路径不预读全部参考资料。仅在 `execute` 返回阻断或需要解释结果时,读取 `reference.md` 和对应协议章节。
70
64
 
71
65
  ### Phase 1:确认归档对象(扫描未归档变更)
72
66
 
73
- 使用 resolver 返回的 active changes 展示变更概要;同时读取 `plannedPhases`、已完成阶段、Git 能力、平台连接和待上传 ZIP 状态。
67
+ 使用 resolver 返回的 active changes 展示变更概要;同时读取 `plannedPhases`、已完成阶段和用户表达的生命周期结局。Git remote 与平台连接不是归档前置条件,不在这里额外探测。
74
68
 
75
- - **Read `checklist.md`** — 归档前检查项
76
69
  - 发现多个未归档变更 → 让用户选择或终止
77
70
 
78
- ### Phase 2:确认归档 / 自动门禁
71
+ ### Phase 2:确定归档结局
79
72
 
80
- 先运行 `harness_archive.py auto-gate --change-dir "<executionRoot>" --intent <intent> --json`。
73
+ 根据用户当前指令确定一次归档结局:
81
74
 
82
- - `autoArchiveAllowed=true`:记录 gate receipt,直接进入 Phase 3;无需 AskQuestion。
83
- - 否则用中文提供:①正常完成并归档(本地验证、不可发布);②作为发布候选归档;③结束未完成变更;④取消。选择③后继续选择“主动废弃”或“被其他方案替代”,并填写中文原因。不要把无 upstream 显示为阻止保存。
75
+ - 明确要求发布候选:`--intent release-candidate --closure completed`。
76
+ - 正常完成但不要求发布:`--intent record-only --closure completed`。
77
+ - 主动停止或被其他方案替代:`--intent record-only --closure abandoned|superseded --closure-reason "<中文原因>"`。
84
78
 
85
- - **Read `reference.md`** — 确认对话框的内容格式
79
+ 信息已经明确时,不重复询问。信息不足且会改变结局时,才用中文提供「正常完成」「发布候选」「主动废弃」「被其他方案替代」「取消」。无 upstream 不阻止保存。
86
80
 
87
81
  ### Phase 3:执行归档
88
82
 
89
- 1. 对“发布候选”使用 `--intent release-candidate --closure completed`;对正常本地完成使用 `--intent record-only --closure completed`;主动废弃或被替代分别使用 `--intent record-only --closure abandoned|superseded --closure-reason "<中文原因>"`。先运行 `status`;有可复用全量 ledger 时可运行 `certify-local`,不得重复测试。
90
- 2. `meta/archive-meta.md` **由 `harness_archive.py finalize` 自动生成**(与 summary-data `finalStatus` 同源);**禁止 agent 手写**该文件,手写视为数据丢失。维护者结论写入 events(decision/issue)即可,finalize 会汇总到 summary / archive-meta。
91
- 3. 运行 `harness_archive.py finalize --change-dir "<executionRoot>" --archive-root ".harness/archive" --intent <...> --closure <...> [--closure-reason "..."] --json`;读 JSON(cleanup、事件、移动、collect、validate、manifest、archive-meta、ZIP、上传与服务端知识状态)。finalize 内部只负责一次 `phase.start` / `phase.end`,调用者不得重复追加。**本地不执行知识 ingest**;服务端在 ZIP 持久保存并解包后 ingest。失败时保留原目录、ZIP 和回执。
83
+ 1. 不要为归档预先补跑测试或手工修 ledger。候选收据缺失时,`execute` 会先尝试复用身份一致的全量 ledger 完成本地认证;无法认证时保留结构化原因,`record-only` 仍按事实归档,`release-candidate` 则由发布门禁决定是否阻断。
84
+ 2. 运行一次 `harness_archive.py execute --change-dir "<executionRoot>" --archive-root ".harness/archive" --intent <...> --closure <...> [--closure-reason "..."] --json`。`execute` 只采集一次状态,并把准备耗时、正式归档耗时和唯一阶段轮次写入事件。调用者不得在它前后再运行 `auto-gate`、`status`、`finalize` 或手工追加 archive 阶段边界。
85
+ 3. `meta/archive-meta.md` `reports/final/summary-data.json` 均由脚本生成。禁止手写、补字段或手工修改 ledger 以绕过门禁。
86
+ 4. `execute` 失败时,先按 `reasonCode` 处理,不要盲目重跑:
87
+ - `ARCHIVE_BASE_EQUALS_FEATURE_TIP`:正常完成不允许空范围归档。仅可选择受控认领既有提交范围、改为未完成封存,或取消。
88
+ - 受控认领既有范围:先展示 base、tip 和产品文件清单;获得用户明确确认后运行 `adopt-existing-range --base <base> --tip <tip> --reason "<中文原因>" --confirm-existing-range --json`,再重跑一次 `execute`。禁止直接编辑 snapshot 或 ledger。
89
+ - `abandoned` / `superseded`:允许没有产品增量,但必须保存中文原因,并固定 `releaseEligible=false`。
90
+ - 其他阻断:读取 `reference.md` 中对应错误的恢复说明;原变更目录必须保留。
91
+
92
+ **本地不执行知识 ingest**;服务端在 ZIP 持久保存并解包后 ingest。失败时保留原目录、ZIP 和回执。
92
93
  - **归档包上传**:始终先生成一个确定性 ZIP;有远程凭据时再调用 `npx hunter-harness archive upload`。ZIP 仅包含 `summary-data.json`、`spec/**/*.md`、`plans/**/*.md`、`archive-meta.md`、`change-context.json` 和稳定 manifest;明确排除 logs、review/test 报告、HTML、缓存、备份、凭据和临时文件。
93
94
  - **失败可恢复**:无论远端凭据是否齐全,都先生成 ZIP 与 `<change-key>.upload.json`;上传或服务端 ingest 失败不破坏本地归档。待上传 ZIP 与逐 change 回执保留在 `.harness/state/local/archive-packages/`,可枚举独立重试。只有 CLI 核验 package hash 且服务端同时返回 `archive_status=durable`、`knowledge_status=ready` 后,才清理对应 ZIP 与回执;`indexing` 记为 pending,不记为失败。
94
95
  - **监控终态(C3)**:auto-upload 之后自动 `events-sync`,用归档前 change 路径派生的 `run_id` + 原 `change_key` 上报;失败只记 warning。
95
96
 
96
- - **Read `reference.md`** — finalize 输出字段、archive-meta 格式、CONDITIONAL_OK 规则
97
- - **Read `templates/summary-data-template.json`** — summary-data 数据结构
97
+ - **Read `reference.md`** — 仅在阻断恢复或需要解释输出字段时读取
98
+ - **Read `templates/summary-data-template.json`** — 仅在排查 summary 校验失败时读取
98
99
 
99
100
  ### Phase 4:验证与提示
100
101
 
101
- 验证归档目录完整 提示用户归档完成。
102
-
103
- - **Read `checklist.md`** — 归档后验证项
102
+ `execute` 返回的 manifest、ZIP、上传回执和 `finalStatus` 为权威结果。只有返回字段缺失或不一致时,才读取 `checklist.md` 做人工诊断。不要为了重复确认成功结果再次扫描整个目录。
104
103
 
105
104
  ## P0 执行可信度规则
106
105
 
@@ -124,7 +123,7 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
124
123
 
125
124
  ### 二、归档前确认或自动门禁
126
125
 
127
- `auto-gate` 只有在实际计划最后阶段终态、边界快照存在且 `status.archivable=true` 时才允许无确认执行。未满足时按 Phase 2 的四个中文选项确认;用户取消则不执行移动。
126
+ `execute` 内部运行自动门禁。实际计划最后阶段已终态、边界快照存在且 `status.archivable=true` 时直接执行;未满足时返回阻断且不移动原目录。用户取消后不得重跑。
128
127
 
129
128
  ### 三、文件移动只用内置工具或 PowerShell
130
129
 
@@ -149,6 +148,8 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
149
148
 
150
149
  归档前读 `evidence/verification-ledger.json`,提取各阶段 status、postTestClassification、复用关系,供 summary-data 真实记录状态演进。若 ledger 有 `postTestClassification`,summary-data 必须记录该分类及对应的复用/重测决策。
151
150
 
151
+ 门禁策略未声明 `database` 能力,且 `requiredValidations` / `requiredValidationsByPhase` 也未要求 `dbCompatibility` 时,数据库兼容性投影固定为 `NOT_APPLICABLE`,原因来自能力配置。不得要求纯前端、文档或无数据库项目伪造数据库证据。
152
+
152
153
  ### 八、summary-data 不得伪造且必须记录状态演进
153
154
 
154
155
  无测试报告 → 显示"未运行测试 / 静态验证",不得 100% 通过率;无 review → "📝ADVISORY:未运行 review"。状态用 ✅OK / 🟡WARN / 🔁REUSED / 🔁RETESTED / 📝ADVISORY / 🧹NON_BEHAVIORAL_CLEANUP,复用前一阶段结果显示 🔁REUSED,**不得伪装成重新执行,不得无脑全绿**。
@@ -165,7 +166,7 @@ API 测试 `USER_SKIPPED` 或 DB 兼容 `BLOCKED_BY_DBA` 时,最终状态必
165
166
 
166
167
  ### 十一、归档事件单一所有权
167
168
 
168
- finalize 内部负责且仅负责一次 `phase.start` / `phase.end`,并在移动后继续向归档目录中的同一事件流追加。调用者不得在 finalize 前后重复追加阶段边界,否则会造成重复阶段或在原 changes 路径生成幽灵目录。归档后 events.ndjson 与自动渲染的 execution-log.md 一起位于 archive。
169
+ `execute` 在一次性预检通过后负责且仅负责一次正式 `phase.start` / `phase.end`。准备阶段单独使用 `phase.prepare.start/end`,不计为归档重跑。调用者不得重复追加阶段边界。归档后 events.ndjson 与自动渲染的 execution-log.md 一起位于 archive。
169
170
 
170
171
  ### 十二、Shell 安全 / 敏感信息 / 证据化报告
171
172
 
@@ -188,14 +189,13 @@ git 命令通过 `powershell.exe -Command "..."` 执行;archive-meta.md 和 su
188
189
 
189
190
  ## 渐进披露
190
191
 
191
- - **Read `checklist.md`** 仅在 Phase 1 归档前检查和 Phase 4 验证时 — 含归档前检查项和归档后验证项
192
- - **Read `reference.md`** 仅在 Phase 0/2/3 时 — 含归档流程、archive-meta 格式、summary-data 字段说明、目录结构与最终状态规则
193
- - **Read `reference.md`** 仅在 Phase 3 finalize 时 — summary-data 校验与 archive-meta 补写
194
- - **Read `templates/summary-data-template.json`** 仅在 Phase 3 生成 `summary-data.json` 时 — 含最终报告数据结构
192
+ - **Read `checklist.md`** 仅在 `execute` 返回结果缺失或 manifest 不一致时
193
+ - **Read `reference.md`** 仅在阻断恢复、受控范围认领或解释最终状态时
194
+ - **Read `templates/summary-data-template.json`** 仅在 summary 校验失败时
195
195
 
196
196
  ## 交互白名单
197
197
 
198
- 仅当 `auto-gate` 未满足时,允许归档确认(Phase 2 blocking user confirmation);拒绝 → 终止,不执行任何移动。
198
+ 仅当生命周期结局不明确,或需要受控认领既有提交范围时,允许阻断式确认。拒绝后终止,不执行任何移动。
199
199
 
200
200
  ## 执行日志
201
201
 
@@ -32,9 +32,9 @@ description: harness-archive 的归档前检查项和归档后验证项。仅在
32
32
 
33
33
  ## 归档前检查(Phase 1)
34
34
 
35
- > ⚠️ **单一所有权**:finalize 内部负责且仅负责一次 `phase.start` / `phase.end`。归档前检查不得自行追加 archive 阶段边界。
35
+ > ⚠️ **单一所有权**:`execute` 负责一次准备记录和一次正式 `phase.start` / `phase.end`。归档前检查不得自行追加 archive 阶段边界。
36
36
 
37
- - [ ] 未在调用 finalize 前手工追加 archive 阶段边界
37
+ - [ ] 未在调用 `execute` 前手工追加 archive 阶段边界,也未串行调用 `auto-gate`、`status`、`finalize`
38
38
  - [ ] 只有一个未归档变更目录(多个时终止或让用户选择)
39
39
  - [ ] 已确认 `closure`:正常完成 / 主动废弃 / 被其他方案替代;后两者有中文 `closure-reason`
40
40
  - [ ] 正常完成按 `plannedPhases` 核对;提前结束只要求可识别变更与安全封存的最小材料
@@ -45,6 +45,9 @@ description: harness-archive 的归档前检查项和归档后验证项。仅在
45
45
  - [ ] Git 项目记录 dirty/HEAD/upstream;无 upstream 或仅本地 commit 允许归档,但 `releaseEligible=false`
46
46
  - [ ] 非 Git 项目生成确定性内容清单与产品树散列,并记录 `sourceControl=none`
47
47
  - [ ] 最终产品身份来自 Submit 收据或当前内容;提交前候选只允许在验证输入未变化时由 `certify-local` 自动重绑定
48
+ - [ ] `changeBase` 来自首次 Plan 快照,后续阶段未用当前 HEAD 覆盖;正常完成时 base 与产品 tip 不得相同
49
+ - [ ] 如使用既有提交范围,`meta/archive-range-adoption.json` 由 `adopt-existing-range --confirm-existing-range` 生成,未手工修改 ledger 或 snapshot
50
+ - [ ] 主动废弃或被替代允许没有产品增量,但必须有中文原因,且不得声明为发布候选
48
51
  - [ ] **test/review 报告状态确认**:
49
52
  - ✅ `.harness/changes/<change-name>/tests/test-report-*.md` 存在 → 归档正常
50
53
  - 🟡 不存在 → 必须在 archive-meta.md 和 summary-data.json 中标记「跳过测试」或「未运行测试」,不得伪造通过率
@@ -6,14 +6,30 @@ description: harness-archive 的归档流程、manifest、summary-data 校验、
6
6
 
7
7
  ## 归档流程(对齐 SKILL.md Workflow)
8
8
 
9
- - **Phase 0 读取上下文**:读 SKILL.md / 本文件 / 共用协议(`../protocols/archive-report-protocol.md`、`../protocols/report-pipeline-protocol.md`、`../protocols/state-layout-protocol.md`、`../protocols/powershell-protocol.md`、`../protocols/sensitive-info-protocol.md`、`../protocols/evidence-based-reporting-protocol.md`)/ 解析 `$ARGUMENTS`。
10
- - **Phase 1 确认归档对象**:Glob `.harness/changes/*/plans/*-plan.md`(排除 archive),展示概要;多变更让用户选择或终止。
11
- - **Phase 2 确认归档(强制阻断)**:blocking user confirmation 确认,拒绝即终止。
9
+ - **Phase 0 读取上下文**:读 SKILL.md,解析 `$ARGUMENTS`,刷新一次状态快照。常规路径不预读全部参考资料。
10
+ - **Phase 1 确认归档对象**:使用 `harness_change.py resolve` 解析 contract/state 双层目录;多变更让用户选择或终止。
11
+ - **Phase 2 确定归档结局**:已有明确指令时直接使用;只有结局不明确或需要认领既有提交范围时才询问。
12
12
  - **Phase 3 执行归档**:
13
- 1. 运行 `python <skills-root>/scripts/harness_archive.py status --change-dir ... --json` 做前置检查。
14
- 2. `meta/archive-meta.md` finalize 生成(与 summary `finalStatus` 同源);禁止手写。维护者结论写入 events 即可。finalize 在 before-manifest 前执行 cleanup(删除 lock/pid/launcher/credential,截断超大日志)。
15
- 3. 运行 `python <skills-root>/scripts/harness_archive.py finalize --change-dir ... --archive-root ".harness/archive" --json`;读 JSON 结果。finalize 内部负责且仅负责一次 `phase.start` / `phase.end`,调用者不得重复追加。**finalize 报错或 validate 失败时不删除原 changes 目录**。finalize 为冻结优先(freeze-first):collect 前先 fsync 事件并写 `evidence/evidence-cutoff.json`,cutoff 后不再追加事件;随后依次执行 source consistency(层 1,写入 `reportPipeline.sourceConsistency`)→ summary consistency(层 2),任一层失败即恢复原目录并非零退出。
16
- - **Phase 4 验证与提示**:见 `checklist.md` 归档后验证项。
13
+ 1. 运行一次 `python <skills-root>/scripts/harness_archive.py execute --change-dir ... --archive-root ".harness/archive" --intent <...> --closure <...> --json`。不要预先运行 `auto-gate` 或 `status`,也不要随后直接运行 `finalize`。候选收据缺失时,`execute` 会先尝试复用完整且身份一致的 `unitTestFull` ledger;产品内容未变化而 HEAD 因提交前移时,只重绑定候选身份,不重跑测试。
14
+ 2. `execute` 在同一进程中复用预检结果,并负责且仅负责一次正式 `phase.start` / `phase.end`。准备耗时单独记录。`meta/archive-meta.md` summary 均由 finalize 内部生成;禁止手写。
15
+ 3. finalize before-manifest 前执行 cleanup(删除 lock/pid/launcher/credential,截断超大日志)。它采用冻结优先:collect fsync 事件并写 `evidence/evidence-cutoff.json`;cutoff 后不再追加事件。source consistency summary consistency 失败时,脚本保留原 changes 目录并非零退出。
16
+ - **Phase 4 验证与提示**:优先使用 `execute` 返回的 manifest、ZIP 和上传回执;仅在结果缺失或不一致时读取 `checklist.md`。
17
+
18
+ 数据库兼容性由项目能力配置决定。门禁策略既未声明 `database` 能力,也未把 `dbCompatibility` 列为必需验证时,汇总结果为 `NOT_APPLICABLE`,并以 `capability-profile` 作为类型化来源;只有明确具备数据库能力或明确要求该验证时才检查数据库证据。
19
+
20
+ ## 基线折叠的恢复方式
21
+
22
+ `ARCHIVE_BASE_EQUALS_FEATURE_TIP` 表示正常完成的归档范围为空。不要修改 ledger、state snapshot 或制造空提交。按实际情况选择:
23
+
24
+ 1. 本次变更确实对应仓库中一段已有提交:展示 base、tip 和产品文件清单,获得用户明确确认后运行:
25
+
26
+ ```powershell
27
+ python <skills-root>/scripts/harness_archive.py adopt-existing-range --change-dir ".harness/changes/<change>" --base <base> --tip <tip> --reason "<中文原因>" --confirm-existing-range --json
28
+ ```
29
+
30
+ 命令生成不可覆盖的 `meta/archive-range-adoption.json`,并绑定仓库身份、ownership、diffHash 和文件清单。随后只重跑一次 `execute`。
31
+ 2. 需求未完成:使用 `--intent record-only --closure abandoned|superseded --closure-reason "<中文原因>"`。零产品增量作为 warning 保存,不阻止封存,且 `releaseEligible=false`。
32
+ 3. 无法证明范围或用户拒绝确认:取消归档,保留原目录。
17
33
 
18
34
  ## manifest 生成
19
35
 
@@ -3,7 +3,7 @@ name: harness-codebase-map
3
3
  description: 分析现有代码库并生成 .harness/codebase/map/
4
4
  下的结构化代码库地图。用于陌生项目理解、重大改造前梳理、harness-sync 提示 map 过期后的人工刷新。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=af08899f70332067; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
7
7
  # harness-codebase-map — 代码库地图
8
8
 
9
9
  ## Purpose
@@ -2,7 +2,7 @@
2
2
  name: harness-knowledge-ingest
3
3
  description: 确认归档 ZIP 已上传并由 Hunter Platform 在服务端解包、校验和入库。客户端不再构建或维护本地知识索引。
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=af08899f70332067; overlay=none; agent=codex; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
6
6
  # harness-knowledge-ingest
7
7
 
8
8
  知识 ingest 完全由 Hunter Platform 负责。归档完成时,客户端生成一个确定性 ZIP;
@@ -2,7 +2,7 @@
2
2
  name: harness-knowledge-query
3
3
  description: 在规划、实现或排查前,通过 hunter-harness CLI 查询远端项目知识库。远端不可用时直接报告不可用,不建立本地索引或离线回退。
4
4
  ---
5
- <!-- generated by harness_deploy.py; core=af08899f70332067; overlay=none; agent=codex; do not edit -->
5
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
6
6
  # harness-knowledge-query
7
7
 
8
8
  项目知识以 Hunter Platform 的服务端索引为唯一真源。客户端只提交查询并消费结果:
@@ -3,7 +3,7 @@ name: harness-plan
3
3
  description: 将需求转化为设计文档+实施计划+完整测试场景表,必须在编码前完成。仅当用户显式调用 /harness-plan 或明确要求进入
4
4
  Harness 规划阶段时使用;不得因用户描述需求就自动触发。
5
5
  ---
6
- <!-- generated by harness_deploy.py; core=af08899f70332067; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
7
7
  # harness-plan — 需求规划
8
8
 
9
9
  ## Purpose
@@ -28,7 +28,7 @@ description: 将需求转化为设计文档+实施计划+完整测试场景表
28
28
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
29
29
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
30
30
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
31
- 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
31
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --json`。首次 Plan 捕获时,脚本把当时的 Git HEAD 写入不可变 `changeBase`;后续阶段只刷新 `git.head` 与各段指纹,不得用当前 HEAD 覆盖 `changeBase`。只有迁移旧 change 且已能证明真实计划起点时,才在首次补录时传 `--base <baseCommit>`。`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
32
32
 
33
33
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
34
34
  > 片段:read-protocol · plan 额外写 `meta/worktree.json`、`meta/change-context.json`
@@ -66,7 +66,7 @@ description: 将需求转化为设计文档+实施计划+完整测试场景表
66
66
  | 阶段 | 动作 |
67
67
  |------|------|
68
68
  | 0 | 用当前解释器运行 `harness_runtime.py doctor`,后续消费绝对 argv;git status;脏工作区 → baseline 隔离 + `decision`,不询问 |
69
- | 0.5 | 确定英文 `change-name` 后,根据需求生成一次简洁的中文展示标题(建议 6~24 个可见字符,保留必要产品名),执行 `harness_context.py prepare --phase plan --executor <tool> [--change <id>] --title "<中文标题>" --json`;英文名继续作为目录与稳定标识。以其唯一 change/executionRoot 初始化 plan-run-id 与 attempt(首次为 1),用同一身份追加 `phase.start`;从第一条知识查询起保留事件证据,并在 finalizer 中复用该身份 |
69
+ | 0.5 | 确定英文 `change-name` 后,根据需求生成一次简洁的中文展示标题(建议 6~24 个可见字符,保留必要产品名),执行 `harness_context.py prepare --phase plan --executor <tool> [--change <id>] --title "<中文标题>" --json`;英文名继续作为目录与稳定标识。立即运行一次 `harness_state.py capture --project . --change-dir "<executionRoot>" --json`,把此时 HEAD 固定为不可变 `changeBase`。以其唯一 change/executionRoot 初始化 plan-run-id 与 attempt(首次为 1),用同一身份追加 `phase.start`;从第一条知识查询起保留事件证据,并在 finalizer 中复用该身份 |
70
70
  | 0.6 | 使用 `classify` 返回的默认阶段和项目能力生成 `plannedPhases`,向用户用中文说明可选阶段;确认后运行 `harness_context.py configure-plan --project . --change <id> --phases "plan,run,...,archive" --operator <tool> --reason "<中文原因>" --json`。无 Git 或不需要提交时不得加入 `submit`;快速迭代默认 `plan,run,archive`。省略项由脚本写入 `skippedPhases`,不得伪造阶段事件 |
71
71
  | 1 | `harness-knowledge-query` 单次远端 query(失败记 `issue`,不建立本地索引或离线回退) |
72
72
  | 2 | 歧义优先检查 + 复杂度分级;先确认会改变实现方向的语义歧义 |
@@ -107,7 +107,7 @@ change-name 范围变更 → 提示重命名或记 🟡WARN(→ `reference.md`
107
107
  | 简单修复探索预算 | 预计不超过 2 个代码文件、且不涉及认证/安全/迁移/并发/API 契约重设时,最多 1 次合并 CodeGraph 查询 + 1 次定向补查、1 个用户澄清问题;无关发现只记非阻断说明 |
108
108
  | 精简产物 | 简单修复只保留实现所需的设计、任务、边界和测试;禁止在 spec/plan/detail/scenarios 中重复同一背景和结论 |
109
109
  | 测试执行成本 | 场景表必须设计快速反馈层级、预计时长、资源预算、超时和可复用证据;默认先跑受影响测试,再跑模块门禁,候选验证只复用身份一致的全量证据 |
110
- | state snapshot | 读取 `state-snapshot.json`(`harness_state.py` / state-layout-protocol §state-snapshot.json)了解 project/worktree root、HEAD/base、profile/rules/map/knowledge 指纹;失效由脚本刷新,**不得仅凭缓存跳过代码探索或验证门禁**(design §3.6) |
110
+ | state snapshot | Plan 首次 capture 固定不可变 `changeBase`;后续刷新只更新 HEAD 和各段指纹,不得把当前 HEAD 写回为基线。读取 `state-snapshot.json` 了解 project/worktree root、HEAD/base、profile/rules/map/knowledge 指纹;失效由脚本刷新,**不得仅凭缓存跳过代码探索或验证门禁**(design §3.6) |
111
111
  | 协议 | sensitive-info / evidence-based-reporting / state-layout |
112
112
 
113
113
  产出物表、frontmatter、legacy 兼容、结束输出模板 → `reference.md`
@@ -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=af08899f70332067; overlay=none; agent=codex; do not edit -->
7
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
8
8
  # harness-review — 代码审查
9
9
 
10
10
  ## Purpose
@@ -38,7 +38,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
38
38
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
39
39
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
40
40
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
41
- 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
41
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --json`。首次 Plan 捕获时,脚本把当时的 Git HEAD 写入不可变 `changeBase`;后续阶段只刷新 `git.head` 与各段指纹,不得用当前 HEAD 覆盖 `changeBase`。只有迁移旧 change 且已能证明真实计划起点时,才在首次补录时传 `--base <baseCommit>`。`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
42
42
 
43
43
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
44
44
  > 片段:read-protocol
@@ -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=af08899f70332067; overlay=none; agent=codex; do not edit -->
6
+ <!-- generated by harness_deploy.py; core=af7bb8ab1a56ca6b; overlay=none; agent=codex; do not edit -->
7
7
  # harness-run — 需求编码
8
8
 
9
9
  ## Purpose
@@ -48,7 +48,7 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
48
48
  6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
49
49
  7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
50
50
  8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
51
- 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
51
+ 9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --json`。首次 Plan 捕获时,脚本把当时的 Git HEAD 写入不可变 `changeBase`;后续阶段只刷新 `git.head` 与各段指纹,不得用当前 HEAD 覆盖 `changeBase`。只有迁移旧 change 且已能证明真实计划起点时,才在首次补录时传 `--base <baseCommit>`。`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹,不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段,知识始终按需远端查询;快照不能替代源码读取和验证门禁。
52
52
 
53
53
  状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
54
54
  > 片段:read-protocol · run 必读文件优先级 → `reference.md` Step 0