Convert the sse_client contract into an issue-scoped contract bound to the freshly-filed gitea issue #1. Frontmatter migrates from module-shape (module:/purpose:) to issue-shape (target_module:/scope:/prd:) per CONTRACT-FORMAT §2.1.I. The prd: block pins to issue #1's body SHA-256 (abcbc49467e86f1d at 2026-05-21T03:57:37+00:00); drift check verifies the pin matches the live issue body. scripts/contract_drift_check.py needs pyyaml; added to [dev] in pyproject.toml. Without it the drift check (and the contract parser) fail with ModuleNotFoundError — that's a scaffold hole I'd hit again on a fresh checkout. Also seed 17 default labels on gitea via tea so issue tracking has a working vocabulary out of the gate. Five buckets: Sleipnir gating (ready-for-agent, blocked-needs-contract, blocked-needs-dependency), triage (needs-triage, needs-architect-decision, needs-info), type (bug, enhancement, task, documentation), resolution (duplicate, wontfix, invalid), Ratatoskr-specific area (sse-client, tui, cli, observability). Labels are gitea-side state — not in this commit. Known: contract_parser.py --validate ERRORs on the issue-scoped frontmatter because the parser is v2.0-shape. CONTRACT-FORMAT §2.1.L H10 explicitly marks parser kind-aware validation as a Brokkr-side follow-up. Parser is a canonical-synced file so we do NOT patch it locally (would drift from corviduo-project-template).
9.8 KiB
Persistent memory — ratatoskr
This file captures durable intent and supporting evidence (goals, decisions,
foot-gun warnings, in-flight state) across context resets. Read it at session
start; treat it as one input alongside CLAUDE.md and the auto-memory system,
not as the single source of truth.
When durable state shifts enough to warrant capture, run /snapshot and
commit alongside the next commit per the persistent-memory commit-along rule
in CLAUDE.md.
Repo purpose
Ratatoskr is a dev-grade debug-observability TUI for Worldtree's Conversation API. 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: agent SSE stream, persona/Vili affect dispatch, tool calls, Bifrost handshake state, admin lifecycle events, optional raw server log.
Named after the squirrel that runs up and down Yggdrasil carrying messages between layers. On-the-nose Worldtree resonance (Yggdrasil = the World Tree).
Origin: althing ask from worldtree-dev (thread 01KS3R34XD3N6HMK91VXESHGW7,
2026-05-20) for the shape of a TUI Conversation API consumer. brokkr-smithy
ran the shape pass; operator's reframe routed it as a new repo with a
separate dev team rather than an in-tree Worldtree tool.
Current state / in-flight
Status: scaffold verified + first contract authored. Design is locked;
implementation begins next, TDD against sse_client.contract.md.
What's in the repo:
docs/design-brief.md— the locked design (copy frombrokkr-smithy/docs/ratatoskr-design-brief.md).docs/SPEC-PIN.md— Worldtree spec pin documentation + bump procedure.docs/conversation-api-spec.md— vendored Worldtree spec at the pinned SHA.docs/conversation_api.contract.md— vendored Worldtree server-side contract at the pinned SHA.docs/contracts/issues/1.contract.md— issue-scoped contract for issue #1 (#1). v2.1, complexity=high.target_module: ratatoskr.sse_client.prd:block pins to issue body SHAabcbc49467e86f1dat2026-05-21T03:57:37+00:00. Four FN blocks:stream_turn,reconnect_turn,cancel_turn,_parse_sse_id. Drift check (scripts/contract_drift_check.py) returns clean.pyproject.toml— Python 3.12, hatchling, uv-managed. Deps: httpx, httpx-sse, textual. Dev deps: pytest, pytest-asyncio, respx, ruff, mypy, textual-dev, pyyaml (consumed bydocs/contracts/contract_parser.py+scripts/contract_drift_check.py).src/ratatoskr/__init__.py+cli.py— stubs.tests/test_no_worldtree_imports.py— boundary smoke test (passes; verified 2026-05-20).tests/snapshots/README.md— recording/replay convention for SSE snapshot tests.
What's NOT in the repo yet:
- Implementation of
ratatoskr.sse_client— next move, TDD per the contract's tracer tests. - Gitea remote — operator provided
git@gitea.phasefinal.com:vh/ratatoskr.giton 2026-05-20; about to be added + first push at the same commit as this update. - CLAUDE.md customization — currently using the canonical template's generic CLAUDE.md. The dev team may want to add Ratatoskr-specific conventions on first substantive work.
Branch: main. Remote: origin → git@gitea.phasefinal.com:vh/ratatoskr.git (added 2026-05-20).
Next natural moves:
- Implement
ratatoskr.sse_clientvia TDD perdocs/contracts/sse_client.contract.md. Vertical slice — start with_parse_sse_id(the foundation; tracer testhappy_simple), thenstream_turn(tracer testhappy_one_text_done), thenreconnect_turn(tracer testhappy_resume_from_seq_3), thencancel_turn(tracer testhappy_cancel). Each FN's tracer test gets RED → GREEN before any other test in that FN. - Record real SSE snapshot fixtures from a running Worldtree (per
tests/snapshots/README.md) oncestream_turnis GREEN against mocks — gives version-skew detection per design-brief §2. - Build presenters:
--sendstdout presenter first (simplest consumer ofstream_turn); Textual TUI second. Both consume the sameEventiterator.
Recent decisions
Chronological log of decisions with [YYYY-MM-DD] prefix. One line per
decision. Captures rationale that won't be obvious from code alone.
[2026-05-20]Project name Ratatoskr (squirrel on Yggdrasil — runs up and down carrying messages). Earlier candidate Andvari demoted on the cursed-ring association.[2026-05-20]Separate repo, separate dev team. Operator's call; the in-tree-at-Worldtree/tools/ alternative was considered and rejected to dogfood the API boundary.[2026-05-20]No Worldtree-source imports. Spec-only dependency. Triple version-skew mitigation: spec-pin in pyproject.toml + recorded-SSE snapshot tests + conformance smoke. Initial pin:55101e909abcd2219833266b6f905c5bc956e0f0(Worldtree v0.19.0). Seedocs/SPEC-PIN.md.[2026-05-20]Textual (not rich+prompt_toolkit). Driver: debug observability is the primary purpose, and a multi-pane dashboard with persistent side panes + independent scrollback is structurally application-shell-shaped. Volva consulted via cross-frontier second-opinion and converged on the same call.[2026-05-20]httpx-ssefor SSE consumption. The server emits composite{turn_id}:{seq}id:lines (Worldtree INV-014) load-bearing for SSE-resume; hand-rolleddata:-only parsing (the skaldsong pattern) silently drops these. Ratatoskr becomes the reference Python SSE-resume implementation.[2026-05-20]Persona-pane PII posture: label-don't-refuse.persona.logis process-wide; pane title flips between[Persona — PROCESS-WIDE]and[Persona — session <id>…]based on whether log lines carry session_id. Refuse-against-non-local was considered and rejected as paternalistic.[2026-05-20]Server-stdout pane: opt-in via--server-log <path>. No auto-detection of well-known paths.[2026-05-20]Two-stage Ctrl-C. First cancels in-flight turn server-side; second exits app. Ctrl-D bound to immediate exit.[2026-05-20]Single-session-per-launch + startup picker. No in-app/switch. CLI flags--session <id>and--newfor scripted use. Session identity always visible in Textual footer.[2026-05-20]Markdown rendering default-on;--rawopt-out. Don't pre-design--no-stream-formatting(Volva: add only if streaming-markdown rendering is empirically ugly).[2026-05-20]Non-interactive--sendmode. Single SSE consumer module, two presenters (TUI + stdout). Keeps Ratatoskr honest as an API consumer; useful for CI / scripted probes.[2026-05-20]First contract:ratatoskr.sse_client. Bundlesstream_turn+reconnect_turn+cancel_turn+ private_parse_sse_idinto one module — the SSE-resume flow is coupled (cancel needsturn_idfrom the SSE wireid:, reconnect re-uses the same parsedSseId), so they share a contract. Hard invariant INV-002 makes the composite{turn_id}:{seq}id:parsing load-bearing — closes the foot-gun the design-brief §3 names (hand-rolleddata:-only parsing silently drops theid:). v2.1 test categoriesadversarial/scenario/traceused freely; parser warns but format spec §2.1.E permits them.[2026-05-21]Contract converted to issue-scoped (issue #1). Moveddocs/contracts/sse_client.contract.md→docs/contracts/issues/1.contract.md. Frontmatter shape switched from module-scoped (module:/purpose:) to issue-scoped (target_module:/scope:/prd:) per CONTRACT-FORMAT §2.1.I.prd:block pins to issue #1's body hash (abcbc49467e86f1d).scripts/contract_drift_check.pyreturns clean. Known parser stale-ness:contract_parser.py --validateERRORs on issue-scoped frontmatter (missingmodule:/purpose:) — this is CONTRACT-FORMAT §2.1.L H10, a documented Brokkr-side follow-up. Parser is a canonical sync, so we do NOT patch it locally (would drift from canonical). Treat parser ERROR-on-issue-scoped as expected until the canonical bumps.[2026-05-21]Default issue-tracker labels seeded (17 total). Sleipnir gating (ready-for-agent,blocked-needs-contract,blocked-needs-dependency), triage (needs-triage,needs-architect-decision,needs-info), type (bug,enhancement,task,documentation), resolution (duplicate,wontfix,invalid), Ratatoskr-specific area (sse-client,tui,cli,observability).
Tried and abandoned
Log of approaches that were tried and rejected, with rationale. Future-self defense against re-attempting the same cul-de-sac.
[2026-05-20]rich + prompt_toolkit framework choice. Considered first (during initial shape draft). Volva flagged that §1 and §5 pulled in opposite directions: a real side-panel observability surface would silently become a widget framework reimplementation. Operator's debug-observability reframe sealed the flip to Textual. Don't re-attempt rich+pt unless the scope shrinks to transcript-first REPL (which would also flip back §5 to inline-log-presenter).[2026-05-20]In-tree at Worldtree/tools/ratatoskr/. Earlier draft committed to in-tree-with-import-direction-smoke-test. Rejected at operator-routing — separate dev team forces separate repo.[2026-05-20]New/persona/logSSE endpoint on Worldtree. Considered as alternative to file-tailingpersona.log. Rejected — contract amendment + Vor round + AFK dispatch loop is weeks of consumer-side spec work for a debug feature file-tail handles in a day. Documented follow-up trigger indocs/design-brief.md§5: if a Worldtree-on-server / TUI-on-laptop debug case appears, the contract cost becomes worth paying.[2026-05-20]Cross-process Last-Event-ID resume. Considered — would require persisting per-session Last-Event-ID to~/.config/ratatoskr/. Deferred to v2 if/when it turns out to matter; v1 ships "reconnect, not resume-across-process."