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.
- package/dashboard/subscriptions.js +50 -0
- package/dist/core/EnrollmentWizard.d.ts +8 -0
- package/dist/core/EnrollmentWizard.d.ts.map +1 -1
- package/dist/core/EnrollmentWizard.js +12 -0
- package/dist/core/EnrollmentWizard.js.map +1 -1
- package/dist/core/PendingLoginStore.d.ts.map +1 -1
- package/dist/core/PendingLoginStore.js +21 -2
- package/dist/core/PendingLoginStore.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +60 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +112 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +72 -64
- package/src/scaffold/templates.ts +3 -1
- package/src/templates/scripts/load-assess.sh +113 -0
- package/upgrades/1.3.627.md +23 -0
- package/upgrades/1.3.628.md +23 -0
- package/upgrades/side-effects/matrix-cell-operator-cancel.md +151 -0
- package/upgrades/side-effects/robust-load-assessment-fleet.md +74 -0
|
@@ -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.
|