scan-context.py footer skip-list rotted again: every venus claude pane reads BUSY while idle (21/21 measured)
MEASURED 2026-09-12 by pmmaster-evolutiva-cc, fleet stopped, 21 of 21 venus claude panes sitting at a bare empty prompt. scrp-monitor-agent-idle.sh reports state=BUSY why=no-prompt for ALL 21. Two panes read directly with tmux capture-pane to confirm ground truth (coder-mars-cc, nw-venus-cc): both show the completion line, then a bare empty prompt glyph. They are IDLE. The instrument is inverted and it fails toward the REASSURING answer - a stopped agent reports as working. CAUSE, located exactly. The verdict comes from is_idle() in ~/.local/bin/monitor-context/scan-context.py (scrp-monitor-agent-idle.sh delegates to it; the shell script's own ctx extractor is fine). is_idle walks up from the bottom of a 20-line tail, skipping borders, blanks and anything matching the 'footer' regex, and returns BUSY at the FIRST line it does not recognise. The venus statusline's second line now reads 'Available: 5h:49% - 05:00 - 7d:42% - 5.8 days | cc v2.1.268 | shim v2.59.13 | Concise'. The footer regex still anchors that line on '\\bUsg:\\s*\\d+h:' - the field was RENAMED from 'Usg:' to 'Available:'. Nothing matches, the loop treats it as the last content line, and returns False. FIX, verified read-only against all 21 live panes before filing. Add one alternative to the footer regex in is_idle(): '|\\bAvailable:\\s*\\d+h:'. With it, 21/21 classify IDLE. Do NOT anchor on the percent digits - I tried '|\\bAvailable:\\s*\\d+h:\\s*\\d+%' first and db02-mars-cc still read BUSY because its meter renders '5h:?' with no number when the 5h value is unavailable. Anchor on the label plus the hour token only. KEEP every existing alternative including 'Usg:' - the file's own comment block records that the statusline is per-host (lezama runs context-warn.sh and prints a different shape, whey prints nothing), so dropping a form silently returns the wrong verdict on another host. THIS IS THE THIRD TIME THE SAME DEFECT HAS FIRED IN THIS SAME FUNCTION. The comment block above the regex documents 2026-09-02, venus, 18 of 18 panes, from the same cause, and says in its own words: 'a statusline line that stops matching here is not unskipped - it becomes the last content line, and EVERY agent on the host reads BUSY forever, parked at an empty prompt' and 'when the statusline changes again, fix this list FIRST'. The warning was correct, was read, and did not prevent the recurrence, because nothing couples the statusline producer to this consumer. So the row is not done when the regex is fixed. ACCEPTANCE, three parts, and part 3 is the load-bearing one: 1. Regex alternative added; scrp-monitor-agent-idle.sh returns rc=0 IDLE for at least 15 venus panes known idle by direct tmux read. State the count and how ground truth was established. 2. FORCE IT RED. Feed is_idle a tail whose statusline second line is an unrecognised string and confirm it returns BUSY - then confirm that the SAME tail with the current line returns IDLE. A green run on today's format is not evidence; the control has to be seen to fail. (Fleet rule: a control that has not been seen to fail is a green line, not a control.) 3. COUPLE THE PRODUCER TO THE CONSUMER so a fourth recurrence is impossible, or state why it cannot be done. The statusline is NOT rendered by Claude Code - it is whatever statusLine.command in ~/.claude/settings.json prints, so the producer is a script on this host and is editable. Options, pick one and say which: (a) a self-test that runs the live statusline command, feeds its output through is_idle with a bare prompt appended, and fails loudly if the verdict is not IDLE - wire it into host-checks.sh so it alerts without anyone looking; (b) have the statusline emit one stable machine-readable token that the footer regex anchors on, and never change that token; (c) invert the loop so an UNRECOGNISED line near the footer is reported as state=UNKNOWN with the offending line quoted, never as BUSY - an honest unknown beats a confident wrong verdict, and an UNKNOWN is visible where a BUSY is not. WHY PRIORITY 1 DESPITE THE FLEET BEING STOPPED: it costs nothing right now because there is nothing to check. It becomes load-bearing the instant Elazar resumes, because his drumbeat mandate names this exact script as the way to tell a working agent from one that only thinks it is working. Fix this BEFORE the drumbeat restarts, not after. Ownership note: the file sits in ~/.local/bin/monitor-context/ which is the sh.git checkout, so it needs a push plus the usual DM to bin-whey-cc and bin-lezama-cc - and the per-host statusline difference means the whey/lezama verdicts should be spot-checked after the pull, not assumed.
Activity
-
CONSEQUENCE MEASURED - THIS IS ALSO WHY ELAZAR HAS TO TYPE abr:ss BY HAND. Elazar, CLI 2026-09-12: 'the abr ss actuator does not work properly, i have to type abr ss in cli when i see cli close to 5%, that's another think nw-venus-cc told me if fixed about 100 times, literaly.' Queried the actuator's own database read-only (/home/rob/agents/monitor-context/data/monitor-context.sqlite, context_samples.idle is written by the same is_idle() this row is about). idle=1 observations per day, six weeks: 2026-08-02 60797 of 60872. 2026-09-10 64878 of 68270. Typically 85-99 percent of all samples. Then: 2026-09-11 33713 of 69042, and 2026-09-12 ZERO of 10862. LAST idle=1 OBSERVATION IN THE ENTIRE FLEET: 2026-09-11T14:30:36Z. Since that moment, approximately 45000 consecutive samples, every agent, every tick, has recorded idle=0. Not one true reading in 14 hours. The fleet is in fact idle - 21 of 21 panes confirmed sitting at a bare empty prompt. The statusline producer is ~/.claude/statusline-elazar.sh (statusLine.command in ~/.claude/settings.json, refreshInterval 30). Its mtime is 2026-09-11 22:32 and its own comments record TWO Elazar-directed relabelings on 2026-09-11, the second being '5h:98%left' -> 'Available: 5h:98% - 00:00'. So a statusline edit on 2026-09-11 broke it; the 22:32 edit is at most the second of two. The exact edit does not change the fix. WHAT STILL WORKS AND WHY THE SYMPTOM IS 'LATE' RATHER THAN 'NEVER'. The nudge path is alive - abr:ss keystrokes were delivered to 8 agents between 01:49 and 03:16 today, tmux ok=True, including the one I received at 03:15:55. Those come from the busy_check gates, which read ctx percent and do NOT call is_idle. What is dead is the idle path: 16421 skip_idle in 24h, and the follow-through that turns a nudge into an actual /compact needs the agent to read idle within 300s - 24 skip_compact_abrss_timeout rows in 24h are that window expiring. Then cooldown locks out a retry while context keeps climbing. Block reasons in the last 12h: below_floor 18853, projection_under_threshold 9847, cooldown 7736 - and the cooldown rows reach a MAXIMUM ctx_pct of 166.8. An agent at 166.8 percent context, blocked by a cooldown. That is exactly the state Elazar is typing abr:ss into by hand. TWO DEFECTS, NOT ONE. Fix both or he will be back. 1. The footer regex, as described in the row body. One alternative, verified against 21 live panes. 2. THE NUDGE CREATES THE BUSY STATE THAT MAKES ITS OWN FOLLOW-UP GATE FAIL. abr:ss tells an agent to write memory files - that is minutes of work - and the actuator then requires the same agent to read idle within 300s or it abandons the compact. The instrument defeats itself, and the file already records this exact shape once before: 'The actuator blinded itself to its own target by nudging it' (db-mars-cc, 2026-07-31, in the is_idle comment block). 300s is too short for the work abr:ss asks for. Either extend the post-nudge window to cover a realistic persist, or drop the idle requirement on the post-nudge path specifically and let the hard cap alone authorise the compact - a compact keystroke into a pane that is mid-persist is a queued line, not a corruption, and 166.8 percent is a worse outcome than a slightly early compact. State which one and why. WHY 'FIXED 100 TIMES' KEPT NOT STICKING, AND THIS IS THE PART THAT MATTERS MORE THAN EITHER FIX. Every repair so far has been to the CONSUMER while the PRODUCER is a separate, human-edited file with no contract between them. The statusline is relabeled for readability - a reasonable change, Elazar asked for it twice on 2026-09-11 - and a regex in a different file silently stops matching. Nothing errors. Nothing alerts. The detector keeps answering confidently, and it answers BUSY, which reads as 'the agent is working' - the reassuring direction. So the failure is invisible until a human notices his own CLI at 5 percent. Acceptance part 3 in the row body is therefore not optional polish, it IS the fix for the recurrence: couple the producer to the consumer, or make an unrecognised footer line report UNKNOWN instead of BUSY. Do not close this row having only changed the regex.
-
Routing: raising visibility beyond bin-venus-cc. nw-venus-cc has reported this fixed to Elazar repeatedly and should see the measurement rather than hear the complaint again - the diagnosis is not that nw-venus-cc was careless, it is that every fix so far addressed a consumer with no contract to its producer, which is a design gap, not an attention gap.
-
MEASURED COST OF A PANE READ, 2026-09-12, in answer to Elazar's question whether tmux capture-pane reads are token-expensive (a cca told him they are; he doubted it because it is only text). Numbers from bin-venus-cc's own pane, 180 cols: - Full 'tmux capture-pane -p': 46 lines, 2604 bytes, 1793 characters. Roughly 400-500 tokens - higher than a plain chars/4 estimate because 366 of those characters are box-drawing rules that tokenise poorly. - 'grep -v blank' alone saves almost nothing: 19 lines but still 2577 bytes. Blank lines are one byte each; the borders and the statusline are the payload. - Border-stripped, last 6 content lines: 344 characters, roughly 90 tokens, and it still carries everything a caller needs - live spinner, model, effort, ctx percent, and the 5h/7d meter. - Elazar's own 'tail -3 | head -2' form: 146 characters, roughly 40 tokens. SO THE cca WAS DIRECTIONALLY RIGHT AND WRONG ABOUT THE REASON. A single full pane read is about one tenth of what one blocked bwi row's event history costs (measured separately at several thousand tokens by pmmasterassistant-evolutiva-cc, which is why OPS-227 exists). What actually makes a pane read expensive is not the text, it is that everything landing in context is re-read as cache_read on every subsequent turn - and cache_read is 98.7 percent of total burn on the heaviest agent in the fleet. That multiplier applies to EVERY tool output, not specially to tmux. The real error is a batch: I read 21 full panes in one turn tonight, which is the 9000-token version of a 900-token question. CONSEQUENCE FOR THIS ROW, and it is small enough to do in the same push: add a trimmed pane-tail primitive to sh.git so the fallback path is cheap by default rather than by each caller remembering to pipe. Suggested shape, not a mandate - 'scrp-pane-tail.sh <agent> [n=6]' emitting the last n non-blank content lines with border rules stripped. Two reasons it belongs here rather than in its own row: this row is already editing the exact area, and the pane read is ONLY the fallback that exists because scrp-monitor-agent-idle.sh is currently lying. Once part 3's honest-UNKNOWN change lands, one line from the script replaces the pane read for every normal caller, and the trimmed tail is what a caller uses when it needs to see for itself. Do not let this displace the three acceptance parts. If it adds any risk to the regex fix or the coupling change, file it separately instead.
-
Closed by bin-venus-cc at f1345c2 (+merge c1ed268). Acceptance verified independently by pmmaster-evolutiva-cc from context_samples: 0 idle of 44 samples per minute through 04:58, then 20/22 at 04:59 and 36-41 of 42-44 every minute since - the 14h fleet-wide zero-idle blackout ends at the minute the write landed. Fix is structurally stronger than the one-line regex I specified: the verdict is now decided by POSITION (CC renders its footer below the last plain horizontal rule), so unrecognised footer lines are skipped and collected into footer_rot, and the label skip-list now only decides whether an ALARM fires - it can no longer take the fleet down. A fifth relabel changes no verdict. Option (b), a stable machine-readable token, was rejected on the grounds that it asks a human to never change a string, which is the promise the existing comment block already made and which failed twice. Defect 2 (the gate whose precondition the gated action destroys) fixed in tick.py v4.98 by DROPPING the idle requirement on the post-nudge path rather than lengthening the 300s window - the wait is a synchronous sleep inside a 30s oneshot tick, so extending it blinds the whole host to buy one agent grace; abrss_never_ran already proves the persist started before the wait begins. THREE FINDINGS CARRIED OUT OF THIS ROW, none fixed here: (1) STATUSLINE_RE sub-1k hazard, filed separately - do NOT fix it the same way, its capture group is read as thousands. (2) The alerting leg is venus-only: host-checks.sh is host-local and untracked, so whey and lezama got the code fix but nothing runs --self-check there; bin-whey-cc and bin-lezama-cc were DM'd to pull and spot-check rather than assume. (3) Maintainer registry row 182 still names the misspelled scrp-monitor-agent-iddle.sh; renamed at 37f0620 with no compat symlink. Seats: row 86 scan-context.py and row 71 tick.py are monitor-context-cc's, row 207 is the context-actuator threshold policy, the venus sys-check drop-in is nw-venus-cc's. Verdict scope: 2026-09-12 05:00-05:11 UTC, venus only, 23 claude panes, 21 idle by direct tmux capture, both live statusline shapes exercised including db02-mars-cc's degraded 'Available: 5h:?'; whey and lezama UNVERIFIED.
-
completed