vh 9d469d5c67 contract(issue#4): author ratatoskr.tui shell + amend issue #3 cli
Issue #4: Textual TUI shell — the interactive primary presenter
(design-brief §1, §5). Single chat-pane App[int] subclass + sync
run_tui(args) entry. Composes existing sessions + sse_client modules
(no forked API-consumption code, per design-brief §8b).

Six FN blocks: run_tui, RatatoskrApp class + on_mount + on_unmount,
on_input_submitted, _stream_turn_worker, _render_event_to_log,
action_interrupt, action_quit, _cancel_via_sse.

Nine hard invariants codifying:
- INV-001: lazy-import boundary so cli.py STILL doesn't import textual
  at module scope (issue #3's INV-001 carried forward)
- INV-002: session-identity-always-visible footer (`<agent> · …<tail8>`)
  with explicit `<unknown>` carve-out for --session without --agent
- INV-003: two-stage Ctrl-C state machine (idle/streaming/cancelling)
  per design-brief §8c
- INV-005: markdown default-on with --raw opt-out; deliberately produces
  streaming-deltas + post-Done markdown re-render (accepted v1 trade-off,
  Static-then-commit refactor deferred)
- INV-007: one AsyncClient per app lifetime
- INV-008: mid-session errors → idle (don't exit); only initial
  session-create errors exit

Concurrent in-place amendment of issue #3's contract:
- --send becomes optional; when omitted, send_content=None is the
  TUI-mode marker
- --raw flag added to ParsedArgs
- main dispatches via lazy `from ratatoskr.tui import run_tui` when
  send_content is None
- _parse_args + main TESTS sections updated (no_send_marks_tui_mode
  replaces usage_no_send; new raw_flag_default_false / raw_flag_set /
  no_send_dispatches_to_tui)

Volva paraphrase round on issue #4: 5 findings, all amended.
(1) INV-002 `<unknown>` carve-out wording.
(2) Idle "Ctrl-C twice to exit" hint kept per design-brief §8c's
    conservative-by-design rationale; INV-003 spells out the
    intentional one-press-from-idle discrepancy.
(3) Markdown double-render trade-off made explicit in INV-005.
(4) Submit-during-streaming now writes `[busy] turn in flight; input
    ignored` (visible notice, not silent swallow).
(5) `{!r:.200}` format spec kept with explanatory inline comment.

Both contracts drift-check clean. prd: pinned to issue #4 body SHA
b1e73e7d2e3dd453 at 2026-05-21T06:21:37+00:00.
2026-05-21 00:31:15 -07:00

Ratatoskr

A Worldtree Conversation API debug TUI. Runs up and down Worldtree's API surface — sessions, turns, persona, tools, admin events, Bifrost state — carrying messages between layers. Like the squirrel.

The product is the observability surface; chat is the input mechanism. Devs run Ratatoskr against a local Worldtree to watch a turn flow through every layer of the system, side-by-side, in one terminal.

Status

v0 scaffold. Design locked; implementation hasn't started. The dev team owns the implementation pass.

Read in this order

  1. docs/design-brief.md — the locked design. Read this first. Every architectural decision is recorded with its rationale, the alternatives considered, and (where relevant) the operator's lock-in moment.
  2. docs/SPEC-PIN.md — what Worldtree spec version Ratatoskr is built against, where the vendored snapshot lives, and how to bump the pin.
  3. docs/conversation-api-spec.md — the vendored Worldtree spec snapshot. Read this to understand the API surface Ratatoskr consumes. Do not import anything from a Worldtree checkout — the boundary is the spec, not the code. See docs/design-brief.md §2.
  4. CLAUDE.md — Claude Code conventions for this repo (mostly inherited from the Corviduo template).
  5. persistent-memory.md — durable intent across context resets. Update as decisions and state evolve.

Quickstart

# 1. Environment
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"

# 2. Verify the spec pin
cat docs/SPEC-PIN.md   # documented Worldtree SHA + bump procedure

# 3. Tests (none yet; scaffold only)
uv run pytest

# 4. Run against a local Worldtree (once implementation lands)
# Worldtree must be running:  python -m core.conversation_api
ratatoskr --agent mimir

What this repo is NOT

  • NOT a polished consumer for Worldtree's end-users — that's the web app.
  • NOT a Worldtree admin tool — admin CLI is separate (sessions_cli.py lives in Worldtree).
  • NOT a featuretracking shadow of the web app — when the Conversation API surface grows, Ratatoskr does not necessarily grow with it.
  • NOT a remote-Worldtree debug client — file-tail surfaces (persona log, server log) assume local-dev posture.

The full negative-clause list lives in docs/design-brief.md §6.

Boundary rule

Ratatoskr depends on three things only:

  • httpx + httpx-sse (network layer)
  • textual (TUI framework)
  • Worldtree's published Conversation API spec at the pinned SHA

Hard rule: no imports from a Worldtree checkout. No core.* imports, no from worldtree.*, no shared models, no submodule of Worldtree. The spec is the entire surface. Boundary smoke test at tests/test_no_worldtree_imports.py enforces this in CI.

Version-skew strategy

The dev team is decoupled from Worldtree's dev team. To detect drift when Worldtree changes the API:

  1. Spec-version pin in pyproject.toml (worldtree-spec-rev). Bump explicitly; bumps are a tracked action.
  2. Recorded-SSE snapshot tests at tests/snapshots/. Captured against a live Worldtree; replayed in CI. Re-record after every pin bump.
  3. Conformance smoke test — boots Worldtree via Docker compose in CI, runs a one-turn happy path. Catches integration-level drift.

See docs/SPEC-PIN.md for the bump procedure.

Consumer-side discoveries

If you find a spec gap, ambiguity, or missing-but-needed endpoint while working on Ratatoskr: route the discovery back to worldtree-dev via althing rather than via PR on Worldtree directly. The separate-team boundary is intentional and helps catch spec gaps that an in-tree consumer would paper over.

  • Worldtree — the API Ratatoskr consumes
  • brokkr-smithy — authored this design brief
  • Skaldsong — another Worldtree API consumer (Python; render-and-aggregate pattern)
  • mead-hall — another Worldtree API consumer (TypeScript; server-side broker)
S
Description
No description provided
Readme 20 MiB
Languages
Python 82%
HTML 17.6%
Shell 0.4%