What happened
On 2026-09-12, against fix/e2e-question-needles (e05b0ffa5, a branch off dev@0ce30c776 touching only internal/e2e and one manual line), TestQuestionsE2E/AssumptionsStandUntilOneIsStruck on deepseek/deepseek-v4-flash, at 100 columns, drew a three-answer question as one row with the third answer gone and the second cut mid-word:
─ q-assumption · deepseek/deepseek-v4-flash · ⠿ auto · via wafer · main ──────────── esc interrupt ─
≈ I am assuming these before I write the migration ▸1 the ledger is append only 2 nobody reads i…
none of them is written down anywhere I can read
←→ choose · enter take it · esc later · c change · ? ask back · goes on in 10m 0s
The asker sent three options — the ledger is append only, nobody reads it from a second process, the timestamps are UTC — and asked for form card. A person reading that screen cannot see, and cannot discover, that a third assumption is standing.
This is the one-row view's own stated law, broken:
// questionRowOffer is that row: the mark, the head, and every answer beside it,
// the pointed one on the selected ground.
//
// NO ANSWER IS EVER DROPPED OR CUT. A row too narrow for every answer is a row
// the chooser should not have chosen, so it says so by giving the question the
// panel instead ([app.questionRowFits] is asked before this is drawn).
questionRowFits is asked by nobody. On dev:
$ grep -rn 'questionRowFits' internal/tui3/
internal/tui3/question.go:1338:// panel instead ([app.questionRowFits] is asked before this is drawn).
internal/tui3/question.go:1362:// questionRowFits reports whether the one-row view can hold this question's head
internal/tui3/question.go:1366:func questionRowFits(q session.Question, width int) bool {
Two mentions in a comment and the definition. The chooser's last rung is unconditional:
{
view: viewRow,
why: "everything left is a question whose whole decision is its answers' own words",
when: func(a *app, q questionShown, width int) bool { return true },
},
Arithmetic for the screen above, from questionRowFits's own formula: 2 + 47 for the head, plus 3 + 1 + 1 + len(word) per answer — 49 + 30 + 41 + 27 = 147 cells wanted on a 100-cell row. The function would have said no. Nothing asked it.
It is not about the assumption kind. Any question with no consequence, no body, no evidence, no pick-with-a-reason and no call — the properties the ladder's earlier rungs read — falls to the last rung whatever its answers are worth in cells.
Replication
Deterministic (no model). A internal/tui3 unit test is enough, because the ladder is a pure function of the question and the width: build a session.Question with three long labels and no consequence/body/pick-reason/subject-call, and ask questionViewOf(q, 100). It answers viewRow, and plain(strings.Join(a.questionRows(100), "\n")) has the third label missing and the second ending in …. grep -rn 'questionRowFits' internal/tui3/ is the one-line version of the same fact: the law has no caller.
Field (real models). Fires on every run, deterministically, in the suite that found it:
make build
go test -tags e2e -run 'TestQuestionsE2E/AssumptionsStandUntilOneIsStruck' -count=1 -timeout 15m -v ./internal/e2e/
Needs OPENROUTER_API_KEY (resolved by liveKey) and tmux; about nine seconds and a fraction of a cent. It fired identically on two consecutive runs (10:32 and 10:38 UTC, 2026-09-12), with the same steer and the same cut. The assertion it fails is the third assumption: the screen never said "the timestamps are UTC".
Where
questionLadder's last rung and questionViewOf in internal/tui3/questionchooser.go.
questionRowFits and questionRowOffer in internal/tui3/question.go (search NO ANSWER IS EVER DROPPED OR CUT).
- The scenario that catches it:
questionsAssumption in internal/e2e/questions_e2e_test.go.
The fix
The ladder is data and each rung is a property, so this is a property it is missing: a row that will not hold every answer is not a row. A rung above the final one, asking questionRowFits, giving viewPanel — which is what the comment on questionRowOffer has claimed all along.
It is a product change and not only a repair: questions that are drawn as one row today become panels at narrow widths, so it wants the owner's eye and a full internal/tui3 run. TestTheChooserIsOneLadderOfProperties reads the rungs' own prose and will want the new one's reason.
Acceptance
- e2e:
TestQuestionsE2E/AssumptionsStandUntilOneIsStruck passes on deepseek/deepseek-v4-flash at questionCols (100) — the screen carries the timestamps are UTC, spelled in full.
- Unit: a
internal/tui3 law that no width ever draws a question through questionRowOffer whose painted row is wider than the frame — i.e. questionRowFits is true wherever viewRow is chosen. A test that pins one width is a test the next long label walks past.
grep -rn 'questionRowFits' internal/tui3/ names a caller.
Found on 2026-09-12 while un-rotting the questions e2e needles (#998). Not caused by that work — the needle it fails is the timestamps are UTC, which is the asker's own word and has never gone through the needle table.
What happened
On 2026-09-12, against
fix/e2e-question-needles(e05b0ffa5, a branch offdev@0ce30c776touching onlyinternal/e2eand one manual line),TestQuestionsE2E/AssumptionsStandUntilOneIsStruckondeepseek/deepseek-v4-flash, at 100 columns, drew a three-answer question as one row with the third answer gone and the second cut mid-word:The asker sent three options —
the ledger is append only,nobody reads it from a second process,the timestamps are UTC— and asked forform card. A person reading that screen cannot see, and cannot discover, that a third assumption is standing.This is the one-row view's own stated law, broken:
questionRowFitsis asked by nobody. Ondev:Two mentions in a comment and the definition. The chooser's last rung is unconditional:
{ view: viewRow, why: "everything left is a question whose whole decision is its answers' own words", when: func(a *app, q questionShown, width int) bool { return true }, },Arithmetic for the screen above, from
questionRowFits's own formula:2 + 47for the head, plus3 + 1 + 1 + len(word)per answer — 49 + 30 + 41 + 27 = 147 cells wanted on a 100-cell row. The function would have said no. Nothing asked it.It is not about the
assumptionkind. Any question with no consequence, no body, no evidence, no pick-with-a-reason and no call — the properties the ladder's earlier rungs read — falls to the last rung whatever its answers are worth in cells.Replication
Deterministic (no model). A
internal/tui3unit test is enough, because the ladder is a pure function of the question and the width: build asession.Questionwith three long labels and no consequence/body/pick-reason/subject-call, and askquestionViewOf(q, 100). It answersviewRow, andplain(strings.Join(a.questionRows(100), "\n"))has the third label missing and the second ending in….grep -rn 'questionRowFits' internal/tui3/is the one-line version of the same fact: the law has no caller.Field (real models). Fires on every run, deterministically, in the suite that found it:
Needs
OPENROUTER_API_KEY(resolved byliveKey) andtmux; about nine seconds and a fraction of a cent. It fired identically on two consecutive runs (10:32 and 10:38 UTC, 2026-09-12), with the same steer and the same cut. The assertion it fails isthe third assumption: the screen never said "the timestamps are UTC".Where
questionLadder's last rung andquestionViewOfininternal/tui3/questionchooser.go.questionRowFitsandquestionRowOfferininternal/tui3/question.go(searchNO ANSWER IS EVER DROPPED OR CUT).questionsAssumptionininternal/e2e/questions_e2e_test.go.The fix
The ladder is data and each rung is a property, so this is a property it is missing: a row that will not hold every answer is not a row. A rung above the final one, asking
questionRowFits, givingviewPanel— which is what the comment onquestionRowOfferhas claimed all along.It is a product change and not only a repair: questions that are drawn as one row today become panels at narrow widths, so it wants the owner's eye and a full
internal/tui3run.TestTheChooserIsOneLadderOfPropertiesreads the rungs' own prose and will want the new one's reason.Acceptance
TestQuestionsE2E/AssumptionsStandUntilOneIsStruckpasses ondeepseek/deepseek-v4-flashatquestionCols(100) — the screen carriesthe timestamps are UTC, spelled in full.internal/tui3law that no width ever draws a question throughquestionRowOfferwhose painted row is wider than the frame — i.e.questionRowFitsis true whereverviewRowis chosen. A test that pins one width is a test the next long label walks past.grep -rn 'questionRowFits' internal/tui3/names a caller.Found on 2026-09-12 while un-rotting the questions e2e needles (#998). Not caused by that work — the needle it fails is
the timestamps are UTC, which is the asker's own word and has never gone through the needle table.