Skip to content

fix: strip ANSI escapes before parsing token counts and model banners - #33

Merged
usjoh merged 1 commit into
mainfrom
fix/strip-ansi-before-parsing
Aug 16, 2026
Merged

fix: strip ANSI escapes before parsing token counts and model banners#33
usjoh merged 1 commit into
mainfrom
fix/strip-ansi-before-parsing

Conversation

@usjoh

@usjoh usjoh commented Aug 16, 2026

Copy link
Copy Markdown
Owner

The third defect from the upstream audit (see #32 for the first two). Confirmed live on main — not theoretical:

parse_token_count("tokens used: \x1b[1m1,234\x1b[0m")     -> None
parse_token_count("\x1b[2mtokens used:\x1b[0m 1,234")     -> None
parse_reported_model("model: \x1b[36mgpt-5.6-sol\x1b[0m") -> '\x1b[36mgpt-5.6-sol\x1b[0m'

Why this matters more than a missing token count

The two failure modes are not equally bad.

An escape around the label breaks the literal match, and the fact is simply lost — annoying, visible as a blank.

An escape around the value still matches. The capture group swallows the escape codes and writes them into reported_model, so the eval row carries a model id that can never match a known model. That is silent corruption of exactly the attribution PR #29 worked to make reliable, and it surfaces in the taxonomy's unattributed quarantine looking like an entirely different problem.

Workers are spawned on a pipe, so this needs a harness that colorizes unconditionally or is told to by FORCE_COLOR/CLICOLOR_FORCE — but nothing in Ringer prevents that, and the failure is invisible when it happens.

The fix

Strip at parse time, never at capture time. worker.log is the raw record of what the worker emitted, and rewriting it would break the documented invariant that captured worker output is never modified (README, redact_spec).

strip_ansi short-circuits when no ESC byte is present, so the common path is a single substring scan. Coverage: CSI (\x1b[...m), OSC (BEL- or ST-terminated, e.g. terminal-title sequences), and two-character escapes.

Also applied to codex_usage_from_log: a colorized JSON stream puts escapes ahead of the {, the event is skipped, and ask reports no model use at all.

Verification

  • Full suite green: 336 tests
  • Prove-fail: with ringer.py reverted and the new tests kept, all four fail — including AssertionError: '\x1b[36mgpt-5.6-sol\x1b[0m' != 'gpt-5.6-sol'
  • Over-stripping guard: a test pins that text merely containing [1m] without an ESC byte is returned untouched, so ordinary prose and log lines are never mangled

Upstream: PR NateBJones-Projects#99 (unmerged).

🤖 Generated with Claude Code

… banners

Workers are spawned on a pipe, but a harness that colorizes unconditionally —
or is told to by FORCE_COLOR/CLICOLOR_FORCE — writes SGR codes into the exact
banners and summaries these regexes read. Two distinct failures:

  parse_token_count("tokens used: \x1b[1m1,234\x1b[0m")  -> None
  parse_reported_model("model: \x1b[36mgpt-5.6-sol\x1b[0m") -> "\x1b[36mgpt-5.6-sol\x1b[0m"

The second is the dangerous one. An escape around the LABEL breaks the match
and the fact is merely lost; an escape around the VALUE still matches and
captures the escapes INTO the value, so a model id that can never match a
known model is written to the eval row. That is silent corruption of exactly
the attribution PR #29 worked to make reliable, and it lands in the taxonomy's
unattributed quarantine looking like a different problem.

Strip at PARSE time, never at capture time: worker.log is the raw record of
what the worker emitted, and rewriting it would break the documented invariant
that captured worker output is never modified. strip_ansi short-circuits when
no ESC is present, so the common path is one substring scan.

Covers CSI, OSC (BEL- or ST-terminated) and two-character escapes, and applies
to codex_usage_from_log too — a colorized JSON stream puts escapes ahead of the
'{' and `ask` reports no model use at all. A guard test pins that text merely
containing "[1m]" without ESC is left alone, so this cannot over-strip.

Matches upstream PR NateBJones-Projects#99, which is unmerged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@usjoh
usjoh merged commit 012ebab into main Aug 16, 2026
2 of 3 checks passed
@usjoh
usjoh deleted the fix/strip-ansi-before-parsing branch August 16, 2026 11:57
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