Skip to content

Fix: the flag message run gets wrong, and five contracts documented wrong - #64

Merged
0xLeif merged 1 commit into
mainfrom
leif/post-0.9.0-findings
Aug 18, 2026
Merged

Fix: the flag message run gets wrong, and five contracts documented wrong#64
0xLeif merged 1 commit into
mainfrom
leif/post-0.9.0-findings

Conversation

@0xLeif

@0xLeif 0xLeif commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The last five findings from the nine-language translation dogfood, each re-verified independently before anything was touched.

The question asked of every one was code or documentation, and that distinction is the whole point of the PR. A sibling finding earlier this week claimed --settle-ms was broken because it is inert under --wait-for-regex; the code was right, a comment recorded why, and "fixing" it would have reintroduced a measured bug.

All five confirmed. Four are documentation.

finding remedy
rank4 run bounds clean_output and raw_output into different windows docs
rank6 --grep help names a match surface it does not have docs
rank7 the unknown-flag message misdiagnoses a space-separated value code
rank9 omitted_bytes reconciles only on ASCII docs
rank10 --max-output does not bound screen docs

The one code change

rune run --timeout 5 -- echo hi answered:

Unknown option: --timeout. rune's own flags are recognized only before the wrapped command; to pass this one to that command instead, put the command first or use a separator: rune run -- <command> --timeout

Wrong three ways at once: it calls a flag rune owns "Unknown", it asserts a position error when the flag was already before the separator, and following its remedy literally hands --timeout to the child instead of applying a timeout. Two agents lost tool calls to it; one nearly filed it as a broken flag.

Now:

--timeout takes its value inline: --timeout=VALUE. To pass it to the command instead: rune run -- <command> --timeout VALUE

The known-flag set is derived from FLAG_PATTERNS rather than hand-written โ€” a hand-maintained list is exactly what let --context ship accepted-and-ignored.

I predicted rank4 would need code, and was wrong

The per-field budget is a stated contract in four places โ€” the flag itself says "BYTES each" โ€” and a caller sizing a context window wants both fields under the cap. What was missing is any statement of the consequence: a pty turns \n into \r\n and raw_output keeps its escapes, so the same budget lands at different points in the child's output.

Measured at --max-output=200 on a 5,200-byte ASCII fixture: metadata reports omitted_bytes: 5000, raw_output's own marker says 5070, and the head boundaries differ. With colour, a whole line of the child's output was present in one field and absent from the other, from one invocation.

So clean_output is not strip_ansi(raw_output) under this flag. That is now stated in the help, the spec, and three guides โ€” docs/pty_architecture.md asserted the identity unconditionally.

Deliberately not fixed

Each of these was measured, and the fix came out worse than the limitation:

  • rank4 as code โ€” deriving clean_output from the bounded raw (what session read does) would cut readable payload by the ANSI fraction on every colour-emitting child, against the flag's stated purpose.
  • rank6 as code โ€” grepping the screen loses scrollback, which is the reason --grep exists. Measured: 39 of 200 lines were on screen.
  • rank9 as code โ€” making the arithmetic reconcile means discarding a split character's fragment, contradicting the scrub invariant; redefining the count changes the marker's rendered length and could flip truncated for callers who changed nothing.
  • rank10 as code โ€” rendering only the bounded bytes paints a discarded frame plus rune's own elision marker into the child's screen, losing 9 of 10 answers.

Both limitations are recorded in ROADMAP.md so they are tracked rather than buried in help text.

Test Plan

  • 573 examples, 0 failures; rubocop clean across 72 files; docs-check green
  • specsync check โ€” 31/31 files, 7115/7115 LOC (100%)
  • --timeout 5 โ†’ new message; --tiemout=5 โ†’ old message unchanged; --timeout=5 โ†’ still applies; -- echo hi --timeout 5 โ†’ still passes through
  • Controls: the inline-value branch removed fails 3 of 4 tests

One control fired unplanned, and it was right. The work order said to derive the known-flag set from FLAG_PATTERNS.keys plus --separate-streams. The drift guard rejected that on its first run: --separate-streams is boolean, so "give the value inline" is nonsense advice for it, and the parser consumes it before this path anyway. The set is the value-taking flags only, and a test now pins that a boolean flag still works normally.

๐Ÿค– Generated with Claude Code

https://claude.ai/code/session_018rf59AtQmJcodUJ6vXDZNY

โ€ฆrong

The last five findings from the nine-language translation dogfood. Each was
re-verified independently, and the question asked of every one was code or
documentation. All five confirmed; four are documentation.

  rank4  run bounds clean_output and raw_output into different windows   DOCS
  rank6  --grep help names a match surface it does not have              DOCS
  rank7  the unknown-flag message misdiagnoses a space-separated value   CODE
  rank9  omitted_bytes reconciles only on ASCII                          DOCS
  rank10 --max-output does not bound screen                             DOCS

The one code change: `run --timeout 5 -- echo hi` said "Unknown option:
--timeout ... put the command first or use a separator", which is wrong three
ways โ€” it calls a flag rune owns Unknown, asserts a position error when the flag
was already before the separator, and its remedy hands --timeout to the child
instead of applying a timeout. It now names the real problem. The known-flag set
derives from FLAG_PATTERNS, because a hand-maintained list is exactly what let
--context ship accepted-and-ignored.

I predicted rank4 would need code and was wrong. The per-field budget is a
stated contract in four places โ€” the flag says "BYTES each" โ€” and a caller
sizing a context window wants both fields under the cap. What was missing is any
statement of the consequence: a pty turns \n into \r\n and raw keeps its
escapes, so the same budget lands at different points and clean_output is not
strip_ansi(raw_output). At --max-output=200 on a 5,200-byte fixture, metadata
said 5000 and raw's own marker said 5070; with colour, a whole line of the
child's output was in one field and absent from the other.

Not fixed, each because the fix measured worse than the limitation: deriving
clean from bounded raw cuts readable payload by the ANSI fraction on every
colour-emitting child; grepping the screen loses scrollback, which is why --grep
exists (39 of 200 lines were on screen); making omitted_bytes reconcile means
discarding a split character's fragment, contradicting the scrub invariant; and
rendering only the bounded bytes paints a discarded frame plus rune's own
elision marker into the child's screen, losing 9 of 10 answers.

Controls: the inline-value branch removed fails 3 of 4 tests. The drift guard
fired unplanned on the first run โ€” the work order said to derive the set from
FLAG_PATTERNS.keys plus --separate-streams, and that flag is boolean, so "give
the value inline" is nonsense for it and the parser consumes it before this path
anyway. The set is the value-taking flags only, and a test now pins that a
boolean flag still works.

573 examples, 0 failures. specsync 31/31 files, 7115/7115 LOC.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018rf59AtQmJcodUJ6vXDZNY
@0xLeif
0xLeif merged commit ad76e22 into main Aug 18, 2026
10 checks passed
@0xLeif
0xLeif deleted the leif/post-0.9.0-findings branch August 18, 2026 05:58
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