@hunter-harness/workflow-harness 0.2.17 → 0.2.19
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.
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +7 -1
- package/harness/bundles/general/claude-code/harness-archive/checklist.md +6 -0
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/reference.md +5 -5
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/reference.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +11 -2
- package/harness/bundles/general/claude-code/harness-submit/checklist.md +24 -8
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +199 -30
- package/harness/bundles/general/claude-code/scripts/harness_events.py +5 -18
- package/harness/bundles/general/claude-code/scripts/harness_integration.py +153 -0
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +15 -2
- package/harness/bundles/general/claude-code/scripts/harness_paths.py +170 -1
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +1 -0
- package/harness/bundles/general/claude-code/scripts/harness_runtime.py +4 -4
- package/harness/bundles/general/claude-code/scripts/harness_service.py +1 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +7 -1
- package/harness/bundles/general/codebuddy/harness-archive/checklist.md +6 -0
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/reference.md +5 -5
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/reference.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +11 -2
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +24 -8
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +199 -30
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +5 -18
- package/harness/bundles/general/codebuddy/scripts/harness_integration.py +153 -0
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +15 -2
- package/harness/bundles/general/codebuddy/scripts/harness_paths.py +170 -1
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +1 -0
- package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +4 -4
- package/harness/bundles/general/codebuddy/scripts/harness_service.py +1 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +7 -1
- package/harness/bundles/general/codex/harness-archive/checklist.md +6 -0
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/reference.md +5 -5
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-run/reference.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +11 -2
- package/harness/bundles/general/codex/harness-submit/checklist.md +24 -8
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +199 -30
- package/harness/bundles/general/codex/scripts/harness_events.py +5 -18
- package/harness/bundles/general/codex/scripts/harness_integration.py +153 -0
- package/harness/bundles/general/codex/scripts/harness_ledger.py +15 -2
- package/harness/bundles/general/codex/scripts/harness_paths.py +170 -1
- package/harness/bundles/general/codex/scripts/harness_profile.py +1 -0
- package/harness/bundles/general/codex/scripts/harness_runtime.py +4 -4
- package/harness/bundles/general/codex/scripts/harness_service.py +1 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +7 -1
- package/harness/bundles/general/cursor/harness-archive/checklist.md +6 -0
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/reference.md +5 -5
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-run/reference.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +11 -2
- package/harness/bundles/general/cursor/harness-submit/checklist.md +24 -8
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +199 -30
- package/harness/bundles/general/cursor/scripts/harness_events.py +5 -18
- package/harness/bundles/general/cursor/scripts/harness_integration.py +153 -0
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +15 -2
- package/harness/bundles/general/cursor/scripts/harness_paths.py +170 -1
- package/harness/bundles/general/cursor/scripts/harness_profile.py +1 -0
- package/harness/bundles/general/cursor/scripts/harness_runtime.py +4 -4
- package/harness/bundles/general/cursor/scripts/harness_service.py +1 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +7 -1
- package/harness/bundles/java/claude-code/harness-archive/checklist.md +6 -0
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/reference.md +5 -5
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/checklist.md +2 -2
- package/harness/bundles/java/claude-code/harness-run/reference.md +352 -352
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +11 -2
- package/harness/bundles/java/claude-code/harness-submit/checklist.md +24 -8
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +199 -30
- package/harness/bundles/java/claude-code/scripts/harness_events.py +5 -18
- package/harness/bundles/java/claude-code/scripts/harness_integration.py +153 -0
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +15 -2
- package/harness/bundles/java/claude-code/scripts/harness_paths.py +170 -1
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +1 -0
- package/harness/bundles/java/claude-code/scripts/harness_runtime.py +4 -4
- package/harness/bundles/java/claude-code/scripts/harness_service.py +1 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +7 -1
- package/harness/bundles/java/codebuddy/harness-archive/checklist.md +6 -0
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/reference.md +5 -5
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/checklist.md +2 -2
- package/harness/bundles/java/codebuddy/harness-run/reference.md +352 -352
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +11 -2
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +24 -8
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +199 -30
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +5 -18
- package/harness/bundles/java/codebuddy/scripts/harness_integration.py +153 -0
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +15 -2
- package/harness/bundles/java/codebuddy/scripts/harness_paths.py +170 -1
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +1 -0
- package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +4 -4
- package/harness/bundles/java/codebuddy/scripts/harness_service.py +1 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +7 -1
- package/harness/bundles/java/codex/harness-archive/checklist.md +6 -0
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-plan/reference.md +5 -5
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/checklist.md +2 -2
- package/harness/bundles/java/codex/harness-run/reference.md +352 -352
- package/harness/bundles/java/codex/harness-submit/SKILL.md +11 -2
- package/harness/bundles/java/codex/harness-submit/checklist.md +24 -8
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/java/codex/scripts/harness_archive.py +199 -30
- package/harness/bundles/java/codex/scripts/harness_events.py +5 -18
- package/harness/bundles/java/codex/scripts/harness_integration.py +153 -0
- package/harness/bundles/java/codex/scripts/harness_ledger.py +15 -2
- package/harness/bundles/java/codex/scripts/harness_paths.py +170 -1
- package/harness/bundles/java/codex/scripts/harness_profile.py +1 -0
- package/harness/bundles/java/codex/scripts/harness_runtime.py +4 -4
- package/harness/bundles/java/codex/scripts/harness_service.py +1 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +7 -1
- package/harness/bundles/java/cursor/harness-archive/checklist.md +6 -0
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/reference.md +5 -5
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/checklist.md +2 -2
- package/harness/bundles/java/cursor/harness-run/reference.md +352 -352
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +11 -2
- package/harness/bundles/java/cursor/harness-submit/checklist.md +24 -8
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +2 -0
- package/harness/bundles/java/cursor/scripts/harness_archive.py +199 -30
- package/harness/bundles/java/cursor/scripts/harness_events.py +5 -18
- package/harness/bundles/java/cursor/scripts/harness_integration.py +153 -0
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +15 -2
- package/harness/bundles/java/cursor/scripts/harness_paths.py +170 -1
- package/harness/bundles/java/cursor/scripts/harness_profile.py +1 -0
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +4 -4
- package/harness/bundles/java/cursor/scripts/harness_service.py +1 -0
- package/harness/manifests/general/claude-code.json +26 -26
- package/harness/manifests/general/codebuddy.json +26 -26
- package/harness/manifests/general/codex.json +26 -26
- package/harness/manifests/general/cursor.json +26 -26
- package/harness/manifests/java/claude-code.json +29 -29
- package/harness/manifests/java/codebuddy.json +29 -29
- package/harness/manifests/java/codex.json +29 -29
- package/harness/manifests/java/cursor.json +29 -29
- package/hunter-workflow-family.json +1 -1
- package/package.json +1 -1
|
@@ -109,6 +109,10 @@ class CleanupResidualError(IntegrationError):
|
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
class AbandonRefusedError(IntegrationError):
|
|
113
|
+
code = "ABANDON_REFUSED"
|
|
114
|
+
|
|
115
|
+
|
|
112
116
|
class JournalConflictError(IntegrationError):
|
|
113
117
|
code = "JOURNAL_CONFLICT"
|
|
114
118
|
|
|
@@ -740,6 +744,8 @@ class IntegrationTransaction:
|
|
|
740
744
|
str(intg_root),
|
|
741
745
|
self.temp_branch,
|
|
742
746
|
)
|
|
747
|
+
# H-5: LF-stable hashing in the integration worktree.
|
|
748
|
+
self.runner.run(intg_root, "config", "core.autocrlf", "false")
|
|
743
749
|
|
|
744
750
|
return self._run_step("prepare", action)
|
|
745
751
|
|
|
@@ -1195,6 +1201,148 @@ class IntegrationTransaction:
|
|
|
1195
1201
|
harness_change.integration_lock_release(self.project_root, run_id=self.run_id)
|
|
1196
1202
|
return journal
|
|
1197
1203
|
|
|
1204
|
+
# ------------------------------------------------------------ abandon
|
|
1205
|
+
|
|
1206
|
+
def _remote_contains_merge(self, journal: dict[str, Any]) -> bool:
|
|
1207
|
+
merge_commit = str(journal.get("mergeCommit") or "")
|
|
1208
|
+
if not merge_commit:
|
|
1209
|
+
return False
|
|
1210
|
+
remote_line = self.runner.text(
|
|
1211
|
+
self.project_root, "ls-remote", "origin", self.target_branch
|
|
1212
|
+
)
|
|
1213
|
+
observed_remote = (
|
|
1214
|
+
remote_line.split()[0] if remote_line and remote_line.split() else ""
|
|
1215
|
+
)
|
|
1216
|
+
return bool(observed_remote and observed_remote == merge_commit)
|
|
1217
|
+
|
|
1218
|
+
def _force_remove_integration_root(
|
|
1219
|
+
self, journal: dict[str, Any], intg_root: Path
|
|
1220
|
+
) -> dict[str, Any]:
|
|
1221
|
+
"""Best-effort remove of the integration worktree for abandon only."""
|
|
1222
|
+
allowed = Path(str(journal.get("allowedCleanupRoot") or "")).resolve()
|
|
1223
|
+
resolved = harness_paths.assert_path_within(intg_root, self.temp_root)
|
|
1224
|
+
if not allowed or resolved != allowed:
|
|
1225
|
+
raise CleanupRefusedError(
|
|
1226
|
+
f"refusing to abandon non-transaction path: {resolved}"
|
|
1227
|
+
)
|
|
1228
|
+
self.runner.run(
|
|
1229
|
+
self.project_root,
|
|
1230
|
+
"worktree",
|
|
1231
|
+
"remove",
|
|
1232
|
+
"--force",
|
|
1233
|
+
str(resolved),
|
|
1234
|
+
check=False,
|
|
1235
|
+
)
|
|
1236
|
+
residual: list[str] = []
|
|
1237
|
+
if resolved.exists():
|
|
1238
|
+
for name in HEAVY_WORKTREE_ROOTS:
|
|
1239
|
+
heavy = resolved / name
|
|
1240
|
+
if heavy.exists():
|
|
1241
|
+
harness_paths.assert_path_within(heavy, resolved)
|
|
1242
|
+
try:
|
|
1243
|
+
if heavy.is_dir() and not heavy.is_symlink():
|
|
1244
|
+
shutil.rmtree(heavy)
|
|
1245
|
+
else:
|
|
1246
|
+
heavy.unlink()
|
|
1247
|
+
except OSError:
|
|
1248
|
+
residual.append(name)
|
|
1249
|
+
if resolved.exists():
|
|
1250
|
+
try:
|
|
1251
|
+
shutil.rmtree(resolved)
|
|
1252
|
+
except OSError:
|
|
1253
|
+
residual.append(str(resolved))
|
|
1254
|
+
listed = self.runner.text(self.project_root, "worktree", "list", "--porcelain")
|
|
1255
|
+
still_registered = str(resolved) in {
|
|
1256
|
+
str(Path(line.removeprefix("worktree ")).resolve())
|
|
1257
|
+
for line in (listed or "").splitlines()
|
|
1258
|
+
if line.startswith("worktree ")
|
|
1259
|
+
}
|
|
1260
|
+
ok = not still_registered and not residual and not resolved.exists()
|
|
1261
|
+
return {
|
|
1262
|
+
"ok": ok,
|
|
1263
|
+
"code": "CLEANUP_COMPLETE" if ok else "CLEANUP_RESIDUAL",
|
|
1264
|
+
"target": str(resolved),
|
|
1265
|
+
"worktreeRegistered": still_registered,
|
|
1266
|
+
"diskPathPresent": resolved.exists(),
|
|
1267
|
+
"residualPaths": residual,
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
def abandon(self) -> dict[str, Any]:
|
|
1271
|
+
"""Official recovery for a failed integration transaction (H-6).
|
|
1272
|
+
|
|
1273
|
+
Allowed only when push is not DONE and the remote does not already
|
|
1274
|
+
contain ``mergeCommit``. Removes the integration worktree, temp branch,
|
|
1275
|
+
protection refs, and integration lock. Never deletes the feature
|
|
1276
|
+
worktree or feature branch.
|
|
1277
|
+
"""
|
|
1278
|
+
try:
|
|
1279
|
+
journal = self._load()
|
|
1280
|
+
except FileNotFoundError as exc:
|
|
1281
|
+
raise AbandonRefusedError(
|
|
1282
|
+
f"no journal for transaction {self.transaction_id}"
|
|
1283
|
+
) from exc
|
|
1284
|
+
|
|
1285
|
+
push_status = str(self._step(journal, "push").get("status") or "")
|
|
1286
|
+
push_intent = journal.get("pushIntent") or {}
|
|
1287
|
+
push_confirmed = (
|
|
1288
|
+
push_status == "DONE"
|
|
1289
|
+
or (
|
|
1290
|
+
isinstance(push_intent, dict)
|
|
1291
|
+
and push_intent.get("status") == "REMOTE_CONFIRMED"
|
|
1292
|
+
)
|
|
1293
|
+
or bool(journal.get("pushedHead"))
|
|
1294
|
+
or bool(journal.get("mergeFinalHash"))
|
|
1295
|
+
)
|
|
1296
|
+
remote_has_merge = self._remote_contains_merge(journal)
|
|
1297
|
+
if push_confirmed or remote_has_merge:
|
|
1298
|
+
raise AbandonRefusedError(
|
|
1299
|
+
"refuse abandon: push succeeded or remote already contains "
|
|
1300
|
+
"the merge commit; use cleanup instead"
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
intg_root = Path(str(journal.get("integrationRoot") or ""))
|
|
1304
|
+
cleanup_result: dict[str, Any] = {
|
|
1305
|
+
"ok": True,
|
|
1306
|
+
"code": "ALREADY_ABSENT",
|
|
1307
|
+
"target": str(intg_root) if intg_root else "",
|
|
1308
|
+
"worktreeRegistered": False,
|
|
1309
|
+
"diskPathPresent": False,
|
|
1310
|
+
"residualPaths": [],
|
|
1311
|
+
}
|
|
1312
|
+
prepare_done = self._step(journal, "prepare").get("status") == "DONE"
|
|
1313
|
+
if prepare_done and str(journal.get("integrationRoot") or "").strip():
|
|
1314
|
+
try:
|
|
1315
|
+
cleanup_result = self.cleanup_target(intg_root)
|
|
1316
|
+
except CleanupRefusedError:
|
|
1317
|
+
cleanup_result = self._force_remove_integration_root(journal, intg_root)
|
|
1318
|
+
|
|
1319
|
+
self.runner.run(
|
|
1320
|
+
self.project_root, "branch", "-D", self.temp_branch, check=False
|
|
1321
|
+
)
|
|
1322
|
+
refs = journal.get("protectionRefs") or {}
|
|
1323
|
+
if isinstance(refs, dict):
|
|
1324
|
+
for ref in (refs.get("base"), refs.get("head")):
|
|
1325
|
+
if ref:
|
|
1326
|
+
self.runner.run(
|
|
1327
|
+
self.project_root, "update-ref", "-d", str(ref), check=False
|
|
1328
|
+
)
|
|
1329
|
+
|
|
1330
|
+
harness_change.integration_lock_release(self.project_root, run_id=self.run_id)
|
|
1331
|
+
|
|
1332
|
+
journal = self._load()
|
|
1333
|
+
journal["abandonedAt"] = now_iso()
|
|
1334
|
+
journal["abandonResult"] = cleanup_result
|
|
1335
|
+
self._save(journal)
|
|
1336
|
+
|
|
1337
|
+
return {
|
|
1338
|
+
"ok": True,
|
|
1339
|
+
"code": "ABANDON_COMPLETE",
|
|
1340
|
+
"transactionId": self.transaction_id,
|
|
1341
|
+
"cleanup": cleanup_result,
|
|
1342
|
+
"featureBranchRetained": True,
|
|
1343
|
+
"featureWorktreeRetained": True,
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1198
1346
|
# ------------------------------------------------------------ recover
|
|
1199
1347
|
|
|
1200
1348
|
def recover(
|
|
@@ -1282,6 +1430,10 @@ def cmd_cleanup(args: argparse.Namespace) -> int:
|
|
|
1282
1430
|
return _emit(_txn_from_args(args).cleanup())
|
|
1283
1431
|
|
|
1284
1432
|
|
|
1433
|
+
def cmd_abandon(args: argparse.Namespace) -> int:
|
|
1434
|
+
return _emit(_txn_from_args(args).abandon())
|
|
1435
|
+
|
|
1436
|
+
|
|
1285
1437
|
def cmd_recover(args: argparse.Namespace) -> int:
|
|
1286
1438
|
return _emit(_txn_from_args(args).recover())
|
|
1287
1439
|
|
|
@@ -1333,6 +1485,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1333
1485
|
add("merge", cmd_merge)
|
|
1334
1486
|
add("push", cmd_push)
|
|
1335
1487
|
add("cleanup", cmd_cleanup)
|
|
1488
|
+
add("abandon", cmd_abandon)
|
|
1336
1489
|
add("recover", cmd_recover)
|
|
1337
1490
|
add("status", cmd_status)
|
|
1338
1491
|
|
|
@@ -131,8 +131,19 @@ def emit_compact_or_verbose(
|
|
|
131
131
|
emit_json(out, as_json=as_json)
|
|
132
132
|
|
|
133
133
|
|
|
134
|
-
def emit_error(
|
|
135
|
-
|
|
134
|
+
def emit_error(
|
|
135
|
+
message: str,
|
|
136
|
+
*,
|
|
137
|
+
as_json: bool,
|
|
138
|
+
code: int = 1,
|
|
139
|
+
error_code: str | None = None,
|
|
140
|
+
extra: dict[str, Any] | None = None,
|
|
141
|
+
) -> int:
|
|
142
|
+
payload: dict[str, Any] = {"ok": False, "error": message}
|
|
143
|
+
if error_code:
|
|
144
|
+
payload["code"] = error_code
|
|
145
|
+
if extra:
|
|
146
|
+
payload.update(extra)
|
|
136
147
|
if as_json:
|
|
137
148
|
sys.stderr.write(json.dumps(payload, ensure_ascii=False) + "\n")
|
|
138
149
|
else:
|
|
@@ -1520,6 +1531,8 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
1520
1531
|
return emit_error(
|
|
1521
1532
|
"ledger identity incomplete: " + ", ".join(missing),
|
|
1522
1533
|
as_json=as_json,
|
|
1534
|
+
error_code="LEDGER_IDENTITY_INVALID",
|
|
1535
|
+
extra={"missing": missing},
|
|
1523
1536
|
)
|
|
1524
1537
|
write_ledger(out_path, ledger)
|
|
1525
1538
|
except (OSError, ValueError, FileNotFoundError, json.JSONDecodeError) as exc:
|
|
@@ -20,7 +20,7 @@ import re
|
|
|
20
20
|
import subprocess
|
|
21
21
|
import sys
|
|
22
22
|
from pathlib import Path
|
|
23
|
-
from typing import Any
|
|
23
|
+
from typing import Any, Sequence
|
|
24
24
|
|
|
25
25
|
SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
26
26
|
if str(SCRIPTS_DIR) not in sys.path:
|
|
@@ -289,6 +289,175 @@ def assert_path_within(
|
|
|
289
289
|
return resolved
|
|
290
290
|
|
|
291
291
|
|
|
292
|
+
class CleanupTopologyError(ValueError):
|
|
293
|
+
"""Raised when cleanup would delete or traverse into state/archive roots."""
|
|
294
|
+
|
|
295
|
+
code = "CLEANUP_TOPOLOGY_REFUSED"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# Skip descending into these when scanning for junction/symlink escapes.
|
|
299
|
+
# Matches integration HEAVY_WORKTREE_ROOTS plus .git (topology scan only).
|
|
300
|
+
CLEANUP_WALK_SKIP_DIRS = frozenset(
|
|
301
|
+
{
|
|
302
|
+
"node_modules",
|
|
303
|
+
".venv",
|
|
304
|
+
"venv",
|
|
305
|
+
"build",
|
|
306
|
+
"dist",
|
|
307
|
+
"target",
|
|
308
|
+
".cache",
|
|
309
|
+
"__pycache__",
|
|
310
|
+
".git",
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def assert_cleanup_safe(
|
|
316
|
+
cleanup_root: Path,
|
|
317
|
+
state_roots: Sequence[Path] | None = None,
|
|
318
|
+
archive_roots: Sequence[Path] | None = None,
|
|
319
|
+
) -> Path:
|
|
320
|
+
"""Refuse cleanup when state/archive roots resolve inside the cleanup tree.
|
|
321
|
+
|
|
322
|
+
Also refuses when a junction/symlink under ``cleanup_root`` resolves into a
|
|
323
|
+
protected state/archive root (Windows junction / symlink escape).
|
|
324
|
+
"""
|
|
325
|
+
cleanup = Path(cleanup_root).resolve()
|
|
326
|
+
protected: list[Path] = []
|
|
327
|
+
for raw in [*(state_roots or ()), *(archive_roots or ())]:
|
|
328
|
+
try:
|
|
329
|
+
protected.append(Path(raw).resolve())
|
|
330
|
+
except OSError as exc:
|
|
331
|
+
raise CleanupTopologyError(
|
|
332
|
+
f"CLEANUP_TOPOLOGY_REFUSED: cannot resolve protected path {raw}: {exc}"
|
|
333
|
+
) from exc
|
|
334
|
+
|
|
335
|
+
for prot in protected:
|
|
336
|
+
if prot == cleanup or prot.is_relative_to(cleanup):
|
|
337
|
+
raise CleanupTopologyError(
|
|
338
|
+
"CLEANUP_TOPOLOGY_REFUSED: protected path "
|
|
339
|
+
f"{prot} is inside cleanup root {cleanup}"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
if cleanup.is_dir():
|
|
343
|
+
for dirpath, dirnames, filenames in os.walk(cleanup, followlinks=False):
|
|
344
|
+
# Inspect this level (including heavy names) for link escapes, then
|
|
345
|
+
# prune descent so we do not walk install/cache trees.
|
|
346
|
+
entries = [*dirnames, *filenames]
|
|
347
|
+
dirnames[:] = [d for d in dirnames if d not in CLEANUP_WALK_SKIP_DIRS]
|
|
348
|
+
for name in entries:
|
|
349
|
+
child = Path(dirpath) / name
|
|
350
|
+
try:
|
|
351
|
+
is_link = child.is_symlink()
|
|
352
|
+
except OSError:
|
|
353
|
+
continue
|
|
354
|
+
# Junctions often report as directories; resolve and compare.
|
|
355
|
+
try:
|
|
356
|
+
resolved_child = child.resolve()
|
|
357
|
+
except OSError:
|
|
358
|
+
continue
|
|
359
|
+
escaped = not (
|
|
360
|
+
resolved_child == cleanup or resolved_child.is_relative_to(cleanup)
|
|
361
|
+
)
|
|
362
|
+
if not (is_link or escaped):
|
|
363
|
+
continue
|
|
364
|
+
for prot in protected:
|
|
365
|
+
if (
|
|
366
|
+
resolved_child == prot
|
|
367
|
+
or resolved_child.is_relative_to(prot)
|
|
368
|
+
or prot.is_relative_to(resolved_child)
|
|
369
|
+
):
|
|
370
|
+
raise CleanupTopologyError(
|
|
371
|
+
"CLEANUP_TOPOLOGY_REFUSED: cleanup path "
|
|
372
|
+
f"{child} resolves to protected {resolved_child}"
|
|
373
|
+
)
|
|
374
|
+
return cleanup
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
_FORMAL_SNAPSHOT_DIRS = ("plans", "spec", "evidence", "reports", "meta")
|
|
378
|
+
_FORMAL_SNAPSHOT_FILES = ("events.ndjson",)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def snapshot_change_formal_layer(
|
|
382
|
+
project_root: Path,
|
|
383
|
+
change_id: str,
|
|
384
|
+
*,
|
|
385
|
+
change_dir: Path | None = None,
|
|
386
|
+
) -> dict[str, Any]:
|
|
387
|
+
"""Copy change formal layer into ``.harness/cache/change-snapshots/<change>/``.
|
|
388
|
+
|
|
389
|
+
Destination is always under the main project / common harness root — never
|
|
390
|
+
under a feature worktree path alone.
|
|
391
|
+
"""
|
|
392
|
+
import shutil
|
|
393
|
+
|
|
394
|
+
project = resolve_main_project_root(Path(project_root))
|
|
395
|
+
src = Path(change_dir).resolve() if change_dir is not None else (
|
|
396
|
+
project / ".harness" / "changes" / change_id
|
|
397
|
+
).resolve()
|
|
398
|
+
if not src.is_dir():
|
|
399
|
+
raise FileNotFoundError(f"change dir not found for snapshot: {src}")
|
|
400
|
+
|
|
401
|
+
dest = (project / ".harness" / "cache" / "change-snapshots" / change_id).resolve()
|
|
402
|
+
# Hard guard: never write the snapshot inside the source tree itself.
|
|
403
|
+
if dest == src or dest.is_relative_to(src):
|
|
404
|
+
raise ValueError(
|
|
405
|
+
f"snapshot destination must not be inside change dir: {dest}"
|
|
406
|
+
)
|
|
407
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
408
|
+
|
|
409
|
+
copied: list[str] = []
|
|
410
|
+
for name in _FORMAL_SNAPSHOT_DIRS:
|
|
411
|
+
source_dir = src / name
|
|
412
|
+
if not source_dir.is_dir():
|
|
413
|
+
continue
|
|
414
|
+
target_dir = dest / name
|
|
415
|
+
shutil.copytree(source_dir, target_dir, dirs_exist_ok=True)
|
|
416
|
+
copied.append(name + "/")
|
|
417
|
+
for name in _FORMAL_SNAPSHOT_FILES:
|
|
418
|
+
source_file = src / name
|
|
419
|
+
if not source_file.is_file():
|
|
420
|
+
continue
|
|
421
|
+
shutil.copy2(source_file, dest / name)
|
|
422
|
+
copied.append(name)
|
|
423
|
+
|
|
424
|
+
file_hashes: dict[str, str] = {}
|
|
425
|
+
for path in sorted(dest.rglob("*")):
|
|
426
|
+
if not path.is_file():
|
|
427
|
+
continue
|
|
428
|
+
rel = path.relative_to(dest).as_posix()
|
|
429
|
+
if rel == "manifest.json":
|
|
430
|
+
continue
|
|
431
|
+
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
432
|
+
file_hashes[rel] = "sha256:" + digest
|
|
433
|
+
|
|
434
|
+
manifest = {
|
|
435
|
+
"schemaVersion": 1,
|
|
436
|
+
"changeId": change_id,
|
|
437
|
+
"source": str(src),
|
|
438
|
+
"destination": str(dest),
|
|
439
|
+
"projectRoot": str(project),
|
|
440
|
+
"copied": copied,
|
|
441
|
+
"files": file_hashes,
|
|
442
|
+
"fileCount": len(file_hashes),
|
|
443
|
+
}
|
|
444
|
+
manifest_path = dest / "manifest.json"
|
|
445
|
+
manifest_path.write_text(
|
|
446
|
+
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
|
447
|
+
encoding="utf-8",
|
|
448
|
+
newline="\n",
|
|
449
|
+
)
|
|
450
|
+
return {
|
|
451
|
+
"ok": True,
|
|
452
|
+
"code": "CHANGE_SNAPSHOT_WRITTEN",
|
|
453
|
+
"changeId": change_id,
|
|
454
|
+
"destination": str(dest),
|
|
455
|
+
"fileCount": len(file_hashes),
|
|
456
|
+
"copied": copied,
|
|
457
|
+
"manifestPath": str(manifest_path),
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
|
|
292
461
|
def cmd_resolve_layout(args: argparse.Namespace) -> int:
|
|
293
462
|
layout = resolve_change_layout(Path.cwd(), args.change)
|
|
294
463
|
sys.stdout.write(json.dumps(layout, ensure_ascii=False, indent=2) + "\n")
|
|
@@ -23,10 +23,10 @@ if hasattr(sys.stderr, "reconfigure"):
|
|
|
23
23
|
SCHEMA_VERSION = 1
|
|
24
24
|
_CHANGE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
|
|
25
25
|
_ADAPTERS = {
|
|
26
|
-
"claude-code": (".
|
|
27
|
-
"codex": (".
|
|
28
|
-
"cursor": (".
|
|
29
|
-
"codebuddy": (".
|
|
26
|
+
"claude-code": (".worktrees", "harness/"),
|
|
27
|
+
"codex": (".worktrees", "harness/"),
|
|
28
|
+
"cursor": (".worktrees", "harness/"),
|
|
29
|
+
"codebuddy": (".worktrees", "harness/"),
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
|
|
@@ -164,6 +164,7 @@ def get_service_start(profile: dict[str, Any]) -> dict[str, Any]:
|
|
|
164
164
|
# runtime resolve 把具体 overlay/profile 注入到 session,不写回持久 profile
|
|
165
165
|
# (spec §3.1 持久 profile 只保存模板;§3.4 修复输入端陈旧 profile)。
|
|
166
166
|
STALE_WORKTREE_MARKERS: tuple[str, ...] = (
|
|
167
|
+
".worktrees/",
|
|
167
168
|
".claude/worktrees/",
|
|
168
169
|
".cursor/worktrees/",
|
|
169
170
|
".codeium/worktrees/",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-apidoc
|
|
3
3
|
description: 从 Java 后端代码生成结构化前端 API 文档(Controller/VO/ErrorCode)。使用场景:接口文档、前端接口文档、API 文档生成
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-apidoc — 前端接口文档(Java overlay)
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-archive
|
|
|
3
3
|
description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到
|
|
4
4
|
.harness/archive/,含归档元数据和可视化最终总结。使用场景:归档、完成归档、收尾、打包产出物
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -33,6 +33,12 @@ description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档
|
|
|
33
33
|
- commit 已 push(`git log @{u}..HEAD` 输出为空)
|
|
34
34
|
- **最终 hash 来源**:worktree 模式(`meta/worktree.json` requested=true)下读 ledger 的 `mergeFinalHash`(由 `/harness-submit` 合并段写入);主目录模式读 submit 的 `final pushed hash`。当前 `git rev-parse HEAD` 须一致
|
|
35
35
|
- test 报告存在或标记"跳过";review 报告存在或标记"未运行 review"(review 不阻塞归档)
|
|
36
|
+
- **最小必备集(H-4 / `harness_archive.py status` blockers)**:
|
|
37
|
+
- `plans/*-plan.md`
|
|
38
|
+
- 非空 `events.ndjson`
|
|
39
|
+
- `evidence/verification-ledger.json`
|
|
40
|
+
- 至少一个 test 或 review 报告/证据
|
|
41
|
+
- 缺失任一项 → `archivable=false`;可从 `.harness/cache/change-snapshots/<change>/` 恢复正式层后再重试
|
|
36
42
|
|
|
37
43
|
## Inputs
|
|
38
44
|
|
|
@@ -20,6 +20,12 @@ description: harness-archive 的归档前检查项和归档后验证项。仅在
|
|
|
20
20
|
|
|
21
21
|
- [ ] 未在调用 finalize 前手工追加 archive 阶段边界
|
|
22
22
|
- [ ] 只有一个未归档变更目录(多个时终止或让用户选择)
|
|
23
|
+
- [ ] **最小必备集 blockers**(`harness_archive.py status`):
|
|
24
|
+
- [ ] `plans/*-plan.md` 存在
|
|
25
|
+
- [ ] `events.ndjson` 存在且非空
|
|
26
|
+
- [ ] `evidence/verification-ledger.json` 存在
|
|
27
|
+
- [ ] 至少一个 test 或 review 报告/证据
|
|
28
|
+
- [ ] 若缺失:优先从 `.harness/cache/change-snapshots/<change>/` 恢复,禁止空壳归档
|
|
23
29
|
- [ ] 变更目录下有 plans/ 子目录(至少有计划文件)
|
|
24
30
|
- [ ] `events.ndjson` 存在;执行日志允许由 finalize 从事件流重新渲染(旧 archive 才兼容根目录 `execution-log.md`)
|
|
25
31
|
- [ ] 准备生成 `archive-manifest-before.json`(path/size/sha256)
|
|
@@ -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=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-codebase-map — 代码库地图
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-knowledge-ingest
|
|
|
3
3
|
description: 从 .harness/archive 归档整理、同步和维护项目知识索引。适用场景:ingest knowledge、sync
|
|
4
4
|
knowledge、rebuild knowledge index、promote knowledge、确认知识条目、检查知识库是否过期。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-knowledge-ingest
|
|
8
8
|
|
|
9
9
|
从 `.harness/archive/**/reports/final/summary-data.json` 抽取项目知识,生成并维护本地 `.harness/knowledge/` 索引。
|
|
@@ -3,7 +3,7 @@ name: harness-knowledge-query
|
|
|
3
3
|
description: 根据新需求、设计任务、代码修改请求或问题排查,在规划和编码前查询 .harness/knowledge
|
|
4
4
|
历史上下文。适用场景:query knowledge、查找历史需求、根据归档理解需求、继续之前类似开发、生成需求上下文包。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-knowledge-query
|
|
8
8
|
|
|
9
9
|
并行 change 中查询必须传 `--change <change-id>`;成功后以该 change 的 `meta/knowledge-context.json` 为稳定指针。全局 `context-packs/latest.json` 仅向后兼容,不得用于判断多个 active change 的上下文归属。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-package
|
|
3
3
|
description: 增量模块打包:拉取最新→编译验证→mvn package→汇总 jar/war。使用场景:打包、package、发版准备
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-package — 增量模块打包(Java overlay)
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -10,7 +10,7 @@ description: harness-package 的8步工作流详细步骤、模块识别方法
|
|
|
10
10
|
|
|
11
11
|
如果 `$ARGUMENTS` 非空且与检测到的变更名不一致,以 `$ARGUMENTS` 为准。
|
|
12
12
|
|
|
13
|
-
**读取 worktree 状态并切换**:读 `.harness/changes/<change-name>/meta/worktree.json`(旧路径 `worktree.json` 兼容)。`requested=true` 且 `.
|
|
13
|
+
**读取 worktree 状态并切换**:读 `.harness/changes/<change-name>/meta/worktree.json`(旧路径 `worktree.json` 兼容)。`requested=true` 且 `.worktrees/<change-name>/` 已创建 → cd 到该 worktree 目录;`requested=true` 但 worktree 不存在 → 停止,提示先修复 `harness-run`,不得静默回主目录;`requested=false` → 在主目录执行。切换后检查构建配置文件(`.mvn/maven.config`、`settings-*.xml` 等)是否完整,缺失时从主目录复制。
|
|
14
14
|
|
|
15
15
|
**强制前置检查 — harness-test(硬门禁)和 harness-review(参考性)**:
|
|
16
16
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-plan
|
|
3
3
|
description: 将需求转化为设计文档+实施计划+完整测试场景表,必须在编码前完成。使用场景:需求分析、feature plan、技术方案设计、实现方案规划
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-plan — 需求规划
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -15,10 +15,10 @@ description: harness-plan 的需求提取模板、任务拆分规则、测试场
|
|
|
15
15
|
"requested": true,
|
|
16
16
|
"created": false,
|
|
17
17
|
"agent": "<active-adapter>",
|
|
18
|
-
"worktreeRoot": "
|
|
19
|
-
"branchPrefix": "
|
|
20
|
-
"path": "
|
|
21
|
-
"branch": "
|
|
18
|
+
"worktreeRoot": ".worktrees",
|
|
19
|
+
"branchPrefix": "harness/",
|
|
20
|
+
"path": ".worktrees/<change-name>",
|
|
21
|
+
"branch": "harness/<change-name>",
|
|
22
22
|
"decisionBy": "user",
|
|
23
23
|
"decisionAt": "YYYY-MM-DD HH:mm",
|
|
24
24
|
"ownerSkill": "harness-run"
|
|
@@ -41,7 +41,7 @@ description: harness-plan 的需求提取模板、任务拆分规则、测试场
|
|
|
41
41
|
|
|
42
42
|
### 决策事件 note 示例
|
|
43
43
|
|
|
44
|
-
`path` 与 `branch` 必须来自 `harness_runtime.py adapter`/`meta/runtime.json
|
|
44
|
+
`path` 与 `branch` 必须来自 `harness_runtime.py adapter`/`meta/runtime.json`。所有 agent 共享统一路径 `.worktrees/<change-name>` 与统一分支前缀 `harness/`;`agent` 字段仅作创建者元数据,不再决定路径。
|
|
45
45
|
|
|
46
46
|
`用户选择使用 Worktree;决策文件为 meta/worktree.json;requested=true, created=false;创建责任为 harness-run。`
|
|
47
47
|
|
|
@@ -3,7 +3,7 @@ name: harness-review
|
|
|
3
3
|
description: 6维度代码审查(架构/安全/规范/兼容/测试/性能),对照项目规则(见
|
|
4
4
|
.harness/context-index.json)和测试场景表,在隔离上下文运行。使用场景:代码审查、提交前检查、合并评审
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-review — 代码审查
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -8,7 +8,7 @@ description: harness-review 的6维度审查检查项详细列表。仅在执行
|
|
|
8
8
|
|
|
9
9
|
用 Glob 搜索 `.harness/changes/*/plans/*-plan.md`(**排除 `.harness/archive/*/`**),读取 frontmatter 提取 `change-name`。默认最多一个未归档变更;如有多个,优先取最近修改的,或询问用户。后续所有路径基于此变更名。
|
|
10
10
|
|
|
11
|
-
**读取 worktree 状态**:读 `.harness/changes/<change-name>/meta/worktree.json`。`requested=true` 且 worktree 已创建 → 后续 `git diff` 的 `<项目路径>` 用 worktree 路径(`.
|
|
11
|
+
**读取 worktree 状态**:读 `.harness/changes/<change-name>/meta/worktree.json`。`requested=true` 且 worktree 已创建 → 后续 `git diff` 的 `<项目路径>` 用 worktree 路径(`.worktrees/<change-name>`);`requested=true` 但 worktree 不存在 → 停止,提示先修复 `harness-run`,不得静默回主目录。
|
|
12
12
|
|
|
13
13
|
## 审查流程
|
|
14
14
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-run
|
|
3
3
|
description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。使用场景:开始编码、实现功能、写代码、TDD 编码
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=f01595d3142d34b7; overlay=java; agent=codebuddy; do not edit -->
|
|
6
6
|
# harness-run — 需求编码
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- [ ] `requested=true` 且 worktree 存在 → 切换到 worktree 执行
|
|
13
13
|
- [ ] `requested=true` 且 worktree 不存在 → 必须创建 worktree
|
|
14
14
|
- [ ] worktree 创建命令必须使用 PowerShell:`git worktree add ...`
|
|
15
|
-
- [ ] 创建后验证 `.
|
|
15
|
+
- [ ] 创建后验证 `.worktrees/<change-name>/.git` 存在
|
|
16
16
|
- [ ] 创建成功后更新 `worktree.json` 的 `created=true/createdAt/createdBy`
|
|
17
17
|
- [ ] 创建失败 → 停止,或 blocking user confirmation 询问是否改为主目录执行
|
|
18
18
|
- [ ] 禁止 `requested=true && worktree 不存在` 时直接主目录执行
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
|
|
197
197
|
> 仅在 worktree 中执行时触发此步骤。在主目录执行时跳过(标记 ⏭️主目录跳过)。
|
|
198
198
|
|
|
199
|
-
- [ ] 确认当前 cwd 在 `.
|
|
199
|
+
- [ ] 确认当前 cwd 在 `.worktrees/<change-name>/` 下
|
|
200
200
|
- [ ] 生成变更摘要:`git diff --stat` + `git diff --stat --cached`
|
|
201
201
|
- [ ] 构建 commit message:`wip(<scope>): <change-name> 编码完成 — N任务/M文件变更`
|
|
202
202
|
- [ ] **⚠️ 强制阻断**:用 blocking user confirmation 展示变更列表 + commit message,等待用户确认
|