diff --git a/internal/tui/w3_copy_export_test.go b/internal/tui/copy_export_test.go similarity index 86% rename from internal/tui/w3_copy_export_test.go rename to internal/tui/copy_export_test.go index e355ee4..0816bc5 100644 --- a/internal/tui/w3_copy_export_test.go +++ b/internal/tui/copy_export_test.go @@ -9,10 +9,9 @@ import ( "github.com/BackendStack21/bodek/internal/client" ) -// W3 — clipboard & export. Regression tests for the judge-2 audit -// (see .ux-review/judge2_copy_export.md). +// Clipboard & export. -// F3/P1: raw cards (help/stats) carry styled ANSI — ^Y after /help must +// raw cards (help/stats) carry styled ANSI — ^Y after /help must // copy the last real reply, never the card. func TestLastReplySkipsRawCards(t *testing.T) { m := newTestModel() @@ -30,7 +29,7 @@ func TestLastReplySkipsRawCards(t *testing.T) { } } -// F1/F8: exports never silently overwrite — same base gets -1, -2… suffixes. +// Exports never silently overwrite — same base gets -1, -2… suffixes. func TestWriteExportNeverOverwrites(t *testing.T) { dir := t.TempDir() p1, err := writeExport(dir, "sess-abc123", "md", []byte("one")) @@ -56,7 +55,7 @@ func TestWriteExportNeverOverwrites(t *testing.T) { } } -// F1/P0: /export exists in the registry with an honest format guard. +// /export exists in the registry with an honest format guard. func TestExportCommandRegistered(t *testing.T) { found := false for _, c := range slashCommands() { @@ -77,7 +76,7 @@ func TestExportCommandRegistered(t *testing.T) { } } -// F2/P0: remote sessions skip exec helpers — the clipboard that matters is +// remote sessions skip exec helpers — the clipboard that matters is // on the machine running the terminal. func TestClipboardToolRemotePrefersOSC52(t *testing.T) { t.Setenv("SSH_TTY", "/dev/ttys004") diff --git a/internal/tui/cosmetics_test.go b/internal/tui/cosmetics_test.go index 2b73f01..e44a9fa 100644 --- a/internal/tui/cosmetics_test.go +++ b/internal/tui/cosmetics_test.go @@ -10,8 +10,7 @@ import ( "github.com/muesli/termenv" ) -// ── C2: the first-run home signposts the core interactions ──────────────── - +// the first-run home signposts the core interactions ─────────────── // TestWelcomeTipSignpostsCoreInteractions: the welcome tip must name the // core composer interactions and command discovery. func TestWelcomeTipSignpostsCoreInteractions(t *testing.T) { @@ -21,7 +20,7 @@ func TestWelcomeTipSignpostsCoreInteractions(t *testing.T) { t.Errorf("welcome tip missing %q:\n%s", want, out) } } - // C11: the ^K microcopy reads as a verb, not jargon. + // the ^K microcopy reads as a verb, not jargon. if !strings.Contains(out, "^K commands") { t.Errorf("welcome tip should identify the command palette:\n%s", out) } @@ -30,8 +29,7 @@ func TestWelcomeTipSignpostsCoreInteractions(t *testing.T) { } } -// ── C4: one failure glyph ────────────────────────────────────────────────── - +// one failure glyph ───────────────────────────────────────────────── // TestFailureGlyphStandardized: ✗ is the single failure glyph; the lamp // set must not carry the stray ✕. func TestFailureGlyphStandardized(t *testing.T) { @@ -40,8 +38,7 @@ func TestFailureGlyphStandardized(t *testing.T) { } } -// ── C5: lamp glyphs belong to the connection state alone ────────────────── - +// lamp glyphs belong to the connection state alone ───────────────── // TestLampGlyphsReserved: no other surface may reuse ● ◉ ◌ ○ — those four // cells are the connection lamp's vocabulary. func TestLampGlyphsReserved(t *testing.T) { @@ -61,8 +58,7 @@ func TestLampGlyphsReserved(t *testing.T) { } } -// ── C3: header instruments get a first-turn decoder ─────────────────────── - +// header instruments get a first-turn decoder ────────────────────── // TestCtxHintOnFirstPrompt: the first real prompt teaches what the ctx // gauge and the connection lamp mean. func TestCtxHintOnFirstPrompt(t *testing.T) { @@ -88,8 +84,7 @@ func TestCtxHintOnFirstPrompt(t *testing.T) { } } -// ── C11: microcopy ──────────────────────────────────────────────────────── - +// microcopy ─────────────────────────────────────────────────────── // TestSwarmHintMicrocopy: the swarm tip speaks in plain verbs. func TestSwarmHintMicrocopy(t *testing.T) { if strings.Contains(hintSwarmText, "registry") || strings.Contains(hintSwarmText, "chips") { @@ -113,8 +108,7 @@ func TestElapsedCarriesLabel(t *testing.T) { } } -// ── C1: the faint contract — body text never renders in faint ──────────── - +// the faint contract — body text never renders in faint ─────────── // TestStepArgNotFaint: step arguments are machine-voice secondary text and // must take muted, not the chrome-only faint token. func TestStepArgNotFaint(t *testing.T) { @@ -127,8 +121,7 @@ func TestStepArgNotFaint(t *testing.T) { } } -// ── C9: the queued count lives in the footer alone ──────────────────────── - +// the queued count lives in the footer alone ─────────────────────── // TestApprovalHeadDropsQueuedChip: with a queue behind the head approval, // the card head no longer repeats the count the footer already carries. func TestApprovalHeadDropsQueuedChip(t *testing.T) { @@ -138,7 +131,7 @@ func TestApprovalHeadDropsQueuedChip(t *testing.T) { if strings.Contains(body, "queued") { t.Errorf("approval card head still carries the queued count:\n%s", body) } - // F1: the shelf chip is the single owner of the PROMPT-queue count — the + // the shelf chip is the single owner of the PROMPT-queue count — the // footer must not repeat it (the approval head's own queue hint stays). foot := plain(m.footer()) if strings.Contains(foot, "▸") && strings.Contains(foot, "queued") { diff --git a/internal/tui/w1_hardening_test.go b/internal/tui/hardening_test.go similarity index 98% rename from internal/tui/w1_hardening_test.go rename to internal/tui/hardening_test.go index 2076261..8f6427e 100644 --- a/internal/tui/w1_hardening_test.go +++ b/internal/tui/hardening_test.go @@ -8,8 +8,7 @@ import ( "github.com/charmbracelet/lipgloss" ) -// W1 hardening — sanitize coverage, width-discipline, and wire-field hygiene. -// Regression tests for the judge-5 robustness audit (see .ux-review/). +// Hardening — sanitize coverage, width-discipline, and wire-field hygiene. func TestSanitizeStripsInvisibleClasses(t *testing.T) { cases := []struct{ name, in, want string }{ diff --git a/internal/tui/w6_hostile_test.go b/internal/tui/hostile_terminal_test.go similarity index 87% rename from internal/tui/w6_hostile_test.go rename to internal/tui/hostile_terminal_test.go index 6569ba6..ef356b5 100644 --- a/internal/tui/w6_hostile_test.go +++ b/internal/tui/hostile_terminal_test.go @@ -7,10 +7,9 @@ import ( "github.com/charmbracelet/lipgloss" ) -// W6 — hostile terminals (contained items). Regression tests for the -// judge-5 audit E1 and the statusLine clamp. +// Hostile terminals (contained items), plus the statusLine clamp. -// E1: below the old 3-row viewport floor the View was taller than the +// below the old 3-row viewport floor the View was taller than the // terminal — permanent scroll jitter in alt-screen. The View must fit at // any height ≥ the minimum layout. func TestViewFitsShortTerminals(t *testing.T) { diff --git a/internal/tui/model.go b/internal/tui/model.go index 299f1d6..bfbd1ee 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -1389,7 +1389,7 @@ func (m *Model) relayout() { vpH := m.height - headerHeight - footerHeight - m.inputAreaHeight() - sheet // Degradation ladder, step 1: when the layout doesn't fit, give the // transcript rows back by shrinking the composer first — a one-visible- - // row terminal beats a View that never fits (judge-5 E1). + // row terminal beats a View that never fits. if vpH < 1 && m.ta.Height() > 1 { over := 1 - vpH m.ta.SetHeight(max(1, m.ta.Height()-over)) diff --git a/internal/tui/quit_confirm_test.go b/internal/tui/quit_confirm_test.go index 9b3c618..3cde10e 100644 --- a/internal/tui/quit_confirm_test.go +++ b/internal/tui/quit_confirm_test.go @@ -88,7 +88,7 @@ func TestQuitGateOtherKeyDisarms(t *testing.T) { } // The gate eats exactly one decision key: the disarming printable rune // falls through to the composer, so "esc/^C, keep typing" never loses a - // character (judge-3 F2). + // character. if got := m.ta.Value(); got != "n" { t.Errorf("disarm keypress did not reach the input: %q", got) } diff --git a/internal/tui/round4_events_test.go b/internal/tui/telemetry_events_test.go similarity index 96% rename from internal/tui/round4_events_test.go rename to internal/tui/telemetry_events_test.go index 51ea4e8..14cc370 100644 --- a/internal/tui/round4_events_test.go +++ b/internal/tui/telemetry_events_test.go @@ -6,7 +6,7 @@ import ( "github.com/BackendStack21/bodek/internal/client" ) -// Round-4 wave-1 regressions: runCtxCum reset on error, usage straggler +// Telemetry event regressions: runCtxCum reset on error, usage straggler // must not open an orphan turn, stale wakeArmed must not mislabel. func TestErrorResetsRunCtxCum(t *testing.T) { diff --git a/internal/tui/transcript_fixes_test.go b/internal/tui/transcript_fixes_test.go index 0fd4bab..67e6c68 100644 --- a/internal/tui/transcript_fixes_test.go +++ b/internal/tui/transcript_fixes_test.go @@ -8,8 +8,7 @@ import ( "github.com/BackendStack21/bodek/internal/client" ) -// ── F1: the queue count has a single owner — the shelf chip ──────────────── - +// the queue count has a single owner — the shelf chip ─────────────── // TestQueueCountSingleOwner: with prompts queued mid-turn, the shelf chip is // the ONLY surface carrying the count — the status line and the footer must // not repeat it. @@ -31,8 +30,7 @@ func TestQueueCountSingleOwner(t *testing.T) { } } -// ── F2: the status line never hides on disconnect ────────────────────────── - +// the status line never hides on disconnect ───────────────────────── // TestStatusLineReconnectState: while disconnected the status line renders // the reconnect state in-place instead of vanishing — and never shows the // normal busy spinner label. @@ -67,8 +65,7 @@ func TestStatusLineReconnectState(t *testing.T) { } } -// ── F3: one steady new-output row — no insert/remove reflow ──────────────── - +// one steady new-output row — no insert/remove reflow ─────────────── // TestNewOutputRowSteady: the new-output indicator lives on ONE footer row // that never inserts or removes a line — the layout height must not change // when the busy state toggles while scrolled up. @@ -101,8 +98,7 @@ func TestNewOutputRowSteady(t *testing.T) { } } -// ── F4: a failed turn marks its head ─────────────────────────────────────── - +// a failed turn marks its head ────────────────────────────────────── // TestFailedTurnHeadMarked: an error event on the streaming turn sets a // sanitized failed flag that paints ✗ on the turn head and survives // finalization within this session. (Replay does not restore it: the @@ -143,8 +139,7 @@ func TestFailedTurnHeadMarked(t *testing.T) { } } -// ── F5: visible notices are capped to one line ───────────────────────────── - +// visible notices are capped to one line ──────────────────────────── // TestNoticeCapOneLine: only the latest unexpired notice renders, folded into // a single line; older ones collapse into a count instead of stacking. func TestNoticeCapOneLine(t *testing.T) { diff --git a/internal/tui/view.go b/internal/tui/view.go index 9c71f2c..fd5d23b 100644 --- a/internal/tui/view.go +++ b/internal/tui/view.go @@ -59,8 +59,7 @@ func (m *Model) plainView() string { return strings.Join(parts, "\n") } -// ── header ───────────────────────────────────────────────────────────────── - +// ── header ──────────────────────────────────────────────────────────────── func (m *Model) header() string { th := m.th // The logo gradient is width-independent, so render it once and cache it @@ -334,7 +333,7 @@ func (m *Model) statusLine() string { return "" } th := m.th - // F2: a dropped socket is exactly when the reader needs this row — + // a dropped socket is exactly when the reader needs this row — // instead of hiding, the status line owns the reconnect state in-place. if m.disconn { label := "◌ disconnected · ⏎ retry" @@ -390,8 +389,7 @@ func (m *Model) statusLineVisible() bool { return (m.busy || m.disconn) && m.curApproval() == nil } -// ── transcript ─────────────────────────────────────────────────────────── - +// ── transcript ────────────────────────────────────────────────────────── // streamRenderInterval is the coalescing window for high-frequency streaming // events (tokens, thinking): instead of rebuilding the viewport — which // re-runs glamour on the streaming tail — per event, they share one rebuild. @@ -1486,8 +1484,7 @@ func (m *Model) renderNotices() string { return line } -// ── input / approval area ────────────────────────────────────────────────── - +// ── input / approval area ───────────────────────────────────────────────── func (m *Model) inputArea() string { box := m.th.inputBox.Width(m.cardWidth()).Render(m.ta.View()) var above []string @@ -1643,8 +1640,7 @@ func (m *Model) approvalBody() string { return strings.Join(lines, "\n") } -// ── footer ───────────────────────────────────────────────────────────────── - +// ── footer ──────────────────────────────────────────────────────────────── func (m *Model) footer() string { return ansi.Truncate(m.footerContent(), max(1, m.width), "") } @@ -2025,8 +2021,7 @@ func (m *Model) panelFooter(hints ...string) string { return ansi.Truncate(out+last, max(1, m.width), "") } -// ── small helpers ────────────────────────────────────────────────────────── - +// ── small helpers ───────────────────────────────────────────────────────── // plural renders a count with its noun inflected: plural(1, "match", // "matches"), plural(3, "approval", "approvals"). Every "%d " surface // goes through this so a single hit can never read as "1 matches" again. diff --git a/internal/tui/w2_visible_state_test.go b/internal/tui/visible_state_test.go similarity index 87% rename from internal/tui/w2_visible_state_test.go rename to internal/tui/visible_state_test.go index 2f63028..8a32ae1 100644 --- a/internal/tui/w2_visible_state_test.go +++ b/internal/tui/visible_state_test.go @@ -7,10 +7,9 @@ import ( "github.com/BackendStack21/bodek/internal/client" ) -// W2 — visible state & the escape stack. Regression tests for the -// judge-3 interaction audit (see .ux-review/judge3_interaction.md). +// Visible state & the escape stack. -// F1/P0: an armed stop-agent gate must be visible when armed from the +// an armed stop-agent gate must be visible when armed from the // composer (panel == none) — footer carries the headline and the y confirm. func TestStopAgentGateVisibleFromComposer(t *testing.T) { m := newTestModel() @@ -22,7 +21,7 @@ func TestStopAgentGateVisibleFromComposer(t *testing.T) { } } -// F2/P1: a disarmed printable rune falls through to the composer — the gate +// a disarmed printable rune falls through to the composer — the gate // eats exactly one decision key, never the user's first keystroke. func TestDisarmedRuneFallsThroughToComposer(t *testing.T) { m := newTestModel() @@ -41,7 +40,7 @@ func TestDisarmedRuneFallsThroughToComposer(t *testing.T) { } } -// F4/P1: lowercase n is next (vim/less reflex), never query corruption. +// lowercase n is next (vim/less reflex), never query corruption. func TestFindNextBinding(t *testing.T) { m := newTestModel() m.msgs = append(m.msgs, @@ -65,7 +64,7 @@ func TestFindNextBinding(t *testing.T) { } } -// F10/P2: queue deletes are two-step — first d arms, second d deletes. +// queue deletes are two-step — first d arms, second d deletes. func TestQueueDeleteTwoStep(t *testing.T) { m := newTestModel() m.queue = []string{"alpha", "beta"} @@ -108,7 +107,7 @@ func TestEventsSelectionRendered(t *testing.T) { } } -// F3/P1: /help teaches only real keys — no phantom r, and the marquee +// /help teaches only real keys — no phantom r, and the marquee // features (^S stop sub-agent, alt+f find, ^K palette) are listed. func TestHelpTeachesRealKeys(t *testing.T) { m := newTestModel() @@ -124,7 +123,7 @@ func TestHelpTeachesRealKeys(t *testing.T) { } } -// F7/P2: turn jumps name their landing spot — the copy target (alt+y) is +// turn jumps name their landing spot — the copy target (alt+y) is // verifiable on screen. func TestJumpTurnReportsLanding(t *testing.T) { m := newTestModel()