Skip to content

Record verdicts, promote lessons and choose round lessons in the TUI (#174) - #253

Merged
memtomem merged 4 commits into
mainfrom
feat/174-tui-judge-promote-lessons
Nov 5, 2026
Merged

memtomem merged 4 commits into
mainfrom
feat/174-tui-judge-promote-lessons

Conversation

@memtomem

@memtomem memtomem commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Follow-up 2 for #174 (after #245 and #250): the TUI Rooms tab gets the three human review actions the CLI already has. #174 stays open for goal-scoped lessons in rooms.

What changes

j: record a verdict (room judge)

  • Works only when the room's outcome is applied-not-reviewed, accepted or rejected. Any other outcome is refused with its status and reason.
  • The dialog shows the applied candidate, its patch digest and the current outcome block (who, when, why) that a new verdict would supersede.
  • The verdict has no default, so a stray key cannot record one. The rationale is optional and goes to the store as typed (the CLI's rules).
  • Recorded with surface="tui" and fenced on the patch digest the dialog showed. Repeating the current verdict changes nothing.

p: promote a lesson (room promote)

  • Needs the board. When the board is disabled the TUI says so; when it is enabled but failed to open, it shows the open error.
  • Needs an accepted or rejected verdict.
  • The dialog shows the provenance the lesson will record (room, candidate, verdict, digest, judgment id, apply run).
  • Supersede choices are only this project's active lessons. active_decisions(project_id=) also returns unbound decisions, which the board refuses to let a project lesson replace, so those are filtered out. A target with a different scope is refused inside the dialog.
  • RoomCoordinator.promote_lesson gains expected_judgment_id, checked on the same outcome read the provenance comes from. If the verdict changes while the dialog is open (for example from another terminal), the promotion is refused rather than recorded under a verdict the person did not see. The CLI is unchanged.

x: lessons in TUI rounds

  • The round dialog reads the project's approved lessons once and shows one checkbox each, with its source.
  • On every checkbox, prompt or lineage change it recomputes the CLI's select_lessons(skip=unchecked). Unchecking one of the newest 20 lets an over-budget older lesson in, exactly as --skip-lesson does.
  • Deliberate deviation from the CLI: when the prompt leaves no room for the checked lessons, the dialog refuses to start and stays open. The CLI instead drops them and proceeds, but it shows the drop before running; here the dialog had already shown those lessons as going in.
  • The fit check and the launch share round_prompt's fallback rule, so a whitespace-only prompt is measured as the creation prompt it will become.
  • The worker gets exactly the shown (id, text) pairs over the existing --lessons-fd path, with no second board read.
  • The resumed-conversation history note now appears inside the dialog for the chosen lineage, before you confirm, instead of after launch. A board that is off or unreadable is stated in the dialog.

Other changes

  • The round, verdict and promote dialogs capture the room id and store root when they open, and their callbacks act on those. The round dialog used to launch on whatever room was selected at confirmation, which a poll could have changed.
  • The room pane shows its outcome. The Compare/Details evidence snapshot now also compares the room's judgment count: a verdict changes neither revision nor candidates, so a verdict recorded elsewhere used to leave the snapshot showing the old outcome without a hint. A count that cannot be read, at build time or now, gives a "cannot tell" line instead of taking the pane down.
  • Fixed (pre-existing, included on request): _refresh_board read active_decisions() and the selected item outside its BoardError guard, so a failing decisions table killed the app on a Board-tab poll.
  • Textual's Checkbox parses a str label as markup. Lesson labels go through plain(...), and test_tui_render.markup_sinks now flags any non-literal Checkbox label.

Design and review

  • Codex design review, R1: 7 Majors, all adopted after checking the source:
    • promotion bound to the judgment the dialog showed;
    • skip-before-budget parity;
    • one prompt fallback rule for the fit check and the launch;
    • callbacks act on the captured room;
    • the full error boundary;
    • Checkbox markup;
    • the freshness read degrading locally.
  • Codex design review, R2: 2 Majors, the unbound-decision filter and the Board-tab guard (the latter pre-existing; included by decision).
  • Implementation R1: 2 Majors and 4 Minors, all fixed in the second commit.
    • Majors: a round submission did not catch the store's full error boundary, and a snapshot that could not count verdicts silently skipped the check.
    • Minors: board-off text, the outcome block in the verdict dialog, an 80×24 mouse test, and narrower doc wording.
  • Implementation R2: SHIP, no findings.

Tests

  • New tests/test_tui_room_review.py (32 tests) on a real RoomCoordinator and a real Board:
    • verdict states, digest/store/room fences, idempotent repeat;
    • a dropped judgments table on open and on submit;
    • promote: board off or failed, unjudged, provenance, idempotent retry, verdict changed mid-dialog, supersede filtering and scope;
    • board failures after promotion and on a Board-tab poll;
    • lesson budget parity with 21 lessons;
    • fit refusal and recovery, the blank-prompt fallback, an unknown lesson id;
    • markup and bidi in labels, an 80×24 layout plus a mouse toggle-and-submit;
    • history note per lineage, snapshot freshness;
    • coordinator expected_judgment_id and judgment_count.
  • Updated tests:
    • test_tui.py: round-funnel tests use the captured-room signature, and the evidence fakes return a judgment count.
    • test_tui_render.py: the Checkbox guard cases.
  • 25 mutations of the new guards, each caught by a test.
  • docs/assets/tui/09–12 re-rendered: the Rooms action bar gained j verdict | p promote and wraps to a second line at 120 columns, as the bar is designed to.
  • Full suite via suite-run: CLEAN. ruff check . passes.

Docs

CHANGELOG (Added + Fixed), docs/user-guide.md + .ko.md (TUI Rooms keys, lessons in TUI rounds), docs/architecture/tui.md, CLAUDE.md.

🤖 Generated with Claude Code

memtomem and others added 4 commits November 5, 2026 10:02
…174)

The Rooms tab gains the human review actions the CLI already has:

- j records accepted/rejected on an applied room (RoomCoordinator.judge,
  surface="tui"), fenced on the patch digest the dialog showed.
- p promotes a lesson from a judged room; promote_lesson gains
  expected_judgment_id, checked on the same outcome read as the provenance,
  so a verdict changed while the dialog was open is never promoted.
- The round dialog lists the project's approved lessons with checkboxes,
  recomputes select_lessons(skip=unchecked) live, refuses (never drops)
  when round_lessons_fit fails under round_prompt's fallback rule, and the
  worker gets exactly the shown pairs. The history note moves into the
  dialog, per lineage, before confirmation.

Every Rooms dialog callback acts on the room id and store root captured
when it opened (the round dialog used the ambient selection). The room pane
shows the outcome; the evidence snapshot also compares the judgment count,
degrading locally. _refresh_board keeps every read inside its guard.
Checkbox labels from stored text go through plain(), and markup_sinks
flags any other non-literal label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A round submission catches the room store's error boundary, so a room
  row that turned unreadable after the dialog opened is reported, not raised
  out of the callback.
- An evidence snapshot whose build could not count verdicts says it cannot
  tell whether a verdict changed, instead of silently skipping the check.
- The round dialog says when the board is off; the verdict dialog shows the
  current outcome block (who, when, why) it would supersede.
- An 80x24 test toggles a lesson and submits with the mouse.
- Docs: only the round, verdict and promote dialogs capture the room and
  store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Checkbox label never wraps, so a lesson longer than the dialog was cut
off at its edge while every word of it was sent to every member (PR review:
a 401-character lesson made a 421-column checkbox in a 66-column body). The
checkbox label is now just the lesson id; the full text is a wrapping
PlainStatic under it. The lessons heading was a 108-column PlainLabel that
widened the body too; it is a PlainStatic now. At 80x24 the body no longer
scrolls sideways and a 410-character lesson wraps to seven lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The round prompt, verdict rationale and lesson editors kept TextArea's
default 1fr height inside a scrolling dialog body, where it collapses to
its border: at 80x24 all three had a content height of 0, so typing changed
the value without showing it (PR review). They now have fixed heights
(8 rows for the prompt, 6 for the other two), and a test types into each
at 80x24 and 120x40 and checks the words are drawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@memtomem
memtomem merged commit 6706a43 into main Nov 5, 2026
11 checks 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