-
feat(cli,tui): issue #12 — presenter contract semantics amendment (v0.2.0)
released this
2026-05-23 16:13:55 -07:00 | 266 commits to main since this releaseReplaces the stateless _render_event / _render_event_to_log helpers with
stateful per-turn presenters (CliPresenterState / TuiPresenterState).
Coalesces thinking-event deltas into a single growing display per run;
demotes telemetry events with editorial hierarchy; formats duration +
usage for human reading. Headline behavior change: a 50-token thinking
phase now renders as ONE coalesced growing line in CLI (or one closed
RichLog entry + per-delta live Static widget in TUI), not 50 lines of
[thinking] spam.Editorial promotion line (issue #12 INV-002):
- Load-bearing (no demotion prefix): Text, Done, Error, Cancelled
- Demoted telemetry (
.ASCII prefix in CLI; dim·in TUI):
WorkerPhase, Thinking, TextBoundary, ToolStart, ToolResult
Stateful coalescing:
- Thinking deltas accumulate into thinking_buffer; first non-thinking
event closes the run with a single \n boundary in CLI / one closed
dim RichLog entry in TUI. - TUI adds a dedicated Static(id="thinking-current") widget that shows
the last ~200 chars of the active run, mirroring per-delta updates.
Two-views-of-thinking decoupling per INV-004: chronological RichLog +
always-visible widget. - CLI INV-005: when stdout text was streamed mid-line,
text_written_since_newline triggers a stdout flush + \n before the
next stderr terminal label — guarantees [done] / [error] / [cancelled]
land on their own line in a TTY without breaking pipe-to-file
scripted consumers.
Formatting helpers (issue #12 INV-006 / INV-007):
- _format_duration_ms — autoscale
347ms/5.5s/1.2m - _format_usage — natural-language
6756 in -> 126 out (6882 total, 0 cached)with arrow="->" CLI / "→" TUI
Cross-frontier design pass (eitri-smithy-dev, althing
01KSBE52YZR5E3SPTKA672JE43) returned 16-of-16 confirmed decisions + 4
material divergences applied:- ASCII
.prefix in CLI (·is U+00B7, not ASCII) - RichLog one-closed-entry-per-run + Static per-delta updates (not
inline-mirror as initially proposed) - presenter-state object instead of pure-function rendering
- Framed as "contract semantics amendment", not "polish"
Volva paraphrase round (5 prose-precision fixes applied to
12.contract.md): INV-001 "growing display" semantics; single hide
mechanism for the Static widget (Textual reactivedisplay: bool);
[render_error] security clause (type-only, no exception message);
text_written_since_newline\n-terminated text corner case;
[create_session] integration path (bypasses state.render — not an SSE
Event variant).Volva code-review round (5 findings applied):
- F1 drift: render-exception fallback now writes BOTH a plain-label
fallback line for the original event AND the[render_error] <type>
line (was missing the fallback half). - F2 drift: dim Rich style applied to all demoted-telemetry RichLog
writes viarich.text.Text(..., style="dim")(was plain str). - F3 drift: belt-and-braces widget clear+hide on EVERY terminal event
(Done/Error/Cancelled), even when thinking_open was False. - F4 precision: _format_usage gains PRE-001 assertion on the four
expected usage keys. - F5 precision: _run_turn signature amended in issue #3 contract to
document the newstate: CliPresenterState | None = Nonetest-
injection kwarg.
[create_session] lifecycle line demoted to
. create_session:(written
directly by _amain; bypasses state.render since it's not a wire-level
SSE Event variant). Pre-amendment _render_event / _render_event_to_log
and their test classes removed under the no-backwards-compat rule.Issues #3 and #4 contracts amended in-place: #3 (CliPresenterState
CLASS + FN block + helper FN blocks + _run_turn signature + _amain
create_session demotion); #4 (TuiPresenterState CLASS + FN block +
compose Static widget + _stream_turn_worker state construction).209 tests GREEN; ruff clean. Bumps v0.1.0 → v0.2.0 (minor — output
shape change breaks pre-amendment grep patterns like[thinking] ';
no public API surface change beyond the rendering contract).Persistent-memory commit-along: captures the issue #12 decision,
forward direction (require end_user_id for every access — declined
worldtree-dev's requires_end_user_id offer because we'll send it
universally), and the Heimdall scope-model foot-gun note (the
"per-Tier-1-agent scope add" diagnosis was a phantom ask resolved by
worldtree-dev's correction; agent.call:* baseline covers all Tier 1).Downloads