Skip to content

feat(harness): surface the whole statusline run, not just its first row - #60

Merged
AltanS merged 2 commits into
mainfrom
feat/statusline-rows
Jul 30, 2026
Merged

feat(harness): surface the whole statusline run, not just its first row#60
AltanS merged 2 commits into
mainfrom
feat/statusline-rows

Conversation

@AltanS

@AltanS AltanS commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Follow-up to #56, which raised the statusline-run bound but left extractStatusLine returning only
the run's first row. Everything after it — model, cwd, git branch, permission mode — was stripped
off the mirror by stripChrome and rendered nowhere.

Verified on a real host whose statusline is 3 rows: only CTX:20% CACHE:100% LIMITS … was ever
visible; [Opus·medium] ~/… on main* and ⏵⏵ bypass permissions on … · ← 4 agents were not.

Contract: extractStatusLine(lines): string | nullextractStatusLines(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: InputBox gains statusEnd, handed out by the walk that already knows where the run
stops rather than re-derived, so the background-agents footer below the blank separator still cannot
leak in. stripChrome is 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: boolean could never have caught first-row truncation — it now pins statusRows.

web: 92 files / 1316 tests. Root + web typecheck clean.

AltanS and others added 2 commits July 31, 2026 00:11
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>
@AltanS
AltanS merged commit 1a6f532 into main Jul 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant