feat(harness): surface the whole statusline run, not just its first row - #60
Merged
Conversation
Claude Code paints a multi-row statusline under the input box — on a real host three rows: ctx/cache/limits, then [model] + cwd + git branch, then the permission mode and agent count. stripChrome correctly peels the whole run off the mirror, but extractStatusLine re-surfaced only the FIRST non-blank row, so rows 2 and 3 were rendered nowhere at all: the model, working directory, branch and permission mode were invisible in Collie. Those are exactly the fields you check before typing a reply from a phone. The adapter contract now returns the run, not a line: extractStatusLine(lines): string | null extractStatusLines(lines): string[] // [] = no box at the tail One honest shape rather than a parallel method — a harness whose statusline is genuinely one line returns a one-element array. locateInputBox now hands out `statusEnd`, the exclusive end of the run it already walked, so the background-agents footer below the blank separator still can't leak in. The strip stacks the rows, each truncated, instead of joining them: joined, ~150 chars would compete for a strip that fits ~55 on a phone and would truncate away the very fields this exists to surface. Height stays bounded by MAX_STATUS_LINES upstream — no second cap. Refs #56 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A statusline separates its fields by colour before anyone reads them — the context meter, the model, the branch. extractStatusLines flattened the styled lines to text one call before the strip that renders them, so the strip was uniformly muted grey. Rows now come back as StyledLine, and the strip renders segment spans in the MIRROR's colour space rather than as app chrome: terminal colour is dark-space colour, so a bright statusline colour re-themed onto a light background is the illegibility ADR 0002 exists to prevent. The space and its invert rule move to mirror-space.ts, since two surfaces now share them and a second spelling would drift. Refs #56 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #56, which raised the statusline-run bound but left
extractStatusLinereturning onlythe run's first row. Everything after it — model, cwd, git branch, permission mode — was stripped
off the mirror by
stripChromeand rendered nowhere.Verified on a real host whose statusline is 3 rows: only
CTX:20% CACHE:100% LIMITS …was evervisible;
[Opus·medium] ~/… on main*and⏵⏵ bypass permissions on … · ← 4 agentswere not.Contract:
extractStatusLine(lines): string | null→extractStatusLines(lines): string[].One honest shape — a single-row harness returns a one-element array — and it drops the null-vs-empty
double-empty state. Claude is the only adapter implementing it.
Bound:
InputBoxgainsstatusEnd, handed out by the walk that already knows where the runstops rather than re-derived, so the background-agents footer below the blank separator still cannot
leak in.
stripChromeis untouched — the rows stay OUT of the mirror.UI: stacked, one truncated row per line. Joining with a separator would put ~150 chars into a
strip that fits ~55 on a phone, truncating away exactly the fields this exists to surface; wrapping
would make the strip's height depend on pane width and turn a column-aligned statusline into ragged
prose. Height is bounded upstream by
MAX_STATUS_LINES, and the mirror is a shrinking flex child.Hint rows are surfaced too. Filtering them needs the content-matching this module deliberately
refuses, and that row carries the permission mode.
A probe over all 29 real Claude fixtures found every capture is 2 rows, so the corpus table's
statusLineFound: booleancould never have caught first-row truncation — it now pinsstatusRows.web: 92 files / 1316 tests. Root + web typecheck clean.