instar 1.3.626 → 1.3.628

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,113 @@
1
+ #!/usr/bin/env bash
2
+ # load-assess.sh — the DURABLE go-to method for evaluating machine load.
3
+ #
4
+ # WHY THIS EXISTS: `uptime` 1-minute load average is the WRONG signal for "can I
5
+ # do work / is the machine genuinely loaded." It is (a) spike-susceptible (the
6
+ # 1-min figure swings wildly) and (b) on macOS INFLATED by threads stuck in
7
+ # uninterruptible disk I/O (e.g. Spotlight/mds reindex after a cold boot) — so a
8
+ # high load average can coexist with a mostly-idle CPU. On 2026-06-19 a load avg
9
+ # of ~40 was quoted as "heavy load" when the CPU was actually 62% IDLE and the
10
+ # load was Spotlight reindexing, not the agent. Never repeat that.
11
+ #
12
+ # THIS reports the RIGHT signals: real CPU idle% (sampled, not instantaneous),
13
+ # instar's time-windowed ResourceLedger (agent-attributed CPU avg/peak over the
14
+ # last hour), per-core load normalization, and WHAT is consuming CPU (so you can
15
+ # tell "my work" from external/transient like Spotlight). Then it emits a verdict.
16
+ #
17
+ # SCOPE HONESTY: the verdict is a CPU-CAPACITY signal. It does NOT assess
18
+ # memory/swap, thermal throttling, or disk-I/O saturation — OK means "CPU has
19
+ # headroom," not "everything is fine." Memory/pressure is covered separately by
20
+ # the ResourceLedger / reaper-pressure surfaces.
21
+ #
22
+ # Usage: load-assess.sh (human-readable verdict)
23
+ # load-assess.sh --json (machine-readable; human-diagnostic only,
24
+ # unversioned — a programmatic consumer must
25
+ # add a schemaVersion first, not assume shape)
26
+ set -uo pipefail
27
+ cd "$(dirname "$0")/../.." 2>/dev/null || true # agent home (.instar/scripts -> home)
28
+
29
+ JSON=0; [ "${1:-}" = "--json" ] && JSON=1
30
+ OS=$(uname -s 2>/dev/null || echo unknown)
31
+
32
+ # --- cores ---
33
+ if [ "$OS" = "Darwin" ]; then CORES=$(sysctl -n hw.ncpu 2>/dev/null || echo 0)
34
+ else CORES=$(nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo 2>/dev/null || echo 0); fi
35
+
36
+ # --- real CPU idle%: sampled (NOT a single instantaneous read) ---
37
+ IDLE=""
38
+ if [ "$OS" = "Darwin" ]; then
39
+ # macOS: top -l 2, SECOND sample is the real one (first is bogus)
40
+ CPU_LINE=$(top -l 2 -n 0 -s 1 2>/dev/null | grep "CPU usage" | tail -1)
41
+ IDLE=$(echo "$CPU_LINE" | sed -n 's/.*[, ]\([0-9.]*\)% idle.*/\1/p')
42
+ elif [ -r /proc/stat ]; then
43
+ # Linux: TWO-SAMPLE /proc/stat delta. A single read CANNOT compute idle%.
44
+ read_stat() { awk '/^cpu /{idle=$5+$6; tot=0; for(i=2;i<=NF;i++)tot+=$i; print idle, tot}' /proc/stat; }
45
+ S1=$(read_stat); sleep 1; S2=$(read_stat)
46
+ IDLE=$(awk -v a="$S1" -v b="$S2" 'BEGIN{
47
+ split(a,x," "); split(b,y," ");
48
+ di=y[1]-x[1]; dt=y[2]-x[2];
49
+ if(dt>0) printf "%.1f", 100*di/dt; else print "";
50
+ }')
51
+ fi
52
+ [ -z "$IDLE" ] && IDLE="" # empty => CPU read unavailable (e.g. unknown OS / no top)
53
+ # Test-only seam: force the sampled idle% so the verdict-threshold boundaries can be
54
+ # exercised deterministically (the real CPU read is environment-dependent). Read-only,
55
+ # affects only the printed verdict; never set in production.
56
+ [ -n "${LOAD_ASSESS_FORCE_IDLE:-}" ] && IDLE="$LOAD_ASSESS_FORCE_IDLE"
57
+ if [ -n "$IDLE" ]; then BUSY=$(awk -v i="$IDLE" 'BEGIN{printf "%.1f", 100-i}'); else BUSY=""; fi
58
+
59
+ # --- load averages (CONTEXT ONLY — never the basis of the verdict) ---
60
+ LOADS=$(uptime 2>/dev/null | sed -n 's/.*load average[s]*: *//p')
61
+ L1=$(echo "$LOADS" | awk -F'[ ,]+' '{print $1}')
62
+ L5=$(echo "$LOADS" | awk -F'[ ,]+' '{print $2}')
63
+ LOAD_PER_CORE=$(awk -v l="${L5:-0}" -v c="${CORES:-0}" 'BEGIN{if(c+0>0)printf "%.2f", l/c; else print "?"}')
64
+
65
+ # --- instar ResourceLedger: time-windowed agent-attributed CPU (the durable signal) ---
66
+ LEDGER="unavailable"
67
+ AUTH=$(node .instar/scripts/secret-get.mjs authToken 2>/dev/null || echo "")
68
+ PORT=$(node -pe "require('./.instar/config.json').port" 2>/dev/null || echo 4042)
69
+ AGENT_ID="${INSTAR_AGENT_ID:-$(node -pe "require('./.instar/config.json').projectName" 2>/dev/null || echo "")}"
70
+ if [ -n "$AUTH" ]; then
71
+ LEDGER=$(curl -s -m 8 -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: ${AGENT_ID}" "http://localhost:${PORT}/resources/summary?sinceHours=1" 2>/dev/null \
72
+ | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const j=JSON.parse(d);const a=(j.sources||[]).find(s=>s.source==='aggregate')||{};console.log('avg='+(a.avgCpuPercent??'?')+' peak='+(a.peakCpuPercent??'?')+' current='+(a.currentCpuPercent??'?')+' samples='+(j.sampleCount??'?'))}catch(e){console.log('unavailable')}})" 2>/dev/null || echo "unavailable")
73
+ fi
74
+
75
+ # --- top CPU consumers + classify my-work vs external ---
76
+ TOP=$(ps -Ao pcpu,comm -r 2>/dev/null | sed -n '2,4p')
77
+ TOP1=$(echo "$TOP" | head -1 | awk '{print $2}')
78
+ EXTERNAL="unknown"
79
+ case "$TOP1" in
80
+ *mds*|*mdworker*|*Spotlight*|*WindowServer*|*backupd*|*photoanalysisd*|*mediaanalysisd*) EXTERNAL="external-transient (macOS: $(basename "$TOP1" 2>/dev/null))";;
81
+ *instar*|*node*|*claude*|*tmux*|*python*) EXTERNAL="agent-work ($(basename "$TOP1" 2>/dev/null))";;
82
+ "") EXTERNAL="unknown";;
83
+ *) EXTERNAL="other ($(basename "$TOP1" 2>/dev/null))";;
84
+ esac
85
+
86
+ # --- VERDICT (based on real idle%, NOT load average) ---
87
+ awk_lt() { awk -v a="$1" -v b="$2" 'BEGIN{exit !(a+0<b+0)}'; }
88
+ if [ -z "$IDLE" ]; then
89
+ VERDICT="UNKNOWN"; REASON="CPU idle% unavailable on this platform; rely on the agent CPU ledger + load context"
90
+ elif awk_lt "$IDLE" 12; then VERDICT="SATURATED"; REASON="CPU genuinely saturated (idle <12%)";
91
+ elif awk_lt "$IDLE" 30; then VERDICT="ELEVATED"; REASON="CPU busy but not saturated";
92
+ else VERDICT="OK"; REASON="CPU mostly idle"; fi
93
+
94
+ if [ "$JSON" = "1" ]; then
95
+ printf '{"verdict":"%s","reason":"%s","cpuIdlePercent":%s,"cpuBusyPercent":%s,"cores":%s,"loadAvg1":"%s","loadAvg5":"%s","loadPerCore":"%s","ledger":"%s","topConsumer":"%s","topClass":"%s","scope":"cpu-capacity-only","os":"%s"}\n' \
96
+ "$VERDICT" "$REASON" "${IDLE:-null}" "${BUSY:-null}" "${CORES:-0}" "${L1:-}" "${L5:-}" "$LOAD_PER_CORE" "$LEDGER" "${TOP1:-}" "$EXTERNAL" "$OS"
97
+ else
98
+ echo "=== MACHINE LOAD ASSESSMENT ==="
99
+ echo "VERDICT: $VERDICT — $REASON"
100
+ echo " (scope: CPU capacity only — does NOT assess memory/swap/thermal/disk-IO)"
101
+ echo ""
102
+ echo "Real CPU: ${BUSY:-?}% busy / ${IDLE:-?}% idle (sampled, the primary signal)"
103
+ echo "Cores: ${CORES:-?} logical"
104
+ echo "Agent CPU (1h): $LEDGER (instar ResourceLedger, time-windowed)"
105
+ echo "Top consumer: ${TOP1:-?} → $EXTERNAL"
106
+ echo "Load avg (ctx): 1m=${L1:-?} 5m=${L5:-?} (per-core ${LOAD_PER_CORE}) ← context only; spike-prone + I/O-inflated, NOT the verdict"
107
+ echo ""
108
+ echo "Top 3 CPU:"; echo "$TOP" | sed 's/^/ /'
109
+ echo ""
110
+ echo "Interpretation: trust the real CPU idle% + the time-windowed agent CPU."
111
+ echo "A high load average with high idle% = external/transient (e.g. Spotlight), NOT agent pressure."
112
+ fi
113
+ exit 0
@@ -0,0 +1,23 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Added `load-assess.sh` — a robust, durable, go-to method for an agent to evaluate machine load — and shipped it fleet-wide. It reports the RIGHT signals (real CPU idle% sampled, instar's time-windowed ResourceLedger agent-CPU, per-core load, and what's actually consuming CPU) and emits a verdict (OK / ELEVATED / SATURATED), instead of the spike-prone, macOS-Spotlight-I/O-inflated `uptime` 1-minute load average. A static "use load-assess.sh, never trust the load average" reminder is wired into the session-start hook ABOVE its compact-`exec` delegate, so it survives compaction (not just session start), and a Machine Load Assessment section is added to the CLAUDE.md template (generate + migrate) so every agent knows the capability exists.
9
+
10
+ ## What to Tell Your User
11
+
12
+ Your agent now judges whether the machine is genuinely busy using real CPU idle% (and its own recent CPU use), not the misleading 1-minute load average — so it won't mistake a quiet-but-disk-indexing machine for an overloaded one and unnecessarily hold off on work. It's read-only: it measures and reports, changing nothing.
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ - `.instar/scripts/load-assess.sh` (`--json` to parse) — go-to machine-load assessment with a verdict; CPU-capacity-scoped (does not assess memory/thermal). Installed on every agent on update.
17
+
18
+ ## Evidence
19
+
20
+ - 14 new tests pass: verdict thresholds (both sides of each boundary via a `LOAD_ASSESS_FORCE_IDLE` test seam), `--json` contract, fail-soft when the ResourceLedger is unreachable, the compaction-survival property (`blockIdx < execIdx` in `getSessionStartHook()`), `migrateScripts` install (always-overwrite, executable), and `migrateClaudeMd` idempotent append.
21
+ - 568 existing PostUpdateMigrator + scaffold tests still green (no regression).
22
+ - Independent second-pass review (touches compaction) concurred after live simulation — bash builtin `echo` writes directly to fd 1, so the awareness block's stdout reaches the captured output before the process-replacing `exec` on the compact path.
23
+ - Spec converged through `/spec-converge` (2 rounds, `cross-model-review: codex-cli:gpt-5.5`); the review caught two critical bugs in the first draft (editing a dead template file; a mechanically-false compaction guarantee) before any code was written.
@@ -0,0 +1,23 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Added operator-**Cancel** for an in-flight account×machine matrix cell. PR #1230 shipped the matrix happy-path (tap a cell → start a cross-machine sign-in) but left no way to back out: a mis-tapped cell sat "in-progress" (◷) for up to 15 minutes with a live `claude auth login` pane and no undo. This adds a tappable Cancel on the in-progress cell that abandons the pending login and tears down its sign-in pane — on THIS machine or a peer, via a fronting relay that mirrors the existing `submit-code` pattern (Bearer-authed, one mesh hop; offline peer → honest 502). Two new routes (`POST /subscription-pool/follow-me/enroll/:id/cancel` target-local + `POST /subscription-pool/follow-me/cancel` relay), dark behind the existing `multiMachine.accountFollowMe` flag (no new config key). Two `PendingLoginStore` hardening tweaks ride along: a `transition()` terminal guard (a cancel can never clobber a login that just COMPLETED) and an `issue()` that replaces a stale terminal/expired same-id record (so re-enrollment after a cancel works instead of erroring "already exists").
9
+
10
+ ## What to Tell Your User
11
+
12
+ If you start setting up an account on a machine from the dashboard's account×machine grid and realize you tapped the wrong cell, you can now tap **Cancel** right on that spinning cell to back out — it shuts down the leftover sign-in window and frees the cell so you can set up the right one. It works whether the cell is on the machine you're looking at or another of your machines. (This rides the account-follow-me feature, which is off by default.)
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ - **Cancel an in-flight matrix cell** — a tappable Cancel on the in-progress (◷) cell in the dashboard Subscriptions grid; abandons the pending login + tears down its sign-in pane on the owning machine (self or peer). Bearer-only (no PIN — a per-machine PIN can't cross the mesh, same as the code-submit step). Dark behind `multiMachine.accountFollowMe`.
17
+
18
+ ## Evidence
19
+
20
+ - 32 new tests pass: store terminal-guard + issue()-replace + the live-pending-still-throws boundary (unit); `EnrollmentWizard.getById`/`abandon` pass-throughs (unit); the full route matrix over HTTP (integration) — dark→503, happy→200+abandoned with the RAW `tmux kill-session` asserted via a recording stub, idempotent terminal→200, completed-not-clobbered, unknown/malformed id→404, expired-still-cancellable→200, submit-in-flight→409, relay self/peer/offline; and the Tier-3 "feature is alive" E2E booting the real `AgentServer` (200 + abandoned, not 404; dark→503; Bearer required).
21
+ - 178 existing related tests still green (no regression), tsc clean, full custom-lint suite clean.
22
+ - Spec converged through `/spec-converge` (2 rounds, `cross-model-review: codex-cli:gpt-5.5`); the review caught FOUR real bugs in the first draft before any code: the original pane-kill (`sessionManager.killSession`) was a no-op for these unregistered raw-tmux panes; cancel→abandon would have broken re-enrollment; a cancel could have un-done a successful login; and a self-only scope would have silently 404'd peer cells.
23
+ - Independent second-pass review (kill-path + gate) — see `upgrades/side-effects/matrix-cell-operator-cancel.md`.
@@ -0,0 +1,151 @@
1
+ # Side-Effects Review — matrix-cell-operator-cancel
2
+
3
+ **Change:** Adds operator-cancel for an in-flight account×machine matrix cell — a
4
+ target-local route `POST /subscription-pool/follow-me/enroll/:id/cancel`, a fronting
5
+ relay `POST /subscription-pool/follow-me/cancel`, two `PendingLoginStore` hardening
6
+ tweaks, two `EnrollmentWizard` pass-throughs, a dashboard Cancel button, and a one-clause
7
+ CLAUDE.md awareness addition. Dark behind `multiMachine.accountFollowMe`.
8
+
9
+ **Spec:** `docs/specs/matrix-cell-operator-cancel.md` (converged iter 2, approved).
10
+
11
+ ## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
12
+
13
+ - A cancel against a `completed`/`abandoned` login returns a calm idempotent `200`
14
+ (`cancelled:false, alreadyTerminal:true`) — it does NOT reject; correct.
15
+ - A cancel while a `submit-code` is in flight returns `409` ("try again in a moment").
16
+ This is intentional and momentary (the ~2s credential-poll window) — the operator can
17
+ re-tap. It is the right call: cancelling mid-credential-write would strand a partial
18
+ credential. Not an over-block.
19
+ - Malformed id (`^[a-z0-9-]+$` fail) → `404`. The store's own `ID_RE` is identical, so a
20
+ malformed id could never name a real login anyway — no legitimate input rejected.
21
+
22
+ ## 2. Under-block — what failure modes does this still miss?
23
+
24
+ - **Peer cancel when the peer is offline** → honest `502` (the relay can't reach it); the
25
+ peer's pane keeps running until that machine's next enroll pre-cleans it. Surfaced to the
26
+ operator, not silently swallowed.
27
+ - **Crash between abandon and pane-kill** → orphaned pane. Self-heals on the next enroll
28
+ (enroll-start pre-cleans the slot's pane before re-spawning). Accepted (D2).
29
+ - **Mandate not revoked** on cancel (D4) — a bounded, re-mint-only, 1h-expiring mandate
30
+ lingers. Grants no standing authority; accepted residual.
31
+ - **configHome slot not wiped** (D3) — a deliberate decision (wiping could clobber a valid
32
+ prior credential for the same account); stale-slot hygiene is the existing
33
+ credential-coherence path's job.
34
+
35
+ ## 3. Level-of-abstraction fit
36
+
37
+ Correct layer. The route family already exists (`start-cell` mints; `submit-code` operates
38
+ on an in-flight login). Cancel is a peer of `submit-code` ("operate on an existing login")
39
+ and is placed in the same route closure, sharing the `followMeSubmitInFlight` guard. The
40
+ store-level terminal guard + `issue()` replace push correctness DOWN to the store (defense
41
+ in depth) rather than relying on route logic alone. The pane teardown uses the SAME raw
42
+ `tmux kill-session` the spawn (`server.ts:10713`) uses — not a higher-level helper that
43
+ turned out to be a no-op for these unregistered panes.
44
+
45
+ ## 4. Signal vs authority compliance
46
+
47
+ The route holds de-escalating authority (kills a pane, abandons a record) gated by Bearer
48
+ auth + the dark flag. It is NOT a brittle detector with blocking teeth — it reuses the
49
+ existing structural API-edge gates (auth, `^[a-z0-9-]+$` validation, terminal-state
50
+ idempotent read, the in-flight 409). No content/intent judgment is made; the "should I
51
+ cancel?" decision is the operator's tap, not an inferred classifier. The `transition()`
52
+ terminal guard is pure mechanics (refuse a terminal→terminal flip), not a judgment gate.
53
+ Compliant with `docs/signal-vs-authority.md` (the exempted "structural guard at the edge /
54
+ idempotency mechanics" class).
55
+
56
+ ## 5. Interactions
57
+
58
+ - **Shares `followMeSubmitInFlight` with submit-code** — cancel reads it (409 if held). It
59
+ never writes it, so it can't deadlock submit-code. Verified the cancel route is colocated
60
+ in submit-code's closure (it references the same `Set`).
61
+ - **`issue()` change touches ALL callers** — but the only production caller is
62
+ `EnrollmentWizard.start()` (verified: the other `.issue(` hits are the unrelated mandate
63
+ store). start-cell only reaches `issue()` after its reuse pre-check fails on a live-pending
64
+ record, so any same-id record at `issue()` is non-pending → safe to replace.
65
+ - **`transition()` terminal guard** affects `complete()` + `abandon()` (both route through
66
+ it). It only blocks terminal→terminal; normal `pending→completed`/`pending→abandoned`
67
+ unaffected. `reissue()` does not call `transition()` and already guards terminal states.
68
+ - **Cancel vs start-cell reuse** — abandon-first makes the record non-reusable before the
69
+ cell clears, so start-cell's reuse path can never hand out a cancelled login's URL.
70
+ - No double-fire / shadow: the relay and target-local route have distinct paths
71
+ (`/follow-me/cancel` vs `/follow-me/enroll/:id/cancel`); `:id` cannot match the literal
72
+ `start`/`cancel` segments.
73
+
74
+ ## 6. External surfaces
75
+
76
+ - **Dashboard Cancel button** — a new operator-visible affordance on the in-progress (◷)
77
+ matrix cell, behind the dark dev-agent gate. Mobile-first (full-tap-target button,
78
+ optional native confirm that degrades to proceed under headless). Ships with the npm
79
+ package (no migration).
80
+ - **Two new HTTP routes** — dark behind `multiMachine.accountFollowMe` (503 when off). No
81
+ new config key.
82
+ - **No new external network egress** beyond the existing same-mesh relay hop (Bearer-authed,
83
+ to this agent's own paired machines only — `resolvePeerUrls`). No credential ever crosses.
84
+ - Timing/runtime dependence: only the momentary submit-in-flight 409 (a real, transient
85
+ condition the operator retries through).
86
+
87
+ ## 6b. Operator-surface quality (Operator-Surface Quality standard)
88
+
89
+ This change touches an operator surface (`dashboard/subscriptions.js` — the Cancel
90
+ button on the in-progress matrix cell).
91
+
92
+ 1. **Leads with the primary action?** Yes. The Cancel button is the single,
93
+ visible action on an in-progress (◷) cell — a full-tap-target `<button>Cancel</button>`
94
+ rendered inline beside the status glyph on every poll re-render (not collapsed,
95
+ not below the fold, no explanatory prose in front of it).
96
+ 2. **Zero raw internals as primary content?** Yes. The cell shows the glyph + the
97
+ word "Signing in…" + the "Cancel" button; on tap, a plain status line
98
+ ("Cancelling…" → "Cancelled — you can set this up again.") or the route's
99
+ plain-English error. No JSON, no fingerprints, no config paths, no slugs. The
100
+ `loginId`/`machineId` ride as `data-*` attributes (support metadata), never shown.
101
+ 3. **Destructive actions de-emphasized?** N/A in the harmful sense — Cancel is
102
+ itself the *reversing* (de-escalating) action, fully reversible (re-tap "Set up"
103
+ to redo). It is a small single button, optionally guarded by a native confirm
104
+ ("Cancel this in-progress setup?"). There is no separate destructive control to
105
+ demote; the constructive "Set up" path reappears after a cancel.
106
+ 4. **Plain language + phone width?** Yes. "Cancel" / "Signing in…" / "Cancelled —
107
+ you can set this up again." read the way a non-engineer would say them; the
108
+ button is a real tap target in the existing mobile-responsive matrix grid (same
109
+ cell layout as the shipped "Set up"/"Submit" buttons), no horizontal scroll, no
110
+ truncated table hiding the action.
111
+
112
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
113
+
114
+ **Proxied-on-write (relay).** A `PendingLogin` + pane live on the machine running the login
115
+ subprocess; the target-local route is machine-local by necessity. Cross-machine reach is the
116
+ fronting relay `follow-me/cancel`, dispatching by `machineId` to self-loopback or the owning
117
+ peer over the authed mesh hop — the SAME proven pattern as `follow-me/submit-code`. The
118
+ dashboard always calls the relay, so a peer cell cancels correctly; an offline peer yields an
119
+ honest 502. No durable state strands on topic transfer (a PendingLogin is not topic-scoped);
120
+ no generated URL crosses a machine boundary. The earlier self-only draft (which would have
121
+ silently 404'd peer cells) was rejected in convergence — this is the corrected posture.
122
+
123
+ ## 8. Rollback cost
124
+
125
+ Single `git revert`. Pure additive surface behind an existing dark flag. The two store
126
+ changes are backward-compatible (the terminal guard only PREVENTS a clobber that should
127
+ never happen; the `issue()` replace only RELAXES a throw). No data migration — `abandoned`
128
+ is already a valid terminal state the store + sweeps handle today. No agent-state repair. The
129
+ auto-merger handles the merge; a bad merge is one revert.
130
+
131
+ ---
132
+
133
+ ## Second-pass review
134
+
135
+ _(High-risk: touches a block/allow decision surface — a kill path + a dark gate + the word
136
+ "gate". Reviewer appended below.)_
137
+
138
+ An independent second-pass reviewer audited the actual implementation code (not just the
139
+ spec) against the artifact, line by line. Findings: **all [OK]** — kill targets the correct
140
+ pane with no injection (derived from the stored record, not `req.params.id`; `id` regex-gated
141
+ `^[a-z0-9-]+$`; no shell); gate order exactly as specified with abandon-before-kill; the
142
+ `@silent-fallback-ok` tag is correct and both catch ratchets pass; the `transition()` terminal
143
+ guard cannot break `complete()`/`reissue()`/normal flows; `issue()` replace has a single
144
+ production caller (`EnrollmentWizard.start`) and still throws on a live-pending dup; the
145
+ dashboard Cancel is on the durable cell, no PIN, confirm degrades under jsdom; template↔migrator
146
+ parity + idempotency hold; the relay adds no SSRF surface beyond submit-code (own paired
147
+ machines only, `encodeURIComponent`'d id, no `code` body); no new CI ratchet trips. One
148
+ **[MINOR]**: the submit-in-flight 409 is a momentary retry-through window (documented in §1) —
149
+ not a defect. No blockers, no material issues, no signal-vs-authority violation.
150
+
151
+ **Verdict: Concur with the review.**
@@ -0,0 +1,74 @@
1
+ # Side-Effects Review — Robust Load Assessment (fleet-wide + compaction-surviving)
2
+
3
+ **Version / slug:** `robust-load-assessment-fleet`
4
+ **Date:** 2026-06-19
5
+ **Author:** echo
6
+ **Second-pass reviewer:** echo-spawned reviewer subagent (required — touches compaction)
7
+
8
+ ## Summary of the change
9
+
10
+ Ships a read-only machine-load diagnostic (`load-assess.sh`) to every agent as a script template, wires a static "use load-assess.sh / never trust the uptime load average" awareness block into the in-code session-start hook ABOVE its compact-`exec` delegate (so it survives compaction), and adds an Agent-Awareness CLAUDE.md section (generate + migrate). Born from a 2026-06-19 incident where the agent misread the spike-prone, Spotlight-I/O-inflated 1-minute load average as "heavy load" while the CPU was ~60% idle. Three source files + two test files; 14 new tests, 568 existing PostUpdateMigrator/scaffold tests still green.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `load-assess.sh` verdict (OK/ELEVATED/SATURATED) — **add** — produces a SIGNAL (a load verdict printed to stdout); holds **zero** blocking authority. Nothing consumes it as a gate.
15
+ - session-start hook output — **modify** (additive) — adds a static doc block; no conditional logic, no decision.
16
+ - `migrateScripts` / `migrateClaudeMd` — **add** (install + append) — idempotent, content-sniffed; no decision surface.
17
+
18
+ ---
19
+
20
+ ## 1. Over-block
21
+ No block/allow surface — over-block not applicable. The script reads CPU/ledger and prints; the hook emits a static string. Nothing is rejected or gated.
22
+
23
+ ## 2. Under-block
24
+ No block/allow surface — under-block not applicable. The change adds no enforcement, so there is no failure mode to "miss." (The script's verdict is advisory: a human/agent reading "OK" still makes the call. Scope honesty is built in — the verdict explicitly states it is CPU-capacity only, not a universal health oracle, so a reader does not over-trust an OK on a memory/thermal-pressured box.)
25
+
26
+ ## 3. Level-of-abstraction fit
27
+ Right layer. A diagnostic script lives in `.instar/scripts/` alongside `secret-get.mjs`/`emit-session-clock.sh` (the established sibling pattern); the awareness lives in the session-start hook (the existing always-fires-on-lifecycle surface) and the CLAUDE.md template (the canonical agent-awareness surface). No smarter gate should own this — there is no gate; it is read-only observability feeding the agent's own judgment. The time-windowed signal it surfaces (ResourceLedger) is consumed, not duplicated.
28
+
29
+ ## 4. Signal vs authority compliance
30
+ Compliant (`docs/signal-vs-authority.md`). `load-assess.sh` is a pure **detector/signal** — it computes a load verdict and prints it, with no power to block, delay, or rewrite anything. It does not add brittle logic with blocking authority; it adds a read-only signal a smart authority (the agent) consumes. The hook block and CLAUDE.md section are documentation, not decision points.
31
+
32
+ ## 5. Interactions
33
+ - The hook block is placed ABOVE the `exec compaction-recovery.sh` branch so it is emitted on every event (startup/resume/clear/compact); it does not shadow or get shadowed by the recovery delegation (verified by a test asserting `blockIdx < execIdx`). It adds a few lines of static stdout to a hook that already emits many — no latency (no network), and the block precedes no `exit N` (the hook ends on an `echo`, which exits 0), so it cannot change the exit code. (Compaction-survival rests on bash builtin `echo` writing directly to fd 1 with no userspace stdio buffer for the subsequent `exec` to discard — verified empirically by the second-pass reviewer across tty/pipe/file-redirect.)
34
+ - `migrateScripts` adds a sibling install next to `secret-get.mjs`; independent try/catch, cannot affect the other script installs.
35
+ - `migrateClaudeMd` appends a content-sniffed section (`!includes('Machine Load Assessment')`) — idempotent, does not duplicate on re-run (tested), does not edit other sections in place.
36
+ - The CLAUDE.md section text is single-sourced (`MACHINE_LOAD_ASSESSMENT_CLAUDEMD_SECTION()`) and used by BOTH generate (new agents) and migrate (existing) — they cannot drift (tested).
37
+
38
+ ## 6. External surfaces
39
+ - The script reads the existing `GET /resources/summary` endpoint (read-only; no new surface) and degrades to local-only if it is unreachable (fail-soft, tested). No new external API.
40
+ - `--json` is documented human-diagnostic-only and unversioned — no system consumes it; a future programmatic consumer must add a `schemaVersion` first (stated in the script + spec).
41
+ - The hook output is agent-internal stdout, not visible to other agents/users/systems.
42
+ - No timing/conversation-state dependence: the script samples CPU on demand; the hook block is static. A test-only `LOAD_ASSESS_FORCE_IDLE` env seam exists (read-only, affects only the printed verdict for boundary testing; never set in production).
43
+
44
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
45
+ - `load-assess.sh` — **machine-local BY DESIGN**: load is a property of the machine the script runs on; there is nothing to replicate. Cross-machine load reads are already served separately by `/guards?scope=pool` and `/resources/summary`.
46
+ - The hook block and CLAUDE.md section — **machine-local static template content**, installed identically on every machine by each machine's own migration; no replication needed, no strand-on-transfer risk (no durable per-topic state), no generated URLs.
47
+
48
+ ## 8. Rollback cost
49
+ Low. Revert the PR (3 source files). The change is additive and idempotent: reverting stops shipping the script template + hook block + CLAUDE.md section to future migrations. Agents that already migrated keep a harmless read-only `load-assess.sh` on disk (no cleanup required; it gates nothing). No data migration, no agent-state repair. A hot-fix release is sufficient if needed.
50
+
51
+ ---
52
+
53
+ ## Second-pass review (required — touches compaction)
54
+
55
+ **Reviewer:** echo-spawned independent reviewer subagent. **Verdict: Concur with the review.**
56
+
57
+ The reviewer independently audited the 4 additive blocks, ran the 14 tests, AND ran live simulations rather than trusting the framing:
58
+ 1. **Compaction-survival — TRUE (verified by execution):** simulated the hook with stdout to a tty, a pipe, and a file redirect with the compact `exec` actually firing — the MACHINE LOAD block appeared before the delegate's output in every case. Robust because bash builtin `echo` writes via `write(2)` directly to fd 1 (no userspace stdio buffer for `exec` to discard); bytes are in the kernel before `exec` runs. The non-executable-recovery fallback also behaves correctly (exec skipped, block still emitted, hook continues).
59
+ 2. **Timeout/exit code — no risk:** static echoes, zero network; precedes no `exit N`.
60
+ 3. **Script safety — sound:** `set -uo pipefail` with NO `set -e`; ran under stripped env / empty-IDLE / faked unknown-OS — all exit 0 with a clean verdict (UNKNOWN + null JSON on genuine no-CPU-read, never a crash). All `set -u`-sensitive vars assigned before use; verdict chain exhaustive (VERDICT+REASON on every branch); `bash -n` clean.
61
+ 4. **Test `blockIdx < execIdx`** pins the real property; combined with both-boundary script tests + the empirically-confirmed flush-survives-exec behavior, the guarantee is covered.
62
+ 5. **Packaging — sound:** `loadRelayTemplate('load-assess.sh')` resolves via `src/templates/scripts/`, which IS in package.json `files` (ships to npm) — the same proven mechanism as `secret-get.mjs`. Content-sniff anchor `'Machine Load Assessment'` is unique (no cross-section collision); idempotency confirmed count==1 after repeated runs.
63
+
64
+ No concerns raised.
65
+
66
+ ---
67
+
68
+ ## CI-conformance follow-up (post-open, #1231)
69
+
70
+ CI shard 4/4 surfaced two required-conformance gaps (caught by the full suite, not my local subset run):
71
+ - `load-assess.sh`'s localhost API call to `/resources/summary` lacked the `X-Instar-AgentId` header that `template-agent-id-header.test.ts` requires of every Bearer call in a template. Added (AGENT_ID derived from `INSTAR_AGENT_ID`/config.json projectName, same as session-start.sh). No behavior change — the header is identification only; the call already fail-softs.
72
+ - The `Machine Load Assessment` migrateClaudeMd section was not registered in `feature-delivery-completeness.test.ts`'s `legacyMigratorSections` tracking list. Added (observe-only, ships ON, no framework-shadow marker → legacy migrator list, not featureSections). No runtime change — a test-registry entry.
73
+
74
+ Both are conformance fixes to the same change; the design + second-pass review above stand unchanged.