@hunter-harness/workflow-harness 0.2.26 → 0.2.28
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 +1 -1
- package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/general/claude-code/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +8 -4
- package/harness/bundles/general/claude-code/harness-sync/reference.md +28 -0
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +2147 -189
- package/harness/bundles/general/claude-code/scripts/harness_change.py +8 -3
- package/harness/bundles/general/claude-code/scripts/harness_events.py +57 -9
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +530 -8
- package/harness/bundles/general/claude-code/scripts/harness_integration.py +263 -6
- package/harness/bundles/general/claude-code/scripts/harness_phase.py +758 -1
- package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/general/claude-code/scripts/harness_retry.py +175 -0
- package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +702 -22
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/general/codebuddy/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +8 -4
- package/harness/bundles/general/codebuddy/harness-sync/reference.md +28 -0
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +2147 -189
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +8 -3
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +57 -9
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +530 -8
- package/harness/bundles/general/codebuddy/scripts/harness_integration.py +263 -6
- package/harness/bundles/general/codebuddy/scripts/harness_phase.py +758 -1
- package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/general/codebuddy/scripts/harness_retry.py +175 -0
- package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +702 -22
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/general/codex/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +8 -4
- package/harness/bundles/general/codex/harness-sync/reference.md +28 -0
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +2147 -189
- package/harness/bundles/general/codex/scripts/harness_change.py +8 -3
- package/harness/bundles/general/codex/scripts/harness_events.py +57 -9
- package/harness/bundles/general/codex/scripts/harness_gate.py +530 -8
- package/harness/bundles/general/codex/scripts/harness_integration.py +263 -6
- package/harness/bundles/general/codex/scripts/harness_phase.py +758 -1
- package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/general/codex/scripts/harness_retry.py +175 -0
- package/harness/bundles/general/codex/scripts/harness_test_guard.py +702 -22
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/general/cursor/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +8 -4
- package/harness/bundles/general/cursor/harness-sync/reference.md +28 -0
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +2147 -189
- package/harness/bundles/general/cursor/scripts/harness_change.py +8 -3
- package/harness/bundles/general/cursor/scripts/harness_events.py +57 -9
- package/harness/bundles/general/cursor/scripts/harness_gate.py +530 -8
- package/harness/bundles/general/cursor/scripts/harness_integration.py +263 -6
- package/harness/bundles/general/cursor/scripts/harness_phase.py +758 -1
- package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/general/cursor/scripts/harness_retry.py +175 -0
- package/harness/bundles/general/cursor/scripts/harness_test_guard.py +702 -22
- 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 +1 -1
- package/harness/bundles/java/claude-code/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/java/claude-code/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +8 -4
- package/harness/bundles/java/claude-code/harness-sync/reference.md +28 -0
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +2147 -189
- package/harness/bundles/java/claude-code/scripts/harness_change.py +8 -3
- package/harness/bundles/java/claude-code/scripts/harness_events.py +57 -9
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +530 -8
- package/harness/bundles/java/claude-code/scripts/harness_integration.py +263 -6
- package/harness/bundles/java/claude-code/scripts/harness_phase.py +758 -1
- package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/java/claude-code/scripts/harness_retry.py +175 -0
- package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +702 -22
- 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 +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/java/codebuddy/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +8 -4
- package/harness/bundles/java/codebuddy/harness-sync/reference.md +28 -0
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +2147 -189
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +8 -3
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +57 -9
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +530 -8
- package/harness/bundles/java/codebuddy/scripts/harness_integration.py +263 -6
- package/harness/bundles/java/codebuddy/scripts/harness_phase.py +758 -1
- package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/java/codebuddy/scripts/harness_retry.py +175 -0
- package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +702 -22
- 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 +1 -1
- package/harness/bundles/java/codex/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/java/codex/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +8 -4
- package/harness/bundles/java/codex/harness-sync/reference.md +28 -0
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +2147 -189
- package/harness/bundles/java/codex/scripts/harness_change.py +8 -3
- package/harness/bundles/java/codex/scripts/harness_events.py +57 -9
- package/harness/bundles/java/codex/scripts/harness_gate.py +530 -8
- package/harness/bundles/java/codex/scripts/harness_integration.py +263 -6
- package/harness/bundles/java/codex/scripts/harness_phase.py +758 -1
- package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/java/codex/scripts/harness_retry.py +175 -0
- package/harness/bundles/java/codex/scripts/harness_test_guard.py +702 -22
- 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 +1 -1
- package/harness/bundles/java/cursor/harness-archive/templates/render-summary.mjs +25 -6
- package/harness/bundles/java/cursor/harness-archive/templates/summary-data-template.json +44 -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-ingest/scripts/harness_knowledge.py +493 -74
- 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-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +8 -4
- package/harness/bundles/java/cursor/harness-sync/reference.md +28 -0
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +2147 -189
- package/harness/bundles/java/cursor/scripts/harness_change.py +8 -3
- package/harness/bundles/java/cursor/scripts/harness_events.py +57 -9
- package/harness/bundles/java/cursor/scripts/harness_gate.py +530 -8
- package/harness/bundles/java/cursor/scripts/harness_integration.py +263 -6
- package/harness/bundles/java/cursor/scripts/harness_phase.py +758 -1
- package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +74 -0
- package/harness/bundles/java/cursor/scripts/harness_retry.py +175 -0
- package/harness/bundles/java/cursor/scripts/harness_test_guard.py +702 -22
- package/harness/manifests/general/claude-code.json +28 -24
- package/harness/manifests/general/codebuddy.json +28 -24
- package/harness/manifests/general/codex.json +28 -24
- package/harness/manifests/general/cursor.json +28 -24
- package/harness/manifests/java/claude-code.json +30 -26
- package/harness/manifests/java/codebuddy.json +30 -26
- package/harness/manifests/java/codex.json +30 -26
- package/harness/manifests/java/cursor.json +30 -26
- package/hunter-workflow-family.json +2 -2
- package/package.json +1 -1
|
@@ -51,12 +51,227 @@ _FOREIGN_PATTERNS = (
|
|
|
51
51
|
re.compile(r"^\.harness/changes/([^/]+)(/|$)"),
|
|
52
52
|
re.compile(r"^\.harness/state/changes/([^/]+)(/|$)"),
|
|
53
53
|
)
|
|
54
|
+
WORKTREE_LIFECYCLE_STATES = (
|
|
55
|
+
"REGISTERED",
|
|
56
|
+
"MATERIALIZED",
|
|
57
|
+
"AGENT_ATTACHED",
|
|
58
|
+
"ACTIVE",
|
|
59
|
+
"QUIESCING",
|
|
60
|
+
"AGENT_DETACHED",
|
|
61
|
+
"STATE_FINALIZED",
|
|
62
|
+
"REGISTRATION_REMOVED",
|
|
63
|
+
"RESIDUAL_CLEANED",
|
|
64
|
+
"RETIRED",
|
|
65
|
+
)
|
|
54
66
|
|
|
55
67
|
|
|
56
68
|
def now_iso() -> str:
|
|
57
69
|
return dt.datetime.now().astimezone().isoformat(timespec="milliseconds")
|
|
58
70
|
|
|
59
71
|
|
|
72
|
+
def _worktree_registrations(project_root: Path) -> dict[str, dict[str, Any]]:
|
|
73
|
+
proc = subprocess.run(
|
|
74
|
+
["git", "worktree", "list", "--porcelain"],
|
|
75
|
+
cwd=str(project_root),
|
|
76
|
+
capture_output=True,
|
|
77
|
+
text=True,
|
|
78
|
+
encoding="utf-8",
|
|
79
|
+
errors="replace",
|
|
80
|
+
check=False,
|
|
81
|
+
)
|
|
82
|
+
registrations: dict[str, dict[str, Any]] = {}
|
|
83
|
+
current: dict[str, Any] | None = None
|
|
84
|
+
for line in proc.stdout.splitlines() if proc.returncode == 0 else []:
|
|
85
|
+
if line.startswith("worktree "):
|
|
86
|
+
path = str(Path(line.removeprefix("worktree ")).resolve())
|
|
87
|
+
current = {"path": path, "branch": None, "head": None}
|
|
88
|
+
registrations[path] = current
|
|
89
|
+
elif current is not None and line.startswith("branch "):
|
|
90
|
+
current["branch"] = line.removeprefix("branch ")
|
|
91
|
+
elif current is not None and line.startswith("HEAD "):
|
|
92
|
+
current["head"] = line.removeprefix("HEAD ")
|
|
93
|
+
return registrations
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _active_capsules_for_root(
|
|
97
|
+
project_root: Path,
|
|
98
|
+
target_root: Path,
|
|
99
|
+
change_dir: Path | None,
|
|
100
|
+
) -> list[dict[str, Any]]:
|
|
101
|
+
candidates: list[Path] = []
|
|
102
|
+
change_dirs = [change_dir.resolve()] if change_dir is not None else []
|
|
103
|
+
if not change_dirs:
|
|
104
|
+
for root in (
|
|
105
|
+
project_root / ".harness" / "changes",
|
|
106
|
+
project_root / ".harness" / "state" / "changes",
|
|
107
|
+
):
|
|
108
|
+
if root.is_dir():
|
|
109
|
+
change_dirs.extend(path for path in root.iterdir() if path.is_dir())
|
|
110
|
+
for root in change_dirs:
|
|
111
|
+
for capsule_root in (
|
|
112
|
+
root / "runtime" / "phase-context",
|
|
113
|
+
root / "phase-context",
|
|
114
|
+
):
|
|
115
|
+
if capsule_root.is_dir():
|
|
116
|
+
candidates.extend(sorted(capsule_root.glob("*.json")))
|
|
117
|
+
active: list[dict[str, Any]] = []
|
|
118
|
+
for path in candidates:
|
|
119
|
+
try:
|
|
120
|
+
capsule = json.loads(path.read_text(encoding="utf-8-sig"))
|
|
121
|
+
except (OSError, json.JSONDecodeError):
|
|
122
|
+
continue
|
|
123
|
+
if not isinstance(capsule, dict):
|
|
124
|
+
continue
|
|
125
|
+
raw_root = str(capsule.get("executionRoot") or "").strip()
|
|
126
|
+
if not raw_root:
|
|
127
|
+
continue
|
|
128
|
+
try:
|
|
129
|
+
same_root = Path(raw_root).resolve() == target_root
|
|
130
|
+
except OSError:
|
|
131
|
+
same_root = False
|
|
132
|
+
transaction = capsule.get("closeTransaction")
|
|
133
|
+
transaction_status = (
|
|
134
|
+
str(transaction.get("status") or "").upper()
|
|
135
|
+
if isinstance(transaction, dict)
|
|
136
|
+
else ""
|
|
137
|
+
)
|
|
138
|
+
closed = transaction_status == "CLOSED" or bool(
|
|
139
|
+
capsule.get("closedAt") and capsule.get("closeStatus")
|
|
140
|
+
)
|
|
141
|
+
if same_root and not closed:
|
|
142
|
+
active.append(
|
|
143
|
+
{
|
|
144
|
+
"path": str(path),
|
|
145
|
+
"phase": capsule.get("phase"),
|
|
146
|
+
"runId": capsule.get("runId"),
|
|
147
|
+
"transactionStatus": transaction_status or "OPEN",
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
return active
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def evaluate_worktree_retirement(
|
|
154
|
+
project_root: Path,
|
|
155
|
+
target_root: Path,
|
|
156
|
+
*,
|
|
157
|
+
change_dir: Path | None = None,
|
|
158
|
+
agent_root: Path | None = None,
|
|
159
|
+
junction_dependencies: list[dict[str, Any]] | None = None,
|
|
160
|
+
) -> dict[str, Any]:
|
|
161
|
+
"""Journal a unified, read-only preflight for safe worktree retirement."""
|
|
162
|
+
project_root = project_root.resolve()
|
|
163
|
+
target_root = target_root.resolve()
|
|
164
|
+
registrations = _worktree_registrations(project_root)
|
|
165
|
+
registration = registrations.get(str(target_root))
|
|
166
|
+
active_capsules = _active_capsules_for_root(
|
|
167
|
+
project_root,
|
|
168
|
+
target_root,
|
|
169
|
+
change_dir,
|
|
170
|
+
)
|
|
171
|
+
if junction_dependencies is None:
|
|
172
|
+
junction_path = (
|
|
173
|
+
project_root
|
|
174
|
+
/ ".harness"
|
|
175
|
+
/ "config"
|
|
176
|
+
/ "junction-dependencies.json"
|
|
177
|
+
)
|
|
178
|
+
try:
|
|
179
|
+
loaded = json.loads(junction_path.read_text(encoding="utf-8-sig"))
|
|
180
|
+
except (OSError, json.JSONDecodeError):
|
|
181
|
+
loaded = []
|
|
182
|
+
junction_dependencies = loaded if isinstance(loaded, list) else []
|
|
183
|
+
matching_junctions = []
|
|
184
|
+
for item in junction_dependencies:
|
|
185
|
+
if not isinstance(item, dict):
|
|
186
|
+
continue
|
|
187
|
+
try:
|
|
188
|
+
matches = Path(str(item.get("target") or "")).resolve() == target_root
|
|
189
|
+
except OSError:
|
|
190
|
+
matches = False
|
|
191
|
+
if matches:
|
|
192
|
+
matching_junctions.append(item)
|
|
193
|
+
attached = False
|
|
194
|
+
if agent_root is not None:
|
|
195
|
+
try:
|
|
196
|
+
resolved_agent = agent_root.resolve()
|
|
197
|
+
attached = (
|
|
198
|
+
resolved_agent == target_root
|
|
199
|
+
or target_root in resolved_agent.parents
|
|
200
|
+
)
|
|
201
|
+
except OSError:
|
|
202
|
+
attached = True
|
|
203
|
+
blockers: list[dict[str, Any]] = []
|
|
204
|
+
if active_capsules:
|
|
205
|
+
blockers.append(
|
|
206
|
+
{
|
|
207
|
+
"code": "ACTIVE_PHASE_CAPSULE",
|
|
208
|
+
"message": "phase capsules still reference the worktree",
|
|
209
|
+
"capsules": active_capsules,
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
if attached:
|
|
213
|
+
blockers.append(
|
|
214
|
+
{
|
|
215
|
+
"code": "AGENT_STILL_ATTACHED",
|
|
216
|
+
"message": "Agent root/cwd still points into the worktree",
|
|
217
|
+
"agentRoot": str(agent_root.resolve()) if agent_root else None,
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
if matching_junctions:
|
|
221
|
+
blockers.append(
|
|
222
|
+
{
|
|
223
|
+
"code": "JUNCTION_DEPENDENCY_ACTIVE",
|
|
224
|
+
"message": "junction dependencies must be removed or migrated",
|
|
225
|
+
"junctions": matching_junctions,
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
receipt = {
|
|
229
|
+
"schemaVersion": 1,
|
|
230
|
+
"lifecycleId": hashlib.sha256(
|
|
231
|
+
str(target_root).encode("utf-8")
|
|
232
|
+
).hexdigest()[:20],
|
|
233
|
+
"projectRoot": str(project_root),
|
|
234
|
+
"targetRoot": str(target_root),
|
|
235
|
+
"state": "ACTIVE" if blockers else "QUIESCING",
|
|
236
|
+
"allowedStates": list(WORKTREE_LIFECYCLE_STATES),
|
|
237
|
+
"registration": registration,
|
|
238
|
+
"diskPathPresent": target_root.exists(),
|
|
239
|
+
"agentAttached": attached,
|
|
240
|
+
"activeCapsules": active_capsules,
|
|
241
|
+
"junctionDependencies": matching_junctions,
|
|
242
|
+
"blockers": blockers,
|
|
243
|
+
"ok": not blockers,
|
|
244
|
+
"code": (
|
|
245
|
+
"WORKTREE_RETIREMENT_READY"
|
|
246
|
+
if not blockers
|
|
247
|
+
else "WORKTREE_RETIREMENT_BLOCKED"
|
|
248
|
+
),
|
|
249
|
+
"updatedAt": now_iso(),
|
|
250
|
+
}
|
|
251
|
+
receipt_path = (
|
|
252
|
+
project_root
|
|
253
|
+
/ ".harness"
|
|
254
|
+
/ "runtime"
|
|
255
|
+
/ "worktree-lifecycle"
|
|
256
|
+
/ f"{receipt['lifecycleId']}.json"
|
|
257
|
+
)
|
|
258
|
+
receipt_path.parent.mkdir(parents=True, exist_ok=True)
|
|
259
|
+
temporary = receipt_path.with_name(
|
|
260
|
+
f".{receipt_path.name}.{os.getpid()}.tmp"
|
|
261
|
+
)
|
|
262
|
+
try:
|
|
263
|
+
temporary.write_text(
|
|
264
|
+
json.dumps(receipt, ensure_ascii=False, indent=2) + "\n",
|
|
265
|
+
encoding="utf-8",
|
|
266
|
+
newline="\n",
|
|
267
|
+
)
|
|
268
|
+
os.replace(temporary, receipt_path)
|
|
269
|
+
finally:
|
|
270
|
+
temporary.unlink(missing_ok=True)
|
|
271
|
+
receipt["receiptPath"] = str(receipt_path)
|
|
272
|
+
return receipt
|
|
273
|
+
|
|
274
|
+
|
|
60
275
|
def _split_verify_command(raw: str, *, platform: str = sys.platform) -> list[str]:
|
|
61
276
|
# Harness profile/CLI commands use one portable quoting contract. Keep
|
|
62
277
|
# POSIX tokenization on Windows too; executable normalization happens later.
|
|
@@ -243,6 +458,26 @@ def _sanitize(value: str) -> str:
|
|
|
243
458
|
return re.sub(r"[^A-Za-z0-9._-]+", "-", value).strip("-") or "txn"
|
|
244
459
|
|
|
245
460
|
|
|
461
|
+
def _compact_transaction_id(change_id: str, run_id: str, *, limit: int = 48) -> str:
|
|
462
|
+
readable = f"{_sanitize(change_id)}-{_sanitize(run_id)}"
|
|
463
|
+
if len(readable) <= limit:
|
|
464
|
+
return readable
|
|
465
|
+
digest = hashlib.sha256(readable.encode("utf-8")).hexdigest()[:12]
|
|
466
|
+
prefix = _sanitize(change_id)[: max(8, limit - len(digest) - 1)]
|
|
467
|
+
return f"{prefix}-{digest}"
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def default_temp_root(project_root: Path) -> Path:
|
|
471
|
+
"""Use a deliberately short Windows worktree root to protect MAX_PATH."""
|
|
472
|
+
configured = os.environ.get("HUNTER_HARNESS_INTEGRATION_ROOT")
|
|
473
|
+
if configured:
|
|
474
|
+
return Path(configured).expanduser().resolve()
|
|
475
|
+
resolved = Path(project_root).resolve()
|
|
476
|
+
if os.name == "nt" and resolved.drive:
|
|
477
|
+
return Path(resolved.drive + os.sep) / "hh-int"
|
|
478
|
+
return resolved / ".harness" / "runtime" / "i"
|
|
479
|
+
|
|
480
|
+
|
|
246
481
|
def journal_dir(project_root: Path, transaction_id: str) -> Path:
|
|
247
482
|
return (
|
|
248
483
|
Path(project_root)
|
|
@@ -419,7 +654,7 @@ class IntegrationTransaction:
|
|
|
419
654
|
self.feature_branch = feature_branch
|
|
420
655
|
self.temp_root = Path(temp_root).resolve()
|
|
421
656
|
self.runner = runner or GitRunner()
|
|
422
|
-
self.transaction_id =
|
|
657
|
+
self.transaction_id = _compact_transaction_id(change_id, run_id)
|
|
423
658
|
self.temp_branch = f"harness/integration/{self.transaction_id}"
|
|
424
659
|
|
|
425
660
|
# ------------------------------------------------------------- journal
|
|
@@ -1249,6 +1484,23 @@ class IntegrationTransaction:
|
|
|
1249
1484
|
raise CleanupRefusedError(
|
|
1250
1485
|
f"refusing to clean unregistered worktree path: {resolved}"
|
|
1251
1486
|
)
|
|
1487
|
+
lifecycle = evaluate_worktree_retirement(
|
|
1488
|
+
self.project_root,
|
|
1489
|
+
resolved,
|
|
1490
|
+
agent_root=(
|
|
1491
|
+
Path(os.environ["HUNTER_HARNESS_AGENT_ROOT"])
|
|
1492
|
+
if os.environ.get("HUNTER_HARNESS_AGENT_ROOT")
|
|
1493
|
+
else None
|
|
1494
|
+
),
|
|
1495
|
+
)
|
|
1496
|
+
if not lifecycle.get("ok"):
|
|
1497
|
+
codes = ", ".join(
|
|
1498
|
+
str(item.get("code") or "UNKNOWN")
|
|
1499
|
+
for item in lifecycle.get("blockers") or []
|
|
1500
|
+
)
|
|
1501
|
+
raise CleanupRefusedError(
|
|
1502
|
+
f"worktree lifecycle blocks cleanup: {codes}"
|
|
1503
|
+
)
|
|
1252
1504
|
|
|
1253
1505
|
heavy_removed: list[str] = []
|
|
1254
1506
|
heavy_failures: list[dict[str, str]] = []
|
|
@@ -1592,13 +1844,18 @@ class IntegrationTransaction:
|
|
|
1592
1844
|
|
|
1593
1845
|
|
|
1594
1846
|
def _txn_from_args(args: argparse.Namespace) -> IntegrationTransaction:
|
|
1847
|
+
project_root = Path.cwd()
|
|
1595
1848
|
return IntegrationTransaction(
|
|
1596
|
-
project_root=
|
|
1849
|
+
project_root=project_root,
|
|
1597
1850
|
change_id=args.change,
|
|
1598
1851
|
run_id=args.run_id,
|
|
1599
1852
|
target_branch=args.target_branch,
|
|
1600
1853
|
feature_branch=args.feature_branch,
|
|
1601
|
-
temp_root=
|
|
1854
|
+
temp_root=(
|
|
1855
|
+
Path(args.temp_root)
|
|
1856
|
+
if getattr(args, "temp_root", None)
|
|
1857
|
+
else default_temp_root(project_root)
|
|
1858
|
+
),
|
|
1602
1859
|
)
|
|
1603
1860
|
|
|
1604
1861
|
|
|
@@ -1693,7 +1950,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1693
1950
|
p.add_argument("--run-id", required=True)
|
|
1694
1951
|
p.add_argument("--target-branch", default="main")
|
|
1695
1952
|
p.add_argument("--feature-branch", required=True)
|
|
1696
|
-
p.add_argument("--temp-root"
|
|
1953
|
+
p.add_argument("--temp-root")
|
|
1697
1954
|
p.set_defaults(func=func)
|
|
1698
1955
|
|
|
1699
1956
|
add("preflight", cmd_preflight)
|
|
@@ -1710,7 +1967,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1710
1967
|
p_journal.add_argument("--run-id", required=True)
|
|
1711
1968
|
p_journal.add_argument("--target-branch", default="main")
|
|
1712
1969
|
p_journal.add_argument("--feature-branch", required=True)
|
|
1713
|
-
p_journal.add_argument("--temp-root"
|
|
1970
|
+
p_journal.add_argument("--temp-root")
|
|
1714
1971
|
p_journal.add_argument(
|
|
1715
1972
|
"--verbose",
|
|
1716
1973
|
action="store_true",
|
|
@@ -1723,7 +1980,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1723
1980
|
p_verify.add_argument("--run-id", required=True)
|
|
1724
1981
|
p_verify.add_argument("--target-branch", default="main")
|
|
1725
1982
|
p_verify.add_argument("--feature-branch", required=True)
|
|
1726
|
-
p_verify.add_argument("--temp-root"
|
|
1983
|
+
p_verify.add_argument("--temp-root")
|
|
1727
1984
|
p_verify.add_argument("--command", action="append")
|
|
1728
1985
|
p_verify.add_argument(
|
|
1729
1986
|
"--timeout-seconds",
|