instar 1.3.616 → 1.3.618

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.
@@ -0,0 +1,22 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Fixed a server restart loop that could hit an agent on an overloaded machine. The supervisor's CPU-starvation guard — which is supposed to NOT restart an alive-but-slow server while the box is oversubscribed — judged load at a single instant. On a machine whose 1-minute load average oscillates around the starvation threshold, a momentary dip below the line tricked the guard into restarting a server that had been starved the whole time; the restart's heavy boot deepened the overload and the cycle repeated every ~11–15 minutes (2026-06-17 incident, topic 12476). `ServerSupervisor.deferRestartForCpuStarvation()` now judges **sustained** load — it samples the load ratio on each unresponsive health-check tick, keeps the last ~60 seconds of readings, and treats the box as starved if it was starved at any point in that window. A single dip can no longer authorize a restart. The recent-load window is dropped the moment the server recovers, so stale readings can't over-defer a later, unrelated episode. The genuinely-dead-process path (instant restart), the idle-machine-hung path (fast restart), and the ~5-minute hard-cap backstop are all unchanged.
9
+
10
+ ## What to Tell Your User
11
+
12
+ If your agent felt laggy or kept "auto-restarting" on a busy machine — replies arriving late, messages queueing, the dashboard briefly 502-ing every ~10–15 minutes — that was this loop. Your agent now rides out short load spikes instead of bouncing itself, so it stays responsive under load. Nothing for you to do; it applies automatically on update. (Separately: if a machine is *chronically* overloaded, the real cure is reducing how much is running on it — this fix stops the self-inflicted restart loop, not the underlying load.)
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ No new user-facing capability — this is a reliability fix to the existing server supervisor. The supervisor's CPU-starvation restart guard now uses a sustained (windowed) load signal instead of an instantaneous one.
17
+
18
+ ## Evidence
19
+
20
+ - `src/lifeline/ServerSupervisor.ts` — `deferRestartForCpuStarvation()` rewritten to use a windowed-max load signal over `loadSampleWindow` (6 samples ≈ 60s); window cleared on failure-streak reset; deferral log line now reports the sustained ratio.
21
+ - `tests/unit/supervisor-cpu-starvation-defer.test.ts` — updated the test that encoded the old instantaneous behavior (it expected a restart after a single dip — the bug); added cases: a momentary dip does NOT restart a sustainedly-starved server (the 2026-06-17 loop), sustained easing eventually restarts (defer is not permanent), and the window is dropped on streak reset (no stale over-defer).
22
+ - All 4 supervisor-related unit suites green: 40 tests passed (10 in the starvation-defer file). `tsc --noEmit` clean.
@@ -0,0 +1,27 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Fixed a fleet-wide crash in a generated Stop hook. `PostUpdateMigrator.getActionClaimFollowthroughHook()` emitted `.instar/hooks/instar/action-claim-followthrough.js` with a stray, unbalanced `})();` on the last line (the hook body opens with a bare `let data = ''`, not an IIFE). Node rejected the file with `SyntaxError: Unexpected token '}'` on **every** Stop-hook fire — the red "Ran 5 stop hooks → Stop hook error" seen in Claude Code session logs. The hook is signal-only and dark-by-default, so the crash was noisy but harmless (nothing blocked or corrupted); it simply spammed errors once per turn on every agent.
9
+
10
+ Removed the stray line from the source template. Existing agents auto-heal on their next update via the always-overwrite migration path (`migrateHooks` unconditionally re-writes this hook from the corrected template) — no new migration code needed.
11
+
12
+ Added a CI parse gate (`tests/unit/generated-hooks-parse.test.ts`) that GENERATES every `get*Hook()` result and runs `node --check` on each JS hook. The pre-existing `no-bare-require-in-generated-hooks.test.ts` only source-scans for one forbidden pattern and never parses, which is exactly how a plain syntax typo shipped. The new gate closes the whole "generated hook fails to parse" class — a hook typo can no longer reach the fleet.
13
+
14
+ ## What to Tell Your User
15
+
16
+ The recurring "Stop hook error / SyntaxError: Unexpected token '}'" noise in your Claude Code session logs is fixed. It was a harmless-but-noisy typo in one of the always-on bookkeeping hooks — it never blocked or corrupted any work, just printed an error every turn. After this update it's gone, and a new CI check guarantees a broken hook can't ship again.
17
+
18
+ ## Summary of New Capabilities
19
+
20
+ - No new user capability. This is a reliability fix: a fleet-wide per-turn Stop-hook crash is removed, and CI now parse-checks every generated hook so the class can't recur.
21
+
22
+ ## Evidence
23
+
24
+ - `tests/unit/generated-hooks-parse.test.ts` — RED before the fix (failed on `getActionClaimFollowthroughHook` with `SyntaxError: Unexpected token '}'`, 23 other generators passed), GREEN after removing the stray line. Runs `node --check` on the real generated output of every `get*Hook()`.
25
+ - `tests/unit/no-bare-require-in-generated-hooks.test.ts` — still GREEN (unchanged; the two tests cover different failure modes).
26
+ - `pnpm build` — clean TypeScript compile after the template edit.
27
+ - Always-overwrite confirmed at `PostUpdateMigrator.ts` (`fs.writeFileSync(..., 'action-claim-followthrough.js', this.getActionClaimFollowthroughHook())`) — the corrected hook is unconditionally re-written on every migration, healing the deployed fleet on update.
@@ -0,0 +1,67 @@
1
+ # Side-Effects Review — Fix fleet-wide stray-`})();` in action-claim Stop hook + CI parse gate
2
+
3
+ **Version / slug:** `action-claim-hook-parse-fix`
4
+ **Date:** `2026-06-17`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `not required` (no decision-point / no runtime authority — CI-only guard + a generated-text fix)
7
+
8
+ ## Summary of the change
9
+
10
+ `PostUpdateMigrator.getActionClaimFollowthroughHook()` emitted a hook whose template literal ended with `});\n})();` — but the hook body is **not** wrapped in an IIFE (it opens with a bare `let data = ''`). The trailing `})();` is therefore unbalanced, so `node` rejects the generated `.instar/hooks/instar/action-claim-followthrough.js` with `SyntaxError: Unexpected token '}'` on **every** Stop-hook fire. Because built-in hooks are always-overwritten on every migration, this broken hook shipped to the entire fleet and crashes once per turn (visible as the red "Ran 5 stop hooks → Stop hook error" in Claude Code). The hook is signal-only and dark-by-default, so the crash was noisy-but-harmless (it never blocked or corrupted work) — but it spammed errors continuously.
11
+
12
+ Two files change:
13
+ - `src/core/PostUpdateMigrator.ts` — remove the single stray `})();` line from the template (the hook body was already correct top-level code; only the extra IIFE-close was wrong).
14
+ - `tests/unit/generated-hooks-parse.test.ts` (new) — GENERATES every `get*Hook()` result and runs `node --check` on each JS hook. This is the gate that was missing: the existing `no-bare-require-in-generated-hooks.test.ts` only source-scans for one forbidden pattern and never parses, so a plain typo sailed past it.
15
+
16
+ The deployed fleet auto-heals on next update via the existing always-overwrite migration path (`migrateHooks` unconditionally re-writes this hook from the now-correct template). No new migration code required.
17
+
18
+ ## Decision-point inventory
19
+
20
+ This change adds no runtime decision point. It corrects generated text and adds a CI test.
21
+
22
+ - `getActionClaimFollowthroughHook()` template — **modify** — remove one stray line; generated hook now parses.
23
+ - `tests/unit/generated-hooks-parse.test.ts` — **add** — CI-time parse gate over all generated hooks. A build-time signal (fails CI); it holds no runtime authority over any agent.
24
+
25
+ ---
26
+
27
+ ## 1. Over-block
28
+
29
+ No block/allow surface — over-block not applicable. The CI test only fails the build when a generated hook genuinely fails `node --check`; that is the intended (and only) "block."
30
+
31
+ ## 2. Under-block
32
+
33
+ `node --check` validates parse-ability, not runtime correctness — a hook that parses but throws at runtime would still pass this test. That gap is acceptable for this layer (parse failure was the actual fleet bug) and is the explicit motivation for Fix B (the runtime session-start hook-integrity guard), which catches post-deploy/runtime breakage. The test also skips `.sh`/`.py` generators (it is a `node` parse gate); shell/python hooks are out of scope here.
34
+
35
+ ## 3. Level-of-abstraction fit
36
+
37
+ Correct layer. The bug is in source-generated text, so the prevention belongs in CI over the source generators (cheap, deterministic, runs before ship). The complementary runtime layer (heal already-broken hooks on disk without waiting for an update) is deliberately a separate change (Fix B) — this one does not try to be both.
38
+
39
+ ## 4. Signal vs authority compliance
40
+
41
+ - [x] No — this change has no block/allow surface. The new test is a CI signal (fails the build); it never gates agent behavior at runtime. The template edit is a correctness fix to generated code. `docs/signal-vs-authority.md` reviewed — no brittle-detector-with-authority introduced.
42
+
43
+ ## 5. Interactions
44
+
45
+ - **Shadowing:** None. The new test is additive and independent of `no-bare-require-in-generated-hooks.test.ts` (that one keeps its narrower source-scan; this one parses). They cover different failure modes and do not shadow each other.
46
+ - **Double-fire:** N/A — test-only + a one-line generated-text fix.
47
+ - **Races:** None. The test writes to a per-run `mkdtemp` dir; no shared state.
48
+ - **Feedback loops:** None.
49
+
50
+ ## 6. External surfaces
51
+
52
+ - **Other agents / install base:** Positive-only. On next update every agent's `action-claim-followthrough.js` is re-written from the corrected template (always-overwrite), so the per-turn `SyntaxError` stops fleet-wide. No behavior change when the feature is enabled — the hook body is byte-identical except for the removed invalid line.
53
+ - **External systems:** None.
54
+ - **Persistent state:** None.
55
+ - **Operator surface:** No operator-facing actions — not applicable.
56
+
57
+ ## 6b. Operator-surface quality
58
+
59
+ No operator surface (no `dashboard/*` or form files touched) — not applicable.
60
+
61
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
62
+
63
+ **machine-local BY DESIGN** — the fix is to a generated on-disk hook file; each machine installs and runs its own hook files locally (hooks are per-checkout infrastructure). The correction reaches every machine through the same always-overwrite migration each machine runs on update — there is no cross-machine state, notice, durable record, or generated URL involved. No one-voice gating, no topic-transfer stranding, no machine-boundary links.
64
+
65
+ ## 8. Rollback cost
66
+
67
+ Trivial. Revert the two-file commit: the template line returns (re-breaking the hook) and the test is removed. No data migration, no agent-state repair. Because the hook is always-overwritten, a roll-forward or roll-back simply re-writes the hook from whichever template version is shipped.
@@ -0,0 +1,81 @@
1
+ # Side-Effects Review — Supervisor sustained-CPU-starvation restart guard
2
+
3
+ **Version / slug:** `supervisor-sustained-starvation-fix`
4
+ **Date:** `2026-06-17`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `(see Phase 5 section below — required: restart/recovery path)`
7
+
8
+ ## Summary of the change
9
+
10
+ `ServerSupervisor.deferRestartForCpuStarvation()` decided whether to hold off restarting an alive-but-unresponsive server based on the **instantaneous** CPU load ratio (`loadRatioProvider() > maxLoadRatio`). On an oversubscribed box the 1-minute load average oscillates around the threshold, so a single sample dipping below it authorized a restart of a server that had been CPU-starved the whole time — and the restart's heavy boot deepened the starvation, producing a ~11–15-minute restart loop (2026-06-17 incident, topic 12476). The fix records the load ratio on each unresponsive tick into a small fixed window (`loadSampleWindow = 6`, ~60s) and treats the box as starved if the **windowed max** exceeds the threshold. The window is cleared when the failure streak resets (server recovered), so stale high readings can't over-defer a later episode. Files: `src/lifeline/ServerSupervisor.ts` (the guard + 4 new fields + the deferral log line now reports the sustained ratio), `tests/unit/supervisor-cpu-starvation-defer.test.ts` (updated the test that encoded the old instantaneous behavior; added dip-protection, sustained-easing, and window-reset cases).
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `ServerSupervisor.deferRestartForCpuStarvation()` — **modify** — the starvation SIGNAL feeding the restart authority is changed from instantaneous to a sustained windowed-max reading. No new authority added; the existing restart decision (and its hard cap) is unchanged in structure.
15
+
16
+ ---
17
+
18
+ ## 1. Over-block
19
+
20
+ **What legitimate inputs does this change reject that it shouldn't?**
21
+
22
+ Mapped to this domain, "over-block" = deferring a restart that genuinely should happen. The change makes deferral *more* likely (it holds off restarting while recent load was high). The bounded risk: a server that is genuinely hung (frozen event loop) AND happens to be on a box that was busy in the last ~60s will wait up to the full window (~60s) longer before the windowed max falls — and in the worst case waits for the ~5-minute hard cap, which is unchanged and still force-restarts. So the maximum added delay before a truly-hung server restarts is bounded by the existing hard cap; the change never *prevents* a restart, only delays it under recent-high-load — which is the intended, safe direction.
23
+
24
+ ## 2. Under-block
25
+
26
+ **What failure modes does this still miss?**
27
+
28
+ A box that is chronically starved for longer than the ~5-minute hard cap will still force-restart at the cap, which on a sustainedly-overloaded machine is itself unhelpful (restarting never cures starvation). This fix addresses the dominant observed failure (the sub-cap dip-triggered restarts at "10 checks") but does NOT change the hard-cap behavior. Making the hard cap escalate-instead-of-restart under sustained starvation is a genuine follow-up. <!-- tracked: topic-12476 --> The real cure for chronic starvation is reducing machine load (surfaced to the operator separately).
29
+
30
+ ## 3. Level-of-abstraction fit
31
+
32
+ **Is this at the right layer?**
33
+
34
+ Yes. The supervisor is the correct owner of the server-restart decision (process lifecycle). The starvation check is a *detector signal* feeding that authority; this change improves the signal's quality (smoothing out single-sample noise) rather than adding a new authority. It reuses the existing injectable `loadRatioProvider` / `cpuStarvation` primitive — no re-implementation of load reading.
35
+
36
+ ## 4. Signal vs authority compliance
37
+
38
+ **Does this hold blocking authority with brittle logic, or produce a signal that feeds a smart gate?** (`docs/signal-vs-authority.md`)
39
+
40
+ Compliant. The brittle part (reading load) is a detector; this change makes that detector *less* brittle by replacing an instantaneous reading with a sustained windowed one. The restart authority (the supervisor's deterministic lifecycle policy — appropriate for this tightly-constrained domain) is unchanged. No new brittle blocker is introduced; the existing one is smoothed.
41
+
42
+ ## 5. Interactions
43
+
44
+ **Does it shadow another check, get shadowed, double-fire, race?**
45
+
46
+ - It interacts only with `evaluateUnhealthyServer()`, which calls it once per unresponsive tick. No new timers, no concurrency. The window is single-threaded supervisor state.
47
+ - The hard-cap early-return is preserved and runs *after* the sample is recorded, so the window stays accurate for logging.
48
+ - The window-reset detection keys on `consecutiveFailures` not strictly increasing (it strictly increases within a streak; a new episode restarts at the threshold) — verified against all reset sites; deferral is only consulted at/above threshold, so the `<=` reset test is correct.
49
+ - SleepWakeDetector uses the same `cpuStarvation` ratio but keeps its own state — no shared mutable state touched.
50
+
51
+ ## 6. External surfaces
52
+
53
+ **Anything visible to other agents/users/systems? Timing/state dependencies?**
54
+
55
+ Only the supervisor's own log line changes wording (now reports the sustained ratio + window). No API, no message, no cross-agent surface. Behavior is purely local process-lifecycle. The only timing dependency is the existing 10s health-check cadence, which the window is sized against (6 samples ≈ 60s).
56
+
57
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
58
+
59
+ **Machine-local BY DESIGN.** The supervisor watches the server on its OWN machine; CPU starvation and restart decisions are inherently per-machine. There is no cross-machine state to replicate and nothing to proxy on read — each machine's supervisor independently judges its own load. No one-voice/transfer/URL concerns (no user-facing notice, no durable shared state, no generated link).
60
+
61
+ ## 8. Rollback cost
62
+
63
+ **If wrong in production, what's the back-out?**
64
+
65
+ Low. Single-file logic change behind the existing injectable provider. Back-out = revert the commit (a hot-fix patch release). No data migration, no state repair — the supervisor holds only in-memory transient state (the load window), which resets on the next boot. Worst-case failure mode of the fix itself (over-deferral) is bounded by the unchanged ~5-minute hard cap.
66
+
67
+ ---
68
+
69
+ ## Phase 5 — Second-pass review (required: restart/recovery path)
70
+
71
+ **Reviewer:** independent reviewer subagent (general-purpose), 2026-06-17.
72
+
73
+ **Verdict: Concur with the review.**
74
+
75
+ Independent audit findings:
76
+ - **Windowing correctness:** the counter strictly increases within a streak because `consecutiveFailures++` precedes every `evaluateUnhealthyServer()` call (both failure callsites), so the `<=` reset-detection correctly distinguishes a continuing streak (no clear) from a new episode (clear); it cannot false-clear mid-streak.
77
+ - **Empty-array / -Infinity:** safe — a sample is pushed unconditionally before every `Math.max(...)`, even right after a reset-to-`[]`.
78
+ - **Safety valves all preserved:** dead process restarts instantly (early return before the guard); hung server on an idle box restarts promptly (window fills with low samples → max < threshold); the ~5-min hard cap still force-restarts.
79
+ - **Signal vs authority:** compliant — improves a detector signal (instantaneous → sustained windowed-max), no new brittle authority.
80
+ - **Direction:** only ever makes restart *less* aggressive; max added delay for a truly-hung-on-recently-busy box is bounded by the unchanged hard cap.
81
+ - **Test quality:** the new tests genuinely fail against the old instantaneous code (the dip-sample and first-eased-tick cases).