instar 1.3.725 → 1.3.726
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/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +1 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +19 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/AgentWorktreeReaper.d.ts +12 -0
- package/dist/monitoring/AgentWorktreeReaper.d.ts.map +1 -1
- package/dist/monitoring/AgentWorktreeReaper.js +19 -0
- package/dist/monitoring/AgentWorktreeReaper.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/upgrades/1.3.726.md +61 -0
- package/upgrades/side-effects/reaper-initial-pass.md +103 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-03T01:15:56.453Z",
|
|
5
|
+
"instarVersion": "1.3.726",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"domain": "identity",
|
|
12
12
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
13
13
|
"installedPath": ".instar/hooks/instar/session-start.sh",
|
|
14
|
-
"contentHash": "
|
|
14
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
15
15
|
"since": "2025-01-01"
|
|
16
16
|
},
|
|
17
17
|
"hook:dangerous-command-guard": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"domain": "safety",
|
|
21
21
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
22
22
|
"installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
|
|
23
|
-
"contentHash": "
|
|
23
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
24
24
|
"since": "2025-01-01"
|
|
25
25
|
},
|
|
26
26
|
"hook:grounding-before-messaging": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"domain": "safety",
|
|
30
30
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
31
31
|
"installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
|
|
32
|
-
"contentHash": "
|
|
32
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
33
33
|
"since": "2025-01-01"
|
|
34
34
|
},
|
|
35
35
|
"hook:compaction-recovery": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"domain": "identity",
|
|
39
39
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
40
40
|
"installedPath": ".instar/hooks/instar/compaction-recovery.sh",
|
|
41
|
-
"contentHash": "
|
|
41
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
42
42
|
"since": "2025-01-01"
|
|
43
43
|
},
|
|
44
44
|
"hook:external-operation-gate": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"domain": "safety",
|
|
48
48
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
49
49
|
"installedPath": ".instar/hooks/instar/external-operation-gate.js",
|
|
50
|
-
"contentHash": "
|
|
50
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
51
51
|
"since": "2025-01-01"
|
|
52
52
|
},
|
|
53
53
|
"hook:deferral-detector": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"domain": "safety",
|
|
57
57
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
58
58
|
"installedPath": ".instar/hooks/instar/deferral-detector.js",
|
|
59
|
-
"contentHash": "
|
|
59
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
60
60
|
"since": "2025-01-01"
|
|
61
61
|
},
|
|
62
62
|
"hook:self-stop-guard": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"domain": "coherence",
|
|
66
66
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
67
67
|
"installedPath": ".instar/hooks/instar/self-stop-guard.js",
|
|
68
|
-
"contentHash": "
|
|
68
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
69
69
|
"since": "2025-01-01"
|
|
70
70
|
},
|
|
71
71
|
"hook:post-action-reflection": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"domain": "evolution",
|
|
75
75
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
76
76
|
"installedPath": ".instar/hooks/instar/post-action-reflection.js",
|
|
77
|
-
"contentHash": "
|
|
77
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
78
78
|
"since": "2025-01-01"
|
|
79
79
|
},
|
|
80
80
|
"hook:external-communication-guard": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"domain": "safety",
|
|
84
84
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
85
85
|
"installedPath": ".instar/hooks/instar/external-communication-guard.js",
|
|
86
|
-
"contentHash": "
|
|
86
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
87
87
|
"since": "2025-01-01"
|
|
88
88
|
},
|
|
89
89
|
"hook:scope-coherence-collector": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"domain": "coherence",
|
|
93
93
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
94
94
|
"installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
|
|
95
|
-
"contentHash": "
|
|
95
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
96
96
|
"since": "2025-01-01"
|
|
97
97
|
},
|
|
98
98
|
"hook:scope-coherence-checkpoint": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"domain": "coherence",
|
|
102
102
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
103
103
|
"installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
|
|
104
|
-
"contentHash": "
|
|
104
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
105
105
|
"since": "2025-01-01"
|
|
106
106
|
},
|
|
107
107
|
"hook:free-text-guard": {
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"domain": "safety",
|
|
111
111
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
112
112
|
"installedPath": ".instar/hooks/instar/free-text-guard.sh",
|
|
113
|
-
"contentHash": "
|
|
113
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
114
114
|
"since": "2025-01-01"
|
|
115
115
|
},
|
|
116
116
|
"hook:claim-intercept": {
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"domain": "coherence",
|
|
120
120
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
121
121
|
"installedPath": ".instar/hooks/instar/claim-intercept.js",
|
|
122
|
-
"contentHash": "
|
|
122
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
123
123
|
"since": "2025-01-01"
|
|
124
124
|
},
|
|
125
125
|
"hook:claim-intercept-response": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"domain": "coherence",
|
|
129
129
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
130
130
|
"installedPath": ".instar/hooks/instar/claim-intercept-response.js",
|
|
131
|
-
"contentHash": "
|
|
131
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
132
132
|
"since": "2025-01-01"
|
|
133
133
|
},
|
|
134
134
|
"hook:stop-gate-router": {
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"domain": "safety",
|
|
138
138
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
139
139
|
"installedPath": ".instar/hooks/instar/stop-gate-router.js",
|
|
140
|
-
"contentHash": "
|
|
140
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
141
141
|
"since": "2025-01-01"
|
|
142
142
|
},
|
|
143
143
|
"hook:auto-approve-permissions": {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"domain": "safety",
|
|
147
147
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
148
148
|
"installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
|
|
149
|
-
"contentHash": "
|
|
149
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
150
150
|
"since": "2025-01-01"
|
|
151
151
|
},
|
|
152
152
|
"job:health-check": {
|
|
@@ -1562,7 +1562,7 @@
|
|
|
1562
1562
|
"type": "subsystem",
|
|
1563
1563
|
"domain": "updates",
|
|
1564
1564
|
"sourcePath": "src/core/PostUpdateMigrator.ts",
|
|
1565
|
-
"contentHash": "
|
|
1565
|
+
"contentHash": "c6ef2159621a94275497e37919cd6c33a09131e5e3a111c08b182e612f4796ae",
|
|
1566
1566
|
"since": "2025-01-01"
|
|
1567
1567
|
},
|
|
1568
1568
|
"subsystem:scheduler": {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
`AgentWorktreeReaper.start()` scheduled ONLY a 24h `setInterval` — no initial
|
|
9
|
+
pass. Agent servers restart far more often than daily (auto-updates, sleep/wake
|
|
10
|
+
supervisor bounces), so the interval timer reset on every restart and an
|
|
11
|
+
**enabled + armed** reaper never ran a single pass. Measured live on 2026-07-02:
|
|
12
|
+
86 worktrees / 25GB accumulated on a machine with `enabled: true, dryRun: false`
|
|
13
|
+
and `lastPassAt: 0` — the feature was on and structurally inert (root cause of
|
|
14
|
+
that day's fseventsd/reboot resource incident).
|
|
15
|
+
|
|
16
|
+
The fix:
|
|
17
|
+
|
|
18
|
+
- New `initialPassDelayMs` config (default 15 min; `<= 0` disables — the exact
|
|
19
|
+
legacy behavior as a no-release rollback lever) on
|
|
20
|
+
`monitoring.agentWorktreeReaper`.
|
|
21
|
+
- `start()` now schedules a ONE-TIME initial pass after that delay (unref'd
|
|
22
|
+
timer, cleared by `stop()`), then the unchanged 24h cadence. The delay keeps
|
|
23
|
+
the pass off the busy post-boot window.
|
|
24
|
+
- The pass runs through the same `reap()` as always: same KEEP gates (in-use /
|
|
25
|
+
dirty / unmerged / detached), same dry-run gating, same `maxReapsPerPass`
|
|
26
|
+
blast-radius cap, same per-path reclaim-failure breaker. Nothing about WHAT
|
|
27
|
+
may be deleted changed — only WHEN passes run.
|
|
28
|
+
- `GET /worktrees/agent-reaper` snapshot additively reports
|
|
29
|
+
`initialPassPending`.
|
|
30
|
+
- CLAUDE.md template + PostUpdateMigrator addendum so deployed agents learn the
|
|
31
|
+
new behavior (idempotent, content-sniffed on `initialPassDelayMs`).
|
|
32
|
+
|
|
33
|
+
## What to Tell Your User
|
|
34
|
+
|
|
35
|
+
<!-- audience: user, maturity: stable -->
|
|
36
|
+
|
|
37
|
+
If you switched on the stale-worktree auto-cleaner, it now genuinely runs: one
|
|
38
|
+
cleaning pass about 15 minutes after each server start, then daily as designed.
|
|
39
|
+
Previously a quirk meant frequent server restarts kept pushing its first run
|
|
40
|
+
into the future forever, so leftover work folders could quietly pile up into
|
|
41
|
+
tens of gigabytes even with the cleaner enabled. Its safety rules are untouched:
|
|
42
|
+
it still only removes folders whose work is fully merged and saved, still does
|
|
43
|
+
dry-run first, and it stays entirely off unless you enabled it.
|
|
44
|
+
|
|
45
|
+
## Summary of New Capabilities
|
|
46
|
+
|
|
47
|
+
- The stale-worktree reaper actually fires on real deployments: a one-time
|
|
48
|
+
cleaning pass ~15 minutes after boot (tunable/disableable via
|
|
49
|
+
`monitoring.agentWorktreeReaper.initialPassDelayMs`), unchanged daily cadence
|
|
50
|
+
thereafter, and `initialPassPending` visibility on the existing report route.
|
|
51
|
+
|
|
52
|
+
## Evidence
|
|
53
|
+
|
|
54
|
+
- `tests/unit/agent-worktree-reaper.test.ts`: 47/47 green — 7 new tests (initial
|
|
55
|
+
pass fires exactly once at the configured delay and before the interval;
|
|
56
|
+
respects dry-run; disabled reaper sets no timers; `<= 0` restores
|
|
57
|
+
interval-only; `stop()` cancels the pending pass; 24h cadence unchanged after
|
|
58
|
+
the initial pass; snapshot reports `initialPassPending` honestly).
|
|
59
|
+
- Live incident data (2026-07-02, topic 30379): reaper enabled+armed with 25
|
|
60
|
+
reap-eligible worktrees and `lastPassAt: 0`; server uptime history shows no
|
|
61
|
+
24h-continuous window for weeks.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Side-Effects Review — AgentWorktreeReaper one-time initial pass (reaper-never-fires fix)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `reaper-initial-pass`
|
|
4
|
+
**Date:** `2026-07-02`
|
|
5
|
+
**Author:** `Instar Agent (echo)`
|
|
6
|
+
**Second-pass reviewer:** `echo reviewer subagent (independent read)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`AgentWorktreeReaper.start()` scheduled ONLY a 24h `setInterval` with no initial pass. Real agent servers restart far more often than daily (updates, sleep/wake supervisor bounces), so the interval reset forever and an **enabled + armed** reaper never ran a single pass — measured on 2026-07-02 as 86 worktrees / 25GB accumulated on a machine with `enabled: true, dryRun: false` (the fseventsd/reboot incident's root cause). The fix adds a one-time initial pass `initialPassDelayMs` (default 15 min) after `start()`, keeping the 24h cadence unchanged. Files: `src/monitoring/AgentWorktreeReaper.ts` (config field + initial `setTimeout`, unref'd, cleared by `stop()`, `initialPassPending` in `snapshot()`), `src/core/types.ts` + `src/config/ConfigDefaults.ts` (config plumbing), `src/core/PostUpdateMigrator.ts` (CLAUDE.md awareness bullet for fresh + already-installed sections), `tests/unit/agent-worktree-reaper.test.ts` (7 new tests).
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `AgentWorktreeReaper.evaluate()` (keep/reap classifier) — **pass-through** — untouched; all safety gates (in-use / dirty / unmerged / detached → KEEP) unchanged.
|
|
15
|
+
- `AgentWorktreeReaper.start()` scheduling — **modify** — adds WHEN a pass runs (one initial pass), never WHAT a pass may delete.
|
|
16
|
+
- `killsEnabled` (enabled && !dryRun) — **pass-through** — the initial pass runs through the same `reap()` with the same dry-run/enabled gating and the same `maxReapsPerPass` blast-radius cap and per-path failure breaker.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Over-block
|
|
21
|
+
|
|
22
|
+
No block/allow surface — over-block not applicable. (Nothing new is rejected; the classifier is untouched.)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. Under-block
|
|
27
|
+
|
|
28
|
+
The inverse risk (over-DELETE) is the relevant frame for a deleter: the initial pass cannot delete anything the interval pass wouldn't — same `reap()`, same gates, same cap. Residual miss: an agent whose server restarts more often than every `initialPassDelayMs` (15 min) still never completes a pass; that pathological restart-loop case is out of scope here (the crash-loop itself is the incident to fix) and the pass is cheap enough that a partial-uptime server still benefits on the next calm boot.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. Level-of-abstraction fit
|
|
33
|
+
|
|
34
|
+
Right layer: the defect is purely in the component's own scheduling, so the fix lives in the component's `start()`. Alternatives considered and rejected: a scheduler job wrapping the reaper (duplicates an existing component lifecycle for no gain); persisting `lastPassAt` across restarts and firing when overdue (more state, more failure modes — the 15-min delayed pass achieves the same outcome stateless).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 4. Signal vs authority compliance
|
|
39
|
+
|
|
40
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
41
|
+
|
|
42
|
+
- [x] No — this change has no block/allow surface.
|
|
43
|
+
|
|
44
|
+
The change adds no authority and no detector. The reaper's existing (deliberately conservative, fail-closed) classifier keeps sole authority over deletions; this change only makes the already-shipped schedule actually execute. Dry-run/enabled defaults are unchanged (ships OFF + dry-run).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 5. Interactions
|
|
49
|
+
|
|
50
|
+
- **Shadowing:** none — no other component schedules reaper passes.
|
|
51
|
+
- **Double-fire:** initial pass and interval pass could theoretically overlap on a pathologically slow pass; `reap()` already has a `running` re-entrancy guard (returns empty, does not queue). Confirmed in code and covered by existing tests.
|
|
52
|
+
- **Races:** the initial pass lands ~15 min after boot — after the busy post-boot window, and any worktree in use at that moment is kept by the `isInUse` gate (lock or live process cwd). A concurrently-created fresh worktree is kept by `isInUse`/dirty/unmerged gates exactly as during an interval pass.
|
|
53
|
+
- **Feedback loops:** none — reaping does not feed anything that schedules reaping.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 6. External surfaces
|
|
58
|
+
|
|
59
|
+
- Other agents on the same machine: each agent's server reaps only its own `.worktrees/` estate (worktreesDir-bounded) — unchanged.
|
|
60
|
+
- Install base: behavior change is that an agent with the reaper ENABLED will now actually reclaim merged+clean+idle worktrees within ~15 min of boot instead of never. The feature still ships OFF + dry-run by default, so fleet agents see no change until an operator opts in.
|
|
61
|
+
- External systems: the initial pass may trigger the existing one-per-sweep `gh` merged-PR call ~15 min after boot (same call the interval pass makes; fail-safe to cherry-only).
|
|
62
|
+
- Persistent state: none added; `initialPassPending` is in-memory observability on the existing snapshot route.
|
|
63
|
+
- **Operator surface (Mobile-Complete Operator Actions):** no operator-facing actions added — config-only tuning, and the existing `GET /worktrees/agent-reaper` report is unchanged in shape (one additive field).
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
68
|
+
|
|
69
|
+
No operator surface — not applicable (no dashboard/approval/form files touched; one additive JSON field on an existing observability route).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
74
|
+
|
|
75
|
+
**Machine-local BY DESIGN.** A worktree estate is a per-machine filesystem artifact; each machine's reaper reclaims only its own disk. No user-facing notices are emitted (housekeeping events go to the existing reaper event listeners/logs), no durable state is held that could strand on topic transfer, and no URLs are generated. The pool-wide "how much disk is reclaimable across machines?" question remains answerable per-machine via the existing route (a merged view is a separate feature, not regressed here).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 8. Rollback cost
|
|
80
|
+
|
|
81
|
+
Pure code change. Config kill-switch without a release: `{"monitoring": {"agentWorktreeReaper": {"initialPassDelayMs": 0}}}` restores exact legacy scheduling (interval-only). Full rollback: revert the PR, ship next patch. No persistent state, no migrations, no user-visible regression during the rollback window (the feature ships dark; worst case is returning to "reaper never fires", which is the status quo ante).
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Conclusion
|
|
86
|
+
|
|
87
|
+
The review confirms this is a scheduling-only fix to a real, measured never-fires defect, with the deletion authority, safety gates, blast-radius cap, failure breaker, and OFF+dry-run defaults all untouched. The one design question the review surfaced — could the initial pass fire during a busy/fragile boot window — is addressed by the 15-min delay, the unref'd timer (never holds the process open), the `running` re-entrancy guard, and the existing in-use/dirty KEEP gates. Clear to ship.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Second-pass review (if required)
|
|
92
|
+
|
|
93
|
+
**Reviewer:** echo reviewer subagent (independent audit, 2026-07-02)
|
|
94
|
+
**Independent read of the artifact: concur**
|
|
95
|
+
|
|
96
|
+
Concur with the review. Verified against the real diff: (a) the initial pass invokes the identical `reap()` — same KEEP gates, same `killsEnabled` dry-run gate, same `maxReapsPerPass` cap, same per-path breaker, so it cannot delete anything the interval pass could not; (b) the `running` re-entrancy guard returns empty without queuing; (c) double-`start()` is blocked by the `this.timer` guard, `stop()` clears both timers, the fired callback self-clears so `snapshot()` stays honest, and production has exactly one `start()` callsite; (d) the migrator addendum is idempotent via the `initialPassDelayMs` content-sniff, the anchor matches deployed CLAUDE.md byte-for-byte (verified against a live deployment), and anchor drift degrades to an append — no corruption path; (e) `initialPassPending` is additive and backward-compatible at the single `res.json(snapshot())` consumer; (f) config plumbing delivers `initialPassDelayMs` from `.instar/config.json` with no server.ts change, and `0` genuinely restores interval-only scheduling. Reproduced the test evidence: 47/47 green.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Evidence pointers
|
|
101
|
+
|
|
102
|
+
- `tests/unit/agent-worktree-reaper.test.ts` — 47/47 green (7 new: initial-pass timing, dry-run respected, disabled → no timers, `<=0` rollback lever, stop() cancels, 24h cadence unchanged, snapshot honesty).
|
|
103
|
+
- Live incident data: 2026-07-02 topic 30379 — reaper `enabled:true, dryRun:false, lastPassAt:0` with 25 reap-eligible worktrees sitting unreclaimed; server uptime histories show restarts always < 24h apart.
|