Decision log — brain resilience autopilot run (2026-07-13)
Autonomous calls made during the unattended run, newest last. Each entry: the decision, why, what was rejected, and what a reviewer should double-check.
D1 — Background shells do not block turn-end
Decision: a screen showing N shell(s) still running with an idle prompt counts as
READY. Why: the shells are typically dev servers Claude started with
run_in_background — they never exit; 3 of the last 4 run deaths were exactly this
(turn complete, 1 shell still running, 8-minute freeze, death). The final assistant
message is already in the transcript at that point. Rejected: adding shells to
WORKING_RE — would convert the 8-min death into a 90-min hang. Double-check: if a
turn’s real work runs inside a background shell, the brain may prompt “too early”;
the transcript ground-truth check (Phase 2) plus the brain’s own judgment mitigate.
D2 — Blocked background AGENTS still block turn-end
Decision: keep Waiting for N background agent as WORKING. Agent fan-outs are
finite work (unlike servers) and prompting mid-fan-out confuses the turn.
D3 — Survey dismissal alternates 0 and Esc
Decision: first dismissal attempt sends 0 (matches the observed 0: Dismiss
build), subsequent attempts alternate Esc/0, bounded. Why: DB shows an 8-minute
Esc-spam death on the survey screen. Alternating covers both TUI builds.
D4 — Recovery is transcript-first
Decision: before declaring a frozen screen dead, consult the transcript JSONL: recent file growth = progress; last entry is final assistant text + file quiet = turn OVER (success, not error). Why: the transcript is the only stable, version-proof signal; the screen is decoration. Rejected: more regex tuning as the primary fix — it loses the arms race with every Claude Code release.
D5 — Respawn+resume re-injection guarded by transcript byte offset
Decision: when a turn is recovered by kill+--resume, the orchestrator records the
transcript byte size before injection; after resume it re-injects the SAME prompt only
if no assistant reply appeared after that offset. Why: blind re-injection would
double-execute work; blind skip would stall the turn. Double-check: offset
comparison assumes Claude appends to the same transcript file on resume (it does — the
conversation id is unchanged).
D6 — Auto-heal defaults ON, 3 attempts, exponential backoff, not for Auth/Cwd errors
Decision: reliability.autoHeal defaults to true with autoHealMaxAttempts: 3
(backoff 2min → 4min → 8min; counter resets after a healthy 15-minute run). 401s and
bad-cwd errors don’t heal (they need the human). Why: the user’s stated goal is
“have it continue autonomously”; 31 dead runs were restart-and-it-worked cases.
Double-check: if a failure is deterministic at boot, heal burns 3 quiet attempts
before notifying — acceptable (≈14 min), but tune autoHealMaxAttempts to taste.
D7 — Qwen triage may only press Enter, Esc, or a single digit
Decision: the Qwen screen-triage fallback can suggest exactly one keystroke from {enter, esc, 0-9}; anything else is ignored. It never types text. Why: a local 9B model must not be able to inject free-form instructions into a live session; digits cover surveys/pickers, Enter/Esc cover gates/menus.
D8 — Escalation timeout only for autonomous persona
Decision: brain.escalationTimeoutMin (default 20, 0=off) auto-picks the FIRST
escalation option after the timeout — but only for sessions whose autonomy persona is
“autonomous”. Cautious/balanced sessions still wait for the human indefinitely.
A pending DANGEROUS-GATE approval under the same persona times out to deny (the
safe direction — claude routes around it); a gate is never auto-approved.
Why: an autonomous-persona overnight run parking forever on a question defeats the
mode; the first option is by convention the brain’s recommended path. A notification
fires either way. Double-check: if you dislike auto-picks, set it to 0.
D9 — Headless engine is opt-in per session, PTY stays the default
Decision: new engine "claude-headless" drives claude -p --output-format
stream-json with --session-id/--resume chaining; existing sessions keep the PTY
engine. Why: print mode eliminates the whole screen-scraping failure class but
gives up interactive gate mediation and live TUI view; per-session choice lets both
coexist. Subscription-safety unchanged: same CLI binary, same login, scrubbedEnv()
still enforced, no API key involved.
D11 — Adversarial review drove four hardening passes (post-implementation)
Decision: the review findings were fixed by strengthening ground truth, not
heuristics: submit verification & reply reads are transcript-offset based; rung 2
demands 3-minute quiet + double-sample; an unrecognized dialog is deny-by-default
(triage can never Enter/digit-approve anything); an operator stop is never
“recovered”; partial execution re-injects a reconcile wrapper, never the raw prompt;
message-less transcripts are quarantined (claude wedges the id for BOTH –resume and
–session-id while such a file exists — verified on v2.1.207). Double-check: the
quarantine renames files under ~/.claude/projects/<enc>/ to *.unresumable-<ts>;
if you ever see those, they’re forensic leftovers of crashed first turns.
D10 — unknown+static screens get the ladder, not a bigger regex zoo
Decision: IDLE_RE gains only the well-attested current footers; everything else
unrecognized routes through transcript check → repaint nudge → Qwen triage → resume.
Why: the ladder is version-proof; regexes are an optimization, not the safety net.