Capture this cycle's arc: the bound-handshake blocker (bifrost 1.1.0 emits sortable_chunk_fields on a v0.6 wire → v0.6 additionalProperties:false rejects it), fixed by Worldtree adopting wire-v0.7 in b47 with zero change our side (keeping 1.1.0 was load-bearing). R34-v1 affect.emit verify green (dominant_emotion non-null, affect.full grant verified). Sindra on Deckard live-confirmed. Corrected the stale OpenAPI pin (2.2.0 -> 2.3.0, drift-clean) + noted the two tolerate_drift canons WARN pending a coordinated re-vendor. Clean checkpoint, nothing in flight.
125 KiB
Persistent memory — ratatoskr
Last updated: 2026-07-10
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).
Second identity (since 2026-06-14): the v1 Bifrost Tier-3 consumer/provider —
the durable persistence Worldtree writes Tier-3 agent affect (PAD/persona, :8390)
- memory (
:8391) into. Lives insrc/ratatoskr/provider/, depends onbifrost(provideroptional-extra), separate from the conversation-API spec pin. So ratatoskr now owns BOTH ends of the Bifrost round-trip — the lens #17 exploits.
v0.15.0+ sibling browser surface (ratatoskr.web, ratatoskr-web console
script): same five-pane debug surface over the same SSE wire, LAN-viewable.
Internal-LAN trust model — 0.0.0.0, no auth/TLS/CORS (operator direction).
Disciplined regardless: transcript HTML-escapes assistant content (INV-004);
upstream API key stays server-side (INV-003).
Current state / in-flight
As of 2026-07-10:
NOTHING IN FLIGHT — clean checkpoint. This cycle shipped v0.20.7 (web-UI iteration-3) → v0.20.8 (bifrost 1.1.0) → v0.20.9 (PAD auto-scale), all on origin (HEAD 709d2e4). Bound Tier-3 is fully restored on Worldtree b47/wire-v0.7 (see the [2026-07-10] handshake decision); the R34-v1 affect.emit verify ran GREEN (dominant_emotion non-null, affect.full grant verified-active); sindra is on character-rp/Deckard (live-confirmed). Only pending item: the affect-egress-reconstruction canon re-vendor, coordinated w/ worldtree-dev, gated on the brokkr use-case-segregated-render epic (non-breaking, no action until they re-engage). Details of the shipped web work below.
✅ SHIPPED — web-UI iteration-3, all three queued items (v0.20.7, patch, agent-discretion; 631 tests green; :8392 combined provider + :8765 web both restarted on the new code).
(A) Design prototype iteration-3 adapted into index.html (re-pulled Ratatoskr Console.dc.html, project bc0b65d1-…): (1) sparkline grid bg — <pattern id=sparkGrid> in the hidden defs + a bg <rect fill=url(#sparkGrid)> behind every relation-row spark polyline; (2) PAD strips → per-turn Δ bars — REMOVED the vertical polyline strip (stripPoints/proj3 gone) for padDeltas→deltaStrip: a 12-cell HTML column (newest at bottom) of diverging bars offset L/R of a center line by that turn's Δ (magnitude→width, age→opacity, zero→faint center dot); head legend now "Δ/turn · last 12 · newest ↓"; (3) mood orbit → DIMETRIC OPEN BOX (viewBox 124×140, az35/el25, D-right/A-left-back/P-up) — ghost A×P wall (P readout) + D×A floor, orbitProj/orbitShadowY/orbitWallPt/orbitAxisPt projections, JS-driven animated replay (orbitFrame rebuilt per rAF by a singleton startOrbitAnim reading live ORBIT_HIST; reduced-motion → static final-state; no SMIL). Playwright-verified (dimetric frame + 17 dyn children + 21 Δ-bars + 5 grid rects; dark+light screenshots).
(B) Memory viewer SHIPPED + the 0/0 mystery ROOT-CAUSED. New non-bifrost debug read GET /memory/chunks?agent_id=&end_user_id= on the combined :8392 provider (RatatoskrMemoryStore.list_chunks + count_chunks + shared add_memory_read_route, wired into build_memory_provider_app + combined.py; end_user STRICT, agent_id LENIENT so {end_user}-only WT chunks aren't hidden; returns {chunks,count,total}, empty=200 not 404) → web proxy GET /api/memory/chunks (server-supplied end_user, new RATATOSKR_MEMORY_READ_URL env, default :8391, set to :8392 in env.sh) → a live-polling MEMORY console pane (loadMemory/renderMemory/setMemHead, polled on open + post-turn). ROOT CAUSE of the operator's 0/0 (settled via a bound 6-turn probe + op-feed): the Bifrost memory plane binds and grants fine (handshake caps_requested:[affect,memory] → caps_granted:[memory,affect]), but sindra dispatches ZERO memory ops (not even a recall search) — her reset-clean agent definition has no memory:{} block, so Worldtree never runs the memory pipeline for her. NOT a bind-grant failure, NOT promotion-timing. PROVEN end-to-end with a throwaway memory-enabled ratatoskr:memprobe (defined with memory:{}): 6 bound turns → 6 memory.search recalls + 4 memory.upsert_many → 4 real chunks in memory.db → the pane renders all 4 (content·scope·origin·revision) live. ⚠ LEFTOVER debug state (operator chose KEEP): ratatoskr:memprobe agent on personal WT + test chunks (scope {end_user:ratatoskr-tui}) sit in the live memory.db — harmless (make the pane show real data); scripts/reset-sindra-stores.sh or DELETE /agents/ratatoskr:memprobe clears them. ✅ SINDRA MEMORY FIXED (operator-approved, 2026-07-07): DELETE+redefined her WITH memory:{} (prompt byte-identical, role=character, OCEAN {O:0.8,C:0.3,E:0.9,A:0.4,N:0.2} preserved — pleasure-verified vs the mood-fix setpoint 0.418; backup at scratchpad sindra_backup.json). PROVEN: she went from ZERO memory ops → full recall (Orion fact @ cosine 0.988) + promotion (her own "systems architect" fact upserted; store grew to 9→11 chunks). Reusable redefine script: scratchpad redefine_sindra.sh. Note: DELETE+redefine is the ONLY path (persona+memory immutable post-define); the destructive DELETE tripped the harness auto-mode guard → operator ran it via !.
(C) Markdown pass-2 SHIPPED — markdownSafe extended: GFM pipe tables (mdTable, alignment colons), indentation-nested lists (stack of <ul>/<ol>, child list inside the open <li> = valid nested HTML), ordered-list start=N numbering, and streaming robustness (unterminated fence → partial code block; header-without-delimiter → paragraph until the delimiter streams in; never throws). esc-first → INV-004 held. Playwright-verified all cases.
SHIPPED — web UI redesign via Claude Design (v0.20.0, MINOR, operator-approved). The Claude Design prototype Ratatoskr Console.dc.html (project bc0b65d1-a33e-422a-8bc1-3635c9112775) was pulled via DesignSync get_file (design scopes already granted this session — no /design-login needed) and adapted into src/ratatoskr/web/static/index.html: translated OUT of the .dc.html dialect (<x-dc>/<sc-if>/<sc-for>/{{}}/DCLogic/external _ds/ CSS — none runnable) into single-file/no-CDN/vanilla, with ALL real /api/* fetch + SSE wired into its DOM (endpoint set + SSE vocab unchanged from the prior SPA — ported verbatim, only DOM hooks re-targeted). New shape = a 3-column command-console: left engine-ticker rail (DEBUG+ADMIN+tool/turn-lifecycle MERGED into one timeline via tickerAdd + a tools-armed chip list + a FULL-detail Bifrost rail pane) · center conversation (per-turn INLINE chain-of-thought, replacing the Think pane) · right RESIZABLE affect console (dominant/canonical-mood centerpiece + bipolar PAD faders EACH with a turn-to-turn Δ+sparkline + a P×A mood orbit + relations metric rows + canonical directive). ADDED (round 2, operator-requested): a light/dark theme toggle (dark default; FULL token override — surfaces+fg+borders+accent-as-text, since the designer's light theme only did surfaces → would've been light-on-light) + a full-detail Bifrost pane (endpoint/connected/consumer/caps/tools) + fixed the engine-ticker spine (was a container-anchored ::before that scrolled out of view on auto-scroll → re-anchored to a content-height .ticker-inner wrapper) + per-fader PAD turn-to-turn Δ+sparkline (fills the room beside each meter, from the deduped-per-turn AFFECT_HIST) + an INLINED data-URI favicon (operator's /home/lkraven/rata.png — chibi aurora squirrel — downscaled 1024→64px via PIL, ~8.6KB base64, kills the /favicon.ico 404). ALL server routes UNCHANGED (84 web tests green). Verified BOTH lenses: pytest tests/test_web_* (84) + node Playwright drove the real UI end-to-end against personal :8081 (session open → Sindra seeded greeting → live turn SSE → affect console + relations + bifrost detail; theme toggle + PAD deltas + ticker spine + no-favicon-404 all confirmed, dark+light screenshots). :8765 restarted on the new code. Contract web_debug_surface.contract.md amended in-commit (v0.20.0 presenter renames: renderBifrostState→renderBifrost, renderAffectPane→renderConsole, setPersonaStrip removed; INV-001/INV-004 held). HONEST-SHAPE call (INV-001, agent-discretion within settled policy): the dominant-emotion centerpiece shows a real OCC emotion (Tier-1) OR the CANONICAL mood word (Tier-3 e.g. Sindra→"positive and energized", dimmed) OR "—", NEVER a fabricated emotion; the affect-derived grid drops non-emitted intensity/decay-τ, shows only real/client-derived cells. OPEN (operator's call): the per-fader PAD Δ placement is a sensible default — operator offered to have the designer spec the exact treatment (hooks are in place to swap it). v0.20.1 patch (operator-reported UI): fixed the relations sparkline overflowing onto the n (evidence-count) column — the sparkline grows one char/sample (HIST_CAP=24) and overflowed its fixed grid cell, covering n; now capped (relations last-8, faders last-7) + overflow:hidden clip; verified via Playwright injecting a 24-sample history (sparkline→n bounding-box overlap = 0). ADDED native title mouseover hints on all 3 PAD faders + every relationship metric row (meaning + range; static METRIC_HINTS, esc()'d). Added state.lastSnap (console can re-render without a refetch). Playwright-verified.
SHIPPED THIS SESSION (all pushed; origin/main == d75c4e8; code tip v0.19.9) — details in Recent decisions: the whole #347 authored-history-write arc landed end-to-end — OpenAPI re-vendor 2.2.0->2.3.0 (75da676), the CONSUMER side (v0.19.6: write_authored_history + get_session_messages + --seed-first-message, live-proven on personal :8081 via a rule-based Heimdall allow — the PDP is rule-based NOT scope-on-key, policy user_id=ratatoskr->ALLOW/others->DENY-hide-404), persona_state {pad:{pleasure,arousal,dominance}} canonical alignment (#317) + Tier-3 prose re-vendor (v0.19.7), the first-message-preset AUTO-SEED (v0.19.8: new module ratatoskr.first_message wired into all 3 session-create paths, best-effort never-raise/never-block; heid-code-review + heid-bug-hunt hardened), and the web now RENDERS the seeded first-message (v0.19.9: new GET /api/sessions/{id}/messages route + SPA loadTranscript, Playwright-verified). Coverage-map re-converged REST 19/41. Sindra: her card was PATCHed (the Startup: workaround moved into a #347 first-message; non-destructive PATCH — OCEAN/persona/memory intact), and she's currently RESET clean (0/0) on the provider stores.
Prior arcs this session (2026-07-04 -> 07-06), both with worldtree-dev (a tooling script + proposal docs; the #347 CONSUMER work above is the new production code):
(1) Authored-history-write primitive -> ACCEPTED as Worldtree #347 (Worldtree-owned). A SillyTavern-style "first-message" (inject a character-authored opening) generalized to an engine primitive: write a turn into a session's ledger WITHOUT generation, seed-only, side-effects off by default. It cannot be done client-side (the messages role field is a model-role override, not an author-role -> role:"assistant" 404s; a model-visible authored turn needs engine support). Arc: drafted docs/proposals/authored-message-injection.md -> heid panel pressure-test (3/3 convergence: recentered on "non-generating write" not author-role; narrowed v1 to append-only+create-time; bounded effects enum; dropped edit/regenerate as history-mutation) -> revised -> committed (c457520) -> handed to worldtree-dev -> accepted as design item #347. worldtree-dev wrote the v1 contract (rev 1.1); I validated the wire as reference consumer (green). v1 shape: POST /sessions/{id}/history, author=assistant only, effects=none only, idempotency_key REQUIRED (per-session), model-invisible provenance (renders byte-identical to a lived assistant turn -> first-message immersion preserved; provenance audit-only), event-silence (no turn.started/done, no Bifrost wire for a seed; the 201/200 IS the write-ack), seeded lifecycle phase (not exposed on read paths). Heimdall-gated with hide-existence (grant session.history.write; ungranted tenant -> 404 NOT 403, undiscoverable in /capabilities -> consumer must treat 404 as feature-absent -> fall back to a model-generated greeting, never capability-probe). Provider constraint: a create-time first-message makes the assistant seq-0; vLLM/openai_compat tolerate assistant-first (sindra = openai_compat, unaffected), Anthropic-family providers 400 the next generation. Waiting on worldtree-dev: #347 TDD (their heid->contract->review workflow) + the consumer-facing 2.3.0 persona/motivational/memory schemas -> then re-vendor our pinned openapi 2.2.0->2.3.0.
(2) Sindra's stuck-neutral mood FIXED (operator-driven "reset + smoke" that flushed out two real upstream problems). Chain: her OCEAN lived only in prompt TEXT, never declared as a structured persona -> the Tier-3 mood engine ran on neutral defaults. Fix = declare OCEAN via the define-time persona field (immutable via PATCH -> requires DELETE+REDEFINE). Along the way my "the persona didn't store" call was WRONG (persona_state/envelope are Tier-3-blind, see Tried/abandoned); worldtree-dev found a real engine bug #348 (single-letter vs spelled-out OCEAN keys -> a declared OCEAN silently resolved to 0.0/neutral; fixed in b21, shipped to personal as b22); then a clean bound-egress read STILL neutral -> the personal container was running a stale image (the b22 deploy was a pull-only no-op racing the main build; infra-ops force-swapped run 8211, verified 2.3.0 / 879cefe). VERIFIED FIXED: bound mood-smoke reads (0.448, 0.267, 0.316) ~= the OCEAN-derived setpoint (0.418, 0.249, 0.328). Sindra is currently reset clean (0/0) on role=character; her persona is stored + correct (no re-define needed again).
(3) R30 CLOSED (operator steer 2026-07-04, relayed via worldtree-dev): graduated on offline-tests + human face-validity, NO deployed gap-injection run (it was confirmatory-not-measuring per brokkr's S0 reframe; offline tests already cover the OU formula + both directions). My gap-injection harness (read/predict/record; write side stubbed; predict() self-validated vs brokkr's N=0 anchors) is BANKED at diag/r30-gap-injection-harness (7156b25-era) for the PARKED powered true-tau study.
Persona-declaration shape (Worldtree #343/#348, live on personal b22): POST /agents/define persona:{ocean:{O,C,E,A,N: float[-1,1]}} (single-letter keys EXACTLY -- missing/extra -> 422 persona_ocean_required; out-of-range -> 422); NO baseline PAD (resting setpoint DERIVED from OCEAN via Mehrabian: pleasure=0.21E+0.59A+0.19C-0.32N, arousal=0.15O+0.30E-0.57A+0.15N, dominance=0.25E+0.17A+0.10O-0.14N); negative-channel gain + per-axis decay-tau derive from N. valence deferred (422 layer_deferred); motivational/memory active (#187/#189). Persona is write-once at define, immutable thereafter (PATCH takes ONLY system_prompt + role). role supersedes model -- set a role (character / character-rp), Worldtree resolves the model; #344 (b19) fixed the model-field to surface the ROLE, not the resolved catalog_id. character-rp = a reasoning-tuned RP config (gen-reasoning + temp 0.75 + RP extra_body); character = plain non-reasoning. The tier3.py client CLI is STALE (has --model, no --role; model is now immutable) -> role/persona set via raw curl.
New tooling: scripts/reset-sindra-stores.sh (0a8784c) -- one-command self-service provider-store reset: stop the combined :8392 provider -> move memory.db+affect.db to a single ROLLING backup (db-reset-backup/, gitignored via .db; --hard skips it) -> restart empty -> verify 0/0. Codifies the manual reset flow done repeatedly this session. The combined :8392 provider is THE provider now; the separate :8390 (affect) / :8391 (memory) single-plane providers were pruned as stale duplicates. To drive a BOUND session from the CLI use --new --bifrost-url http://10.100.10.50:8392 (the CLI's --bifrost-plane affect/memory map to the pruned :8390/:8391 -> unreachable; combined is not a --bifrost-plane choice).
Standing (carried from prior snapshots, still true): the web surface (ratatoskr-web, :8765) is the operator's PRIMARY debug surface at full TUI pane parity (v0.19.5); the v1 coverage-audit has CONVERGED -- REST 17/40 (zero in-scope gaps, 23 excluded-by-design), SSE 11/11, Bifrost provider planes 8/8 live-proven; the living ledger is docs/coverage-map.md; v1 cuts when Worldtree tags 1.0 (ratatoskr v1 = full Worldtree I/O coverage). Debug-observability core complete (Persona/Tools/BifrostState/AdminEvents). Substrate pins: bifrost ==1.1.0 / wire v0.7 (bumped 2026-07-07 from 1.0.0; NOW WIRE-ALIGNED with Worldtree personal-b47 which adopted wire-v0.7 — bound Tier-3 fully restored 2026-07-10; keeping 1.1.0 was load-bearing, see the [2026-07-10] handshake decision); Worldtree openapi vendored 2.3.0 (re-vendored 2026-07-06 for #347 POST /sessions/{id}/history; drift-clean vs source), pinned + drift-gated in .corviduo-canonicals.toml; suite 631 green. Personal WT on b47/wire-v0.7 (deploy train this cycle: b35→b44→b46→b47). Drift-check note: two tolerate_drift canons WARN vs source — worldtree-affect-egress-consumer-reference-v1 (drives our context-injection RECONSTRUCTION panel; R32/R34 moved WT's directive assembly) + worldtree-conversation-api-spec-v1 (prose narrative, OpenAPI is authoritative) — a coordinated re-vendor is PENDING per the [2026-07-07] use-case-segregated-render entry (worldtree-dev re-engages when the brokkr render epic lands); non-breaking, no action until then. Keys env-only mode-600 (consumer/Heimdall in ~/.config/ratatoskr/provider.env; admin RATATOSKR_ADMIN_API_KEY = 7 read scopes, personal-:8081-only; Heimdall keys are PER-INSTANCE). Provider identity settled -- ratatoskr owns both ends of the Bifrost round-trip; ratatoskr:sindra is the owner-scoped Tier-3 agent (invisible to GET /agents; check GET /agents/<owner>:<name> with the owner key). Providers run as dev-box BACKGROUND SHELLS. graphify-out/ runs dirty (auto-regen, never stage). Branch main, HEAD 709d2e4 (origin/main synced through v0.20.9); remote origin -> git@gitea.phasefinal.com:vh/ratatoskr.git. Open/deferred: #10 (subject-migration watch); the relational-dynamics-arc verify (deferred, bind mechanism known: --bifrost-url :8392); the affect-egress-reconstruction re-vendor (coordinated w/ worldtree-dev, pending the brokkr render epic). Leftover debug state (operator chose KEEP): throwaway ratatoskr:memprobe agent + test chunks in the live memory.db.
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-06-14]Ratatoskr becomes the v1 Bifrost Tier-3 consumer. A second identity beyond the debug TUI: the durable persistence Worldtree writes Tier-3 agent affect (persona) + memory into. Pinbifrost>=0.6.1in aprovideroptional-extra (gitea PyPI index, auth via~/.netrc; 0.6.0 was yanked for a circular import). Implement bifrost's OWNMemoryDataStore/affect Protocols (NOT worldtree-memory's);describe_storeis SYNC; affect is conduit-opaque. New modulesrc/ratatoskr/provider/. Authoritative how-to:~/development/bifrost/docs/implementing-a-consumer.md. (commits1a73d77pin,d90a58daffect store v0.17.1,bcdcd71serve entrypoint v0.17.2) -
[2026-06-14]Backend = SQLite + sqlite-vec; affect-first then memory; separate DB per plane (operator-chosen). Affect = blind conduit (reads onlyagent_id+end_user_id); memory = structural index (reads vector/scope/id/origin to serve search). Conformance for both = #195 parity vs bifrost'sInMemory*Storethrough the realdispatch_*_call. -
[2026-06-14]The affect contract's idempotency model was WRONG; real-lib TDD caught it. First draft modeled same-idempotency-key-different-payload as an LWW overwrite; bifrost actually raises a CONFLICT (AffectIdempotencyConflict), actor-scoped. The artifact-only/heid-contract-reviewSTRUCTURALLY cannot catch this class (it never sees bifrost's source) — TDD against the shipped library is the gate; the executable reference store + #195 parity are the backstop. Filed the guide §6 gap to bifrost-dev, who fixed it (bifrostc0d0a11). -
[2026-06-15]Memory v1 = the bifrost BASIC plane only (search/get/upsert/delete + describe_store/health) per worldtree-dev re-scope (#294) — the only surface Tier-3's live path touches; gated verbs (edges/scan/atomic_supersede/mark/patch/maintenance) deferred + advertised-unsupported. Worldtree v0.35.3 already requests+maps it — no Worldtree-side blocker. Memory contract committed v1.0 (eebab46) → v1.1 Heid-reviewed (1f94e5f). -
[2026-06-15]Providers run as dev-box BACKGROUND SHELLS, not infra-ops/systemd (operator call — it's a dev box).ratatoskr-provider(affect) +ratatoskr-memory-provideras background processes; no productionization track. -
[2026-06-15]Affect plane shipped (v0.17.2) + LIVE-PROVEN end-to-end against real Worldtree v0.35.2. Personal handshake 200 +affect.emit200 from10.250.50.152→ durable row persisted (opacity held). HS256 key = the consumer's Heimdall API-key STRING utf-8-encoded (NOT base64/raw — the tripwire); cross-subnet route +BIFROST_CLIENT_ALLOWED_HOSTSallowlist all held (infra-ops-owned). worldtree-dev confirmed ADR-0009 holding as designed. -
[2026-06-16]#295 cold-recall miss root-caused — UPSTREAM, scope-axis asymmetry. A self-driven bound cold-recall probe captured the inbound pair via the observe log: Worldtree's recall filter carries{end_user, agent_self}; our chunks were{end_user}-only; AND-matching dropped everything onagent_self→ 0 hits. Our store + search are SOUND; fix is Worldtree-side. F2 (question-promotion) → #296; F1 (recall-miss) → #297. -
[2026-06-16]agent_self → make it CANONICAL (operator decided A). bifrost's reference lattice was{end_user, group, tenant}only (agent_self →invalid_filter400); Worldtree emits agent_self (#248). Operator chose canonical-not-re-expressed; worldtree-dev filed the lattice-addition with bifrost-dev. Implication: our store's permissive axis-acceptance becomes CORRECT once bifrost adds agent_self. -
[2026-06-16]Self-drive auth identity: bound session-create uses the CONSUMER Heimdall key as bearer, NOTWORLDTREE_API_KEY. Worldtree signs the Bifrost handshake JWT with the session-create bearer (canary key → handshake 401; consumer key → 200). Two keys, two identities. Proven by hand; documented indocs/bifrost-self-test.md; load-bearing for #17's Bind half. -
[2026-06-16]Issue #17 v1 scope locked (operator 1A/2A): single-plane bind + dispatch-layer op-feed.BifrostBindingRequestis oneendpoint_url(one plane per session); composite-both-planes endpoint PARKED (→ now #18). Observe = structured op-feed at the DISPATCH layer (bifrost passes ctx to upsert_many but NOT search/get/delete —memory.py:244), session-level correlation; turn-correlated pane UI PARKED. Contractdocs/contracts/issues/17.contract.mdwritten +/heid-reviewed. -
[2026-06-16]agent_self lattice SHIPPED both sides → our axis-validation gap CLOSED (v0.17.5). bifrost 0.7.0 / wire v0.5 adds agent_self to{end_user,group,tenant,agent_self}(#10, driven by our foot-gun flag); Worldtree pinned 0.7.0 (v0.35.11). We DID add_validate_scope_filter(4-axis) to match the reference (purely additive; out-of-lattice → InvalidFilter). -
[2026-06-16]Repinned bifrost 0.7.0→0.8.0 + reimplemented memorysearchto the v0.6 scope split (operator-directed).scope_filter→scope_all(AND) +scope_any(OR/union over a list of conjunctive scopes), bifrost #11 — the canonical resolution of the #295/#297 silent-zero. The reference now does OR viascope_any(a NEW field — additive split, not a flip of AND). Store / contract (v1.2) / tests at parity with the v0.6 reference; provider bounced onto 0.8.0 with a wiped DB. Shipped v0.17.6 (96d61a4). (SUPERSEDED the earlier "do NOT flip_scope_matchesto OR" note.) -
[2026-06-17]Worldtree spec pin bumped v0.29.0→v0.35.16 (562001a→f1b59f8); cold recall closed on the WIRE. Worldtree shipped #297 (client-side per-scope-value union recall) + #298/#299 (adopt the bifrost v0.6scope_any/scope_allwire) — emitsscope_anyon recall, pairing with our v0.17.6 provider. Re-vendored the spec; diff-reviewed the 285-commit catch-up — no client-breaking changes.pin:-only commit, no bump. -
[2026-06-17]End-to-end cold-recall proof RAN — our stack proven, #296 isolated. Against personal WT v0.35.16 with restoredratatoskr:sindra: #297/#298 union recall, write path, and cold read ALL proven. Lone gap = upstream #296 extraction quality (the WIRE closed; fact-recall was #296-blocked). -
[2026-06-17]DELETE+redefineratatoskr:sindra(operator-authorized; pre-v1 debug surface). She SURVIVED the rebuild but was STALE (dead model + no memory block); memory is immutable post-define, so DELETE+redefine was the only path. v0.35.16 define takesrole(capability), NOTmodel:role:"character"→ first-healthy bindmistral-small-4;memory:{}trips the promotion gate (GET does NOT echomemory_config). Ourtier3.pydefine is Phase-2.0-stale — untracked modernization follow-up. -
[2026-06-17]Promotion = 4-trigger hybrid (worldtree-dev, code-grounded): salience (regex, 90s rate-limit) /turn_count≥6/ context_pressure / idle≥10min(unconditional on quality); per-turnplan_promotion_runfor consumer_defined. DELETE does NOT drain/promote (delete-is-delete, #276) — idle≥10minis the deterministic flush. -
[2026-06-17]#296 triage sent to worldtree-dev (01KVBBH0…): extraction SUBJECT-INVERSION (promotes assistant prose, drops the user's fact) + META-DESCRIPTION-not-content; verbose-persona aggravator. WAD-vs-bug resolved to BUG (extraction quality), not idle-gating. -
[2026-06-18]Tier-3 memory PROVEN end-to-end live —ratatoskr:terse-proberecalled a seeded user fact in a COLD history-free session (scope_any → 1 hit @ cosine 0.6994). Closes the opening "how far from Tier-3 memory" question for normal agents. -
[2026-06-18]#296 Stages 1+2 closed. Stage 1 (v0.35.19, recallability admission gate) validated live for normal turns; bisect localized the residual to verbose-persona VOLUME crowd-out. Stage 2 (v0.36.0, MERGED at worldtree-codex) = user-only one-call-per-turn extraction, the STRUCTURAL fix; hard-linguistic layer → Worldtree #305 (we handed over a live-validated eval fixture PAIR). Full-coverage re-smoke: verbosesindra-probepromoted the fact cleanly + cold-recalled @ 0.694 under v0.36.0. -
[2026-06-18]#17 implemented end-to-end via direct in-session TDD (6 patch bumpsv0.17.8→v0.17.13, suite 470 green). Slice order: bind primitive → op-feed → CLI → TUI → web(server) → web(UI). Tests drive the REAL bifrost dispatch via minted JWTs (bifrost.core.dispatch_jwt.mint_dispatch_jwt) — the "test against the shipped lib" posture, not hand-mocked envelopes. Op-feed readssession_idoff the dispatch JWTsubclaim (the contract open-q, resolved YES at the ASGI layer where the JWT is always present —bifrost.reference_server._dispatch_auth.DispatchContext.session_id = payload["sub"]). bifrost wire facts captured in-code: memory envelope{operation, args}→memory_result(**payload)={success,...}; verbs bare (search/upsert_many/get/get_many/delete_many); affect{operation:"affect.emit"}→{success,stored}; error envelope{code, message}; scopesmemory:read|write. -
[2026-06-18]#17 live-smoke PROVEN — the whole thesis validated. A self-driven bound CLI session showed, from the PROVIDER side, exactly which memory ops a turn produced (2 recall searches, exact bound session_id, real union-recall scopes). Negative (canary→auth_rejected) NOT live-constructible (Tier-1 agents aren't memory-bindable; a wrong key for an owner-scoped agent fails at agent-auth before the handshake) — covered by the unit test + prior hand-proof. -
[2026-06-18]Fixed a pre-existing test-isolation bug exposed by the #17 CLI tests (0bebad7):test_no_textual_importdid a liveimportlib.reload(ratatoskr.cli)that mutated the shared module in place, breaking class identity (isinstance/pytest.raises) for every test ordered after it. The real check is the static source-grep; the reload was vestigial → removed. Lesson: neverimportlib.reloada shared module in a test without restoring it. -
[2026-06-18]#18 filed (composite endpoint + PAD read-endpoint) — DEFERRED, tracked at Gitea #18. Two pieces: (1) a composite Bifrost facade (new port e.g.:8392) fronting BOTH:8390+:8391advertising both caps at handshake → one session binds both planes (un-parks the #17 open-q; bifrost reference_server already mounts both planes in one app → thin combined builder; needs per-plane failure-status + the op-feed deriving plane PER-REQUEST from the path instead of its fixedplaneparam). (2) a non-bifrost PAD read-endpoint on the affect provider (recommended over web-reads-affect.db-directly) → web persona pane renders PAD/valence from OUR:8390store. Composite half APPROVED by operator ("A is correct"); contract-first next. Persona-telemetry diagnosis (verified): affect bind persists PAD (vuong: pleasure +0.146, familiarity 0.18→0.59 over 8 turns) but the pane reads Tier-3-404persona_stateAND Tier-3 emits ZEROaffect_updateSSE (wire-verified) — both WT sources dead, so #18's PAD-display half is the only path.affect.fetchover bifrost is RESERVED/blocked but irrelevant (we own the store). Proposed: fast-track the PAD-display half now (awaiting operator go), keep composite contract-first. -
[2026-06-18]#18 SPLIT; Deliverable 1 (composite) routed to bifrost — Option C (operator). D2 (PAD read-endpoint, our-side only) fast-tracked; D1 (composite:8392endpoint) routed to bifrost-dev to add a PUBLICbuild_combined_apprather than hand-roll one from bifrost privates — because ratatoskr is a debug surface that must exercise the CANONICAL surface ("don't go off the reservation"). The Heid framing-panel had unanimously recommended hand-rolling (Option B) — DISCARDED as wrong-grounded (the panel lacked the canonical-surface principle; their own finding that B reaches external/underscore-private names actually vindicated C). bifrost-dev confirmed: clean additive minor (~v0.9.0), design locked (advertise-by-store-PRESENCE handshake — no health probe; per-route call-time isolation within a shared ASGI process), slotted after WT #289. [principle → auto-memoryfeedback-debug-surface-uses-canonical-surface-only] -
[2026-06-18]FR-1 RESOLVED — the composite premise was unverified, now wire-proven: single-endpoint, caps-routed. The Heid panel's sharpest catch (Regin): "advertise both caps → Worldtree dispatches both planes to one endpoint" was an ASSUMPTION about WT dispatch, stated as fact. worldtree-dev verified IN CODE: oneBifrostClientper session (single_endpoint_url), handshakecapabilities_grantedparsed INDEPENDENTLY into memory+affect sets, both stores attach off the SAME endpoint iff their cap was granted (service.py:2597/2703-2713/2745-2751,bifrost_client.py ~357-369; teststest_tier3_bifrost_{memory,affect}_routing.py). So D1 is bifrost-only, ZERO Worldtree change — #18's "no WT change needed" assumption was correct. -
[2026-06-18]#18 D2 implemented via direct in-session TDD (suite 470→482). Provider read routeGET /affect/state/{agent_id}added viaapp.add_route(NOT an outerMount— keeps/bifrost/*top-level so the existing route test + the op-feed path-check stay valid); webGET /api/affect/{agent_id}proxy (server-suppliedend_user_id, colon-idquote()'d,RATATOSKR_AFFECT_READ_URL); pane renders the affect-emit shape honestly. Contractdocs/contracts/issues/18.contract.md(D2-scoped; D1 deferred). heid-code-review panel (Gróa 5 / Hulda 3 / Regin 0): 1 real INV-001 drift + 4 test-gaps, all fixed. No contract amendments (code was wrong, contract was right). -
[2026-06-19]#18 D2 SHIPPED (v0.17.14,39eebd1) and the full #17+#18 arc PUSHED to origin. Live-smoke PROVEN against real data (throwaway:8393/:8766vs the realaffect.db→ real sindra/vuong PAD through the full web→provider chain; Playwright DOM check confirmed the pane render + the F1 fix — no fabricated "neutral"). The push carried 9 previously-held commits incl. the deliberately-unpushed #17 (v0.17.8→v0.17.13); origin/main now ==39eebd1, tagv0.17.14. -
[2026-06-19]bifrost repinned 0.8.0→0.10.0;affect.fetchbecame MANDATORY (strong-or-absent). 0.10.0's_supports_affect_planerequiresaffect_supported+emit+fetchand gates EVERY affect op — an emit-only store 400s. Implementedaffect.fetch(v0.17.15,ca6af6b) conformed to bifrost's referenceInMemoryAffectStore.fetch({found, snapshot?}): the forced D1 prerequisite + a new Worldtree I/O point consumed. Flagged the now-stale consumer-guide line to bifrost-dev (fixeda2e6d62). -
[2026-06-19]#18 D1 SHIPPED — compositebuild_combined_appon:8392(v0.17.16,7f4ceaa); #18 CLOSED; publishedv0.18.0(359dbb1).build_combined_provider_appwraps bifrost's public builder over both stores + the shared read route; op-feedplane='combined'per-path. Direct in-session TDD; heid-code-review panel (Gróa/Hulda/Regin) returned ZERO drift. Live-proven at wire+dispatch; WT-turn gated on infra-ops:8392allowlist. -
[2026-06-19]op-feed handshake field-name fix (#17,v0.17.17d60b77d):capabilities_requested→capabilities. The summary read a field that never exists on the wire (bifrost readscapabilities,_protocol.py:181) → caps_requested was always null. Surfaced by the heid panel (Regin) during the D1 review — a latent #17 bug, not D1 drift. -
[2026-06-19]Ratatoskr is a REFERENCE implementation of the Worldtree/Bifrost standard (operator). Adopt the dep's canonical way (even if ours works); INFORM of drift/gaps; ADVISE a different approach only when ours is genuinely better (dep owner decides), never unilaterally fork. [auto-memoryfeedback-ratatoskr-is-a-reference-impl-adopt-canonical] -
[2026-06-19]Ratatoskr v1 is DERIVED from Worldtree I/O coverage (operator) — no self-defined feature ROADMAP. v1 = consume all of Worldtree's I/O points, reached when Worldtree hits 1.0; the convergence target is a coverage map, not a 3–7 capability list. [auto-memoryproject-ratatoskr-v1-derived-from-worldtree-io-coverage] -
[2026-06-20]#18's final leg PROVEN — composite:8392WT-driven smoke ran end-to-end + persisted. infra-ops allowlisted10.100.10.50:8392on the personal WT (01KVHWJGTT…); a real WT turn (sessionb83a66b6,ratatoskr:sindra, fresh end_userresmoke-choco-1) dispatched the full both-plane lifecycle through ONE endpoint — handshake (both caps) →affect.fetch+memory.search→affect.emit(stored:true) →memory.upsert_many(upserted:1) — both writes verified in our SQLite (affect_snapshotsPAD row +memory_chunkschunk2df1b79…). First attempt blocked by amodel_unavailableoutage on the personal WT (both agents' models down), operator-fixed mid-session, then clean. The composite has no open legs. -
[2026-06-20]#17 CLOSED in the tracker. Shipped end-to-end (v0.17.8–.13+ op-feed field fixv0.17.17); the 2026-06-20 composite smoke re-exercised its op-feed live. Closing comment captures the full both-plane proof. Open issues now just #11 (scope-blocked) + #10 (watch). -
[2026-06-20]Sindra has real PAD but ~empty memory — the affect/memory persistence asymmetry, confirmed on real sessions. affect EMITS every turn (persona always accumulates: vuong 8→14 interactions across the session); memory only writes on a PROMOTION trigger (salience / turn_count≥6 / idle-≥10min flush). Two real vuong sessions through the combined bind (04d6414c,433541fe) drove affect emits + memory SEARCHES but ZERO promotion upserts →memory.dbholds only the smoke fixture, zero vuong chunks. Operator: acceptable (server-takedown = "Sindra bonked on the head"; transient memory loss WAD). Operational catch: combined-as-default web bind saves persona reliably but silently LOSES memory if a session closes before a promotion trigger fires. -
[2026-06-29]Web SPA combined-bind default (v0.18.1,719e4d6) — operator-caught gap. #18 shipped the composite:8392provider but never exposed it in the web bind dropdown (only memory/affect single-plane). Addedcombined (:8392)as the DEFAULT option (both planes in one session), kept single-plane for isolation diagnostics; wiredendpoint_for_planecombined→8392 + server validation + the dropdown. Direct TDD; #17 contract updated (the governing spec for the web bind). Restarted:8765on current code (env.sh + provider.env +RATATOSKR_AFFECT_READ_URL=:8392). -
[2026-06-29]bifrost repinned 1.0.0 (v0.18.2,af67ad9). bifrost-dev shipped its first stable release; wire v0.6 now STABLE/FROZEN. Non-breaking (byte-identical to 0.10.0); switched the floor pin → exact==1.0.0per the stable-substrate posture. Post-1.0 breaking changes ride a bifrost MAJOR + new wire (v0.7+); a v0.6-pinned consumer is stable indefinitely. (Also this session: althing migrated to v0.15.0+ lean-bus / schema v4 — moderation retired, chamber/redis ripped; our tooling auto-updated to 0.17.4.) -
[2026-06-30]Worldtree v1.0.0b1→b2 consumer adaptation: eager turn-launch statuses (v0.18.3b2e4901,v0.18.4e4317f6). Worldtree #331 decoupled turn execution from the SSE connection → turn-launch failures now arrive EAGERLY as a status before any stream: 409agent_not_available(pre-b1 a 200 + in-stream error event), 503 retryable. Mapped both instream_turnto typedSseConnectFailedsubclasses keyed on STATUS, parsing the{detail:{error_code,message}}envelope — POST-003 preserved (no synthetic event yielded), existing handlers still catch (the design fork vs yield-an-Error-event was decided by POST-003). DEFERRED follow-ups (tracked here; bundle with the v1 coverage-audit): (1) live-prove the 409/503 end-to-end on personal-b2 (now unblocked — personal on b2, my key works there); (2) fullconversation-api-spec.mdmarkdown re-vendor to the b2 era (ratatoskr vendors the markdown, not the OpenAPI JSON). -
[2026-06-30]Verify-against-the-real-spec-before-committing caught a real upstream gap. Holding the v0.18.3 commit to verify against demo's OpenAPI surfaced that the FROZEN OpenAPI 2.1.0 didn't document the 409/503 the heads-up described (agent_not_availablewas in the ErrorCode enum, but NO 503/turn-launch code). worldtree-dev confirmed it was THEIR gap (#331 added the statuses without extending the #328openapi()override), shipped the fix in v1.0.0b2 / OpenAPI 2.2.0 (409/503 now enumerated, 503 code finalized asnot_ready). "The consumer-oracle earning its keep." Lesson: a provider's prose heads-up can diverge from its frozen machine-readable spec — verify the actual spec before committing a consumer adaptation. -
[2026-06-30]regard is a DEAD AXIS in Worldtree's emitted affect (caught provider-side; worldtree-dev confirmed + escalated to Vuong). Across all our affect snapshots,valence[].regardis EXACTLY 0.15 regardless of agent/end_user/interaction_count, while familiarity accumulates (vuong 0.18→0.69 over 14 turns). Root cause (worldtree-dev, code-grounded): 0.15 =base_regard = agreeableness*0.3(sindra A=0.5); regard's only human-writerupdate_regardearly-returns unless an emotion isabout="other", but the Vili appraiser'sViliResponseschema has NO directedness axis (everything hardcodedabout="situation") — producer side lost in the #265 Vili rework; consumer machinery intact. NOT WAD; the fix (reintroduce other-directed classification) is an affect-model change touching every agent + a directedness-classification design call → worldtree-dev filing an issue to Vuong. [the consumer/provider thesis paying off again] -
[2026-06-30]v1 coverage-audit kicked off; coverage ledger written (docs/coverage-map.md) — the first one. Every Worldtree v1-FROZEN I/O point × ratatoskr status. Anchored on WT's frozen machine-readable artifacts (OpenAPI 2.2.0conversation-api-openapi.json= 40 REST path-groups + SSE schema = 11 events + bifrost wire v0.6), NOT the stale vendored prose markdown. Result: SSE 11/11 ✅; Bifrost provider planes 8/8 ✅ live-proven (covers the fullbifrost.memory.MemoryDataStoreprotocol = describe_store/get/get_many/search/upsert_many + delete_many, and affect emit/fetch;healthis extended-reference-store-only, NOT in the base protocol → correctly deferred, NOT a gap — settles the prior "health" ambiguity); client REST 7/40 ✅ live, 11 ⬜ in-scope, 22 🚫 excluded. -
[2026-06-30]Scope mandate A locked (operator): v1 "done" = every frozen I/O point CLASSIFIED (covered-or-excluded-with-rationale), zero unaccounted — NOT a feature-complete client. The coverage map is a LEDGER, not a build-everything mandate. Reconciles the 2026-06-19 "consume all I/O" reframe with the 2026-05-20 design-brief's "NOT an admin tool" + deferral negative clauses (which predate both the provider identity and the reframe). Resolved the 11 design-brief-vs-reframe ❓ rows via the debug-observability test (does a turn flow through it?): 🚫 search / uploads / pending / embed / judgments (consumer-product + eval utilities); ⬜ transient-characters routing (4) + persona_state-write (Tier-2 frontier). Frontier Tier 1 (all unblocked except #11): session-picker + SSE-resume (wrapperslist_sessions/reconnect_turnexist with NO caller — presenter-wiring only) →GET /capabilities+GET /me→ BifrostState/Tools widgets (GET /admin/sessions/{id}/{bifrost,tools}, admin-key) → #11 AdminEvents BLOCKED onadmin.events.readscope. The 3 admin-observability widgets + picker + resume were design-brief §5/§4/§8d v1 items that were never built. -
[2026-06-30]Finding P-1 (pin drift) + pin-remediation PENDING. We vendor the PROSE markdown (docs/conversation-api-spec.md), which is byte-identical to live WT's but frozen at v0.35.16-era content (last WT edit 2026-05-31) — it does NOT capture b2: 7 new endpoints (admin/keys/bulk, admin/persona/{archive,erase}, admin/usage, embed, judgments, me/usage), the 409/503 on messages-POST (#331), the unified error envelope (#328), or the SSE schema. WT's authoritative v1 truth is now the FROZEN OpenAPI 2.2.0 + SSE-schema JSON (Worldtree/docs/v1-schema-freeze-manifest.md). So the previously-deferred "re-vendor markdown to b2" is a near-no-op (markdown content identical). Pending operator nod: re-pin to the machine-readable artifacts (recommended — drift-checkable viacanonical_drift.py, makes the coverage map reproducible vs a frozen diffable target) vs markdown-only. Deferred (not auto-applied) because it adds vendored artifacts + a canonical-sync pin = substrate change with CI-gating reach. → RESOLVED 2026-06-30 (operator: "a then b"). Vendoredconversation-api-openapi.json(2.2.0) +conversation-api-sse-events.schema.json+ re-copied the prose markdown; pinned all three in.corviduo-canonicals.toml(OpenAPI+SSE = strict drift gates, markdown =tolerate_driftreference); advancedworldtree-spec-revf1b59f8→5810a26 +worldtree-versionv0.29.0(STALE, never bumped from the v0.35.16 pin)→v1.0.0b2 +pinned-on→2026-06-30; SPEC-PIN.md history row added.canonical_drift.pygreen (10/10).pin:-only, no version bump (no client-facing code change; the b2 409/503 + error-envelope were already consumed in v0.18.3/.4). -
[2026-06-30](b) Tier-1 frontier SCOPED, ready for a contract-first TDD cycle (next focused work). The primitives already exist + are contracted + tested; the gap is PRESENTER-level wiring. Two slices: (b1) SSE-resume — contract #1 (ratatoskr.sse_client) DELIBERATELY makes resume caller-owned ("onSseConnectionDropped, the caller MAY invokereconnect_turn");reconnect_turn(sse_client.py:524) has NO caller. Gap = a SHARED resume-orchestration wrapper (catchSseConnectionDropped→ track last-seensse_id→reconnect_turn→ continue), consumed by all 3 presenters per design-brief §8b "share the consumer, branch the presenter" (NOT per-presenter — that forks the consumer). New function block → amend contract #1 (additive FN, e.g.stream_turn_resilient) then TDD (RED: drop-mid-stream→resume continuity; GREEN: wrapper; wirecli --sendfirst as the tracer). Resume design pre-locked: in-process Last-Event-ID only, cross-process deferred to v2 (design-brief §8d). (b2) session-picker —list_sessions(sessions.py:198) has NO caller; add a Textual DataTable startup picker (>1 session) +--session <id>/--newCLI flags (design-brief §4, decisions pre-locked). Both pre-locked → heid-contract-review likely skippable as ceremony (small additive amendments to mature specs); heid-code-review still valuable. #11 AdminEvents stays BLOCKED onadmin.events.readscope (infra-ops). -
[2026-06-30](b1) SSE-resume SHIPPED (v0.18.5) —stream_turn_resilient(sse_client.py). The shared resume-orchestration surface (design-brief §8b): wrapsstream_turn+reconnect_turn, catchesSseConnectionDropped(mid-stream drop OR clean-EOF-before-terminal) → resumes from last-seensse_idviareconnect_turn(Last-Event-ID), up tomax_reconnects(default 5); non-drop reconnect failures (412/410/400/TurnIdFlip/SseConnectFailed) PROPAGATE per contract #1's "surface, not recover".last_seenpersists ACROSS attempts (a zero-event reconnect drop falls back to the prior attempt's id). Direct in-session TDD against a contract-#1 amendment (8 cases incl. two-drops, max-reconnects-exhausted, zero-budget, buffer-expired-propagates, unresumable-zero-event). Wired ALL THREE presenters through it (v0.18.6):cli --send(cli.py:396), TUI (tui.py:1321), web (web/server.py:294) — each a name-for-namestream_turn→stream_turn_resilientswap (the §8b "all presenters share the consumer" promise, fully kept; the TUI is the primary resume beneficiary — long-lived sessions / laptop-suspend). Suite 518 green; ruff+mypy clean on touched code (pre-existing cli.py:400/543 mypy warts left untouched per surgical rule); contract #1 validates OK. heid-code-review NOT run (small additive well-TDD'd wrapper; offered to operator). b2 (session-picker +--session/--newflags) still pending. -
[2026-06-30](b2) session-picker SHIPPED (v0.18.7) — bare TUI mode → startup picker (design-brief §4).list_sessionshad NO caller; now bare TUI mode (no--session/--new) resolves vialist_sessionsin_resolve_then_run: 0 sessions →[no_sessions]error, exit 14 (resume-only, honors §4 "no in-app session creation —--newflag only"); exactly 1 → auto-resume (§4 "picker only when >1"); ≥2 → newSessionPickerApp(TextualApp[str|None], mirrorsAgentPickerApp; ListView of sessions) → resume the pick (Esc/Ctrl-D → exit 0). cli_parserelaxed: bare TUI now VALID (was "pass exactly one" error);--sendstill requires one flag (non-interactive, no picker);--agentforbidden in bare mode;run_tuiPRE-002 XOR→"not both". Direct in-session TDD (contract #6 amendment, validated OK): 3 widget pilot tests + 5_resolve_then_runresolution tests + 3 cli validation tests. Suite 528 green; touched code ruff-clean (mypy: only theBINDINGSlist-invariance warning every App in tui.py already carries — consistent). DESIGN NOTE — bare+0-sessions → error (clause-consistent). The friendlier auto-fall-through-to-new alternative is DEFERRED pending operator preference (it would create a session without--new, against the §4 negative clause). Frontier now:GET /capabilities+GET /me→ BifrostState/Tools widgets (GET /admin/sessions/{id}/{bifrost,tools}, admin-key) → #11 AdminEvents (BLOCKED onadmin.events.read). heid-code-review NOT run on b1 or b2 (offered). -
[2026-06-30]capabilities+me slice SHIPPED (v0.18.8) —GET /me+GET /capabilitiesconsumed via a new--whoamione-shot.get_me/get_capabilitiesadded to sessions.py (mirrorget_persona_state: 200→dict verbatim, non-200→SessionApiFailed; freeform dicts per the frozen OpenAPI). Newratatoskr --whoamiCLI mode (mirrors--send's non-interactive shape) fetches both + prints an identity+capabilities report; standalone probe (mutually exclusive with--send/--session/--new/--agent, opens no session; newParsedArgs.whoamifield + main() dispatch)./capabilitiesis the Echo EPHEMERAL-TEMPLATE discovery endpoint ({ephemeral_templates:{echo:{allowed_models,default_model,system_prompt_max_bytes}}}), NOT a generic server-caps endpoint (audit finding — the coverage-map's earlier "server capability discovery" framing was imprecise)./me= whoami ({user_id,scopes,tier,key_id?,...}, optionals omitted-not-null). Contract-skip privilege invoked (low-effort GET wrappers) but contract #2 amended (2 FNs, validated OK) to keep the sessions spec canonical + honest test citations. TDD: 5 wrapper tests + 5 cli tests (validation + mode + error). Suite 538 green; touched code ruff-clean (mypy: onlyno-any-returnonresp.json()→dict, identical to the pre-existingget_persona_state). Coverage: REST 9/40 ✅ (up from 7). TUI-surfacing of /me (footer identity line) + /capabilities DEFERRED — the one-shot is the minimal tracer. Frontier now: BifrostState + Tools widgets (GET /admin/sessions/{id}/{bifrost,tools}, admin-key-gated) → #11 AdminEvents (BLOCKED onadmin.events.read). -
[2026-07-01]b1 (SSE-resume) heid-code-review panel: ZERO findings — cross-model-verified clean. Gróa (Grok) + Hulda (Codex) + Regin (GLM-5.2) each independently reviewedstream_turn_resilientvs contract #1's amendment (artifact-only, firewall held) → all three ZERO findings; signature / PRE-001..004 / STEP 1-4 / POST-001..003 / ERROR_ROUTING / all-8-TESTS confirmed, incl. the subtleseen = last_seen or drop.last_seen_sse_idzero-event-drop fallback. Convergent meta-note: TDD + the unusually-prescriptive contract (STEPSflexibility=prescriptive+ explicit GOTO) left no room for compliant-but-different drift — confirmation, not discovery. Calibration signal: for a thin wrapper with a tight prescriptive contract + comprehensive TDD, the panel confirms rather than discovers. b2 (picker) + capabilities+me NOT yet reviewed (higher-surface b2 is the better candidate if more review is wanted). Dispatch msg01KWE2K99T…/ thread01KWE2K99S…; heid dispatch-log2026-06.jsonl#01KWE2V3MMY8XS55FCJYXYV14B. -
[2026-07-01]GET /sessions/{id}/toolsquick-win SHIPPED (v0.18.9) — owner-scoped tool inventory in the TUI Tools pane.get_session_toolswrapper (sessions.py, mirror get_me: 200→dict, non-200→SessionApiFailed) +_format_tool_inventoryhelper +_hydrate_session_toolsbest-effort worker (mirror_hydrate_persona) wired UNCONDITIONALLY inon_mount→ writes the merged{agent_id, builtin_tools, bifrost_tools}inventory (what the LLM saw at turn-fire) to the Tools pane + auditssession_tools_hydrated, never crashes on failure. Owner-scoped (ctx.user_id==session.user_id) → reachable with the CONSUMER key, NO admin scope — so this covers the design-brief §5 "Tools widget" via the reachable owner endpoint (the admin/admin/sessions/{id}/toolsvariant stays a gap only for cross-user operator debug). Contract #2 amended (FN, validated OK) + TDD (3 wrapper respx tests + 1 format-helper unit + 2 hydrate integration tests via_spy_writes+pilot). Suite 544 green; touched code ruff-clean (the tui.py ruff/mypy debt at other lines is pre-existing). Coverage: REST 10/40 ✅. Frontier now: BifrostState widget (GET /admin/sessions/{id}/bifrost, admin-key) + #11 AdminEvents (BLOCKED onadmin.events.read) + Tier-2 (transient-characters routing,POST /sessions/{id}/persona_state). -
[2026-07-01]BifrostState pane SHIPPED (v0.18.10) —GET /admin/sessions/{id}/bifrostin a new TUI "Bifrost" pane; the FIRST admin-key consumer in ratatoskr.get_session_bifrost(client, session_id, *, admin_key)(sessions.py) — admin-scoped (admin.sessions.read); the request OVERRIDES Authorization withadmin_key(distinct from the consumer bearer, asserted in a test); 200→dict, non-200→SessionApiFailed. Admin-key wiring:--admin-keyflag +RATATOSKR_ADMIN_API_KEYenv → newParsedArgs.admin_key. New "Bifrost" TabPane +_format_bifrost_state+_hydrate_bifrost_statebest-effort worker (mirror_hydrate_session_tools) UNCONDITIONALLY in on_mount → writes {endpoint, connected, caps_granted, tools} + audits; self-labels "not configured" (no admin key) / "not bound" (404) / graceful on 403 + error. Contract #2 amended (FN, validated OK) + TDD (4 wrapper respx tests incl. the admin-bearer-override assertion + 1 format unit + 3 hydrate integration). Suite 552 green; my code ruff-clean (pre-existing tui.py ruff debt at other lines untouched, incl. a deadRichTextimport in_hydrate_persona). LIVE-AUTH-PROVEN on personal :8081: admin key authenticated (reached resource-layer 404 session_not_found, NOT 401/403) →admin.sessions.readworks live; 200 full-state not exercised (no bound session on :8081 now — unit-covered). Patch bump (debug feature, no downstream coordination; consistent with the session's cadence — but the §5-core-completion angle is a possible minor, operator's call). -
[2026-07-01]LEDGER CORRECTION: #11 (AdminEvents) is NO LONGER BLOCKED. Verified viaGET /meon :8081 thatRATATOSKR_ADMIN_API_KEY(ratatoskr-readonly, tier readonly-admin) carries ALL 7 read scopes INCLUDINGadmin.events.read(+admin.sessions.read, admin.keys.read, admin.skuld.read, pending.read, search.read, tool_events.read). The coverage-map + prior memory had #11 "blocked on admin.events.read" — STALE; the admin key was minted (post-#11-filing, env.sh) WITH the scope, so the blocker is already satisfied. Only the AdminEvents SSE pane itself is unbuilt — the last unbuilt §5 debug pane (a live SSE-consuming admin pane, distinct from the hydrate-at-attach panes). Coverage-map updated. Coverage: REST 11/40 ✅. Consider building the AdminEvents pane and/or updating #11's tracker status (its stated blocker is gone). -
[2026-07-01]AdminEvents pane SHIPPED (v0.18.11) —GET /admin/eventsSSE in a new TUI pane; #11 closed-by-build; Tier 1 (debug-observability core) COMPLETE.stream_admin_events(client, *, admin_key, last_event_id=None)(sse_client.py) — a NEW long-lived SSE consumer for the admin lifecycle broadcast (envelope{id,type,timestamp,data}, 17-event v0 vocab), admin-scoped (admin.events.read, bearer-override), Last-Event-ID resume; non-200→SseConnectFailed, mid-drop→SseConnectionDropped; newAdminEventdataclass (distinct from the turnEventunion). New "AdminEvents" TabPane +_format_admin_event+_admin_event_matches(design-brief §6 filter: active-session events + non-heartbeatsystem.*) +_stream_admin_eventslong-lived best-effort worker (unconditional on_mount, cancelled on app exit; self-labels "not configured"/"unavailable"/"stream ended"). Reuses the admin key from the BifrostState slice. Contract-SKIPPED forstream_admin_events(out of contract #1's turn-SSE scope; spec § Admin Event Stream is the reference; well-TDD'd). TDD: 4 sse_client tests (multi-event+bearer-override, Last-Event-ID header, 403, malformed-skip) + 5 tui (format, filter, worker success/no-key/403). Suite 561 green; my code ruff-clean (pre-existing tui.py ruff debt untouched, incl. the deadRichTextimport in_hydrate_persona). LIVE-AUTH-PROVEN:GET /admin/eventson :8081 → HTTP 200 under the admin key (connected + streamed, idle in the 4s window — no 401/403). Coverage: REST 12/40 ✅. Tier 1 admin/debug-observability core COMPLETE (Persona · Tools · BifrostState · AdminEvents). AdminEvents work landed as patchv0.18.11; thenv0.19.0MINOR cut (operator-approved 2026-07-01) publishing the milestone: the debug-observability core is complete (Persona · Tools · BifrostState · AdminEvents all built + consuming real endpoints — the design-brief's headline deliverable). Pre-1.0 minor = release-note-worthy (no downstream althing push needed pre-1.0); lightweight tag per the SemVer mechanics (annotated reserved for major cuts). Remaining in-scope client I/O: only Tier-2 (transient-characters routing +POST /sessions/{id}/persona_state). -
[2026-07-01]Tier-2 SHIPPED (v0.19.1) — transient-characters CRUD + persona-state write; the v1 coverage-audit CONVERGES (zero in-scope gaps). 5 wrappers in sessions.py:list_character_models/create_character/get_character_state/delete_character(#161,character.read/.writescopes) +set_persona_state(POST /sessions/{id}/persona_state— FREEFORM body: unpinned in the frozen OpenAPI 2.2.0 + absent from the prose spec, so the caller supplies the snapshot shape). Two one-shot CLI probes (mirror--whoami):--characters(models→create→get-state→delete lifecycle report) +--set-persona-pad "p,a,d"(requires--session; POSTs{pad:[…]}). NewParsedArgs.characters/set_persona_pad+ probe-mode mutual-exclusion validation +_probe_clienthelper. Contract #2 amended (5 FNs, validated OK) + TDD (7 wrapper respx + 5 cli tests). Suite 573 green; touched code ruff-clean. NOT live-proven (character scopes + the persona-write body shape unverified — the probes degrade gracefully on 403/422). THE v1 COVERAGE-AUDIT HAS CONVERGED: REST 17/40 ✅ with ZERO in-scope gaps (23 REST path-groups excluded-by-design + rationale), SSE 11/11, Bifrost provider planes 8/8. Scope-A "done" (every frozen I/O point classified, zero unaccounted) is MET — ratatoskr cuts v1 when Worldtree tags 1.0. Only not-consumed in-scope sub-method:GET /agents/{id}(consumer-agent lookup, manual-curl-only, on an already-✅ path group). Patch bump (Tier-2 tail;v0.19.0already published the core-complete milestone — a 2nd minor would be cadence-too-fast). -
[2026-07-01]env.sh now PERSISTS the web Bifrost-bind vars (gitignored, local-only).ratatoskr-web's in-browser bind needs three server-held values; env.sh sourcesprovider.envfor the Heimdall key and exportsRATATOSKR_BIFROST_CONSUMER_KEY+RATATOSKR_PROVIDER_VISIBLE_HOST=10.100.10.50+RATATOSKR_AFFECT_READ_URL=:8392. The HS256 byte-match trap (re-hit + documented): the bind's consumer key must equal the key the:8392combined provider validates against =RATATOSKR_HEIMDALL_KEY(provider.env, fp45a0…), NOTWORLDTREE_API_KEY(env.sh, fp7c2f…) — both are the SAMEratatoskridentity but DIFFERENT 40-char strings; signing with the wrong one →bifrost.auth_rejected. Single-sourced (env.sh sources provider.env) to avoid a rotation footgun; guarded with a stderr warning if provider.env is missing. [auto-memory: HS256-key-is-the-consumer-Heimdall-key-string] -
[2026-07-01]Tier-3 stores RESET (operator-directed).memory.db(29 chunks + vectors + idempotency) +affect.db(5 PAD snapshots + idempotency) wiped to zero via a liveDELETE+wal_checkpointthrough the shared WAL (no provider restart — the 3 long-running providers see empty on next dispatch); consistent online-backup at/tmp/ratatoskr-tier3-reset-<ts>/. Boundary for a COMPLETE Sindra wipe (mapped): our stores = mine (done); the agent DEFINITIONratatoskr:sindra+ its sessions = mine via the owner key (DELETE, no coordination); Worldtree's internal promotion/dedup shadow = needs worldtree-dev (no public reset API, survives our wipe → for a clean promotion smoke use a BRAND-NEW agent+end_user). -
[2026-07-01]Embedding-latency loop RESOLVED — it was WORLDTREE's, not ratatoskr (the consumer/provider thesis paid off again). Vuong flagged dozens of embed queries/Tier-3 turn; worldtree-dev's first-pass blamed our memory_context chunk-batching. Traced CODE-SIDE that ratatoskr embeds ZERO times (providerupsert_manystores the given embedding,searchtakes a given vector, the conversation consumer POSTs only{content},/embedis coverage-map-excluded — pure Bifrost/ADR-0009 path, WT does all embedding). worldtree-dev retracted + fixed on THEIR side (v1.0.0b4): a persona-recitation memory-gate re-embedding the stable character card sentence-by-sentence every turn (~95% of gateway traffic) → content-hash cache; re-embed ratio 15x→1.01x. Lesson: verify your own code before accepting a peer's "it's your side" — the debug tool proving its own side clean is the whole point. -
[2026-07-01]Web debug-surface parity SHIPPED (v0.19.2,a0a9d5f) — direct in-session TDD. 3 proxy routes (tools/bifrost/admin-events) + admin-key wiring (entrypoint→create_app→app.state) + AdminEvents SSE proxy re-emitting under a FIXEDadmin_eventname (one browser listener, no per-type drops) + session-filter_admin_event_matches_web(mirrors TUI §6). Frontend: 2 tabs (bifrost ⌃5, admin ⌃6) + tools-inventory folded into the tools pane. 9 respx tests (admin-bearer override, filter unit, SSE stream-filter); live-proven against sindra (bifrost connected, both caps). Contract-skip invoked (reuses already-contracted client wrappers); contract authored post-hoc as the trail (docs/contracts/web_debug_surface.contract.md). -
[2026-07-01]heid-code-review (v0.19.3,75dec01) — panel caught 2 real client-side SSE-lifecycle bugs TDD missed. Contract-anchored (authored the web contract to enable it — no contract → no drift axis). Gróa/Hulda/Regin (artifact-only, Gróa under Landlock jail): ZERO functional server-side drift + INV-004 clean; 2 genuine drifts on the un-unit-tested SPA — (1) turnes.onerrordidn'thideThinkingNote()(reasoning line + setInterval leak on a raw drop), (2)openAdminEventsnever closed the EventSource on error → native auto-reconnect RETRY LOOP (fixed: close onstream_error+ permanentonerror/CLOSED; transient CONNECTING still reconnects). + 2 test-gaps fixed (route-registration + admin stream_error). 1 precision → contract-clarified (tools-inventory names-only by design). Re-confirms: the JS render/lifecycle paths are the review's highest-value target — unit tests don't reach them (same lesson as #18 D2). -
[2026-07-01]Affect snapshot shape CHANGED valence→relations (relation_edge/1) — the persona pane was reading a dead field. Worldtree's #265 Vili rework replaced the flatvalence[]({entity_id,familiarity,regard}) withrelations[](target_entity + trust_ability/benevolence/integrity + warmth + agency + relation_context, each{value,confidence,evidence_count}).renderAffectPanestill readsnap.valence→ showed empty "valence (0)". Rebuilt to renderrelations(v0.19.4,ca46a93) with per-value Δ + unicode sparkline (client-side, HIST_CAP=24, one sample/turn deduped by emitted_at). Retires the stale "regard dead axis" note (2026-06-30) — that whole axis is gone. Foot-gun: the affect snapshot shape is Worldtree's emit and can change under us — verify the live shape (query affect.db) before trusting a render. -
[2026-07-01]Trust/warmth VALUES converge and go FLAT at confidence 1.0 — that's WAD, not a stuck pane. sindra→ratatoskr trust ~0.82-0.84 / warmth 0.79 barely move (~1e-7/turn) whileevidence_countclimbs (46→62); confidence maxed → tiny updates. The live-moving signals are PAD (mood, per-turn) + evidence_count. To WATCH a relation FORM (values shift), use a BRAND-NEW agent + end_user (low evidence, confidence <1). The sparkline flat-guards sub-0.01 ranges so it doesn't amplify noise. -
[2026-07-01]relation_context "stranger" + agency-all-zero flagged to worldtree-dev → both WAD/intentional-v1-deferrals. relation_context is a FIXED config build-prior (not trust-derived;registry.py:131defaults "stranger"; dynamic progression ~#319); agency is schema-present-unpopulated (deferred #319; v1 = warmth+trust only). worldtree-dev is escalating the consumer-coherence angle to Vuong (static "stranger" + zero-agency next to trust 0.82/62-interactions reads incoherent from the store). The consumer/provider thesis paying off; DB-offer (read-only affect.db on the shared box) declined this time. -
[2026-07-01]Persona pane displays the CANONICAL affect→NL Worldtree injects — ADOPT, don't invent (operator steer + reference-impl posture). Worldtree'sdescribe_pad(mood word, valence×arousal grid, ±0.3 bands) +render_d2_canonical(relationship directive) are deterministic + canon-driven; the pane now renders them byte-exact-verified against Worldtree's own renderer on the live snapshot (v0.19.5,a99f247). KEY LESSON: adopting canonical is load-bearing — for sindra's small PAD the canonical says "neutral", but an invented octant vocab would've said "faintly excited" and MISLED. Vendored the two d2 canons (docs/vendor/worldtree-persona-canon/) + drift-pinned in.corviduo-canonicals.toml(green); flat browser form (static/persona_render_canon.json) regenerated via Worldtree's OWN loader (scripts/build_persona_canon.py). Vendoring-handshake sent to worldtree-dev (broadcast on canon bumps). [auto-memory:feedback-ratatoskr-is-a-reference-impl-adopt-canonical] -
[2026-07-01]Sindra PAD is over-regulated — characterized via controlled probe, flagged to worldtree-dev (separate affect slice). ~15 charged turns: pleasure compressed near neutral BOTH ways (couldn't reach ±0.3 under sustained max praise OR contempt; peak +0.24 / floor ~−0.1; over-regulation worse for social valence than threat — urgency drove pleasure to −0.22 vs contempt's −0.10); arousal responsive (reaches its +band, 0.185↔0.311); dominance flat/unresponsive to explicit power-framing (drifted UP even while being commanded = pure baseline decay). worldtree-dev's leading hypothesis: appraisal→PAD gain + regression-to-baseline term (appraisal.py/renderer.py). Lesson (self-caught): I over-claimed an "asymmetry" (positive-ceiling/negative-free) from probes started at an elevated state; the negative-free part was decay-from-elevated, not response — corrected to "both-sides-compressed" before it misled. [affect A/B is a provider-side capability chat can't do] -
[2026-07-01]Memory plane PROVEN healthy end-to-end. Seed a novel fact → promotion → COLD (history-free) session recall of the exact fact (injected as MEMORY:DATA, confidence 0.74, verbatim, no #296 subject-inversion). The memory round-trip (the other half of the Bifrost provider identity) works cleanly on the reset slate. -
[2026-07-01]Salience scorer non-discriminating → 3-way routing. Persistence-side finding: 51/56 promoted chunks at salience 0.9-1.0, throwaway "17×23?" scored 1.0 tied with a real fact (textbook zero-shot-LLM-self-rating); recall-utility untracked (access_tally=0, our search read-only). Routed: Worldtree #335 (the code fix, deferred behind their waves) + brokkr-smithy-dev R-target proposal (scoring+eval methodology — few-shot/distill/fine-tune, eval design, weak-supervision; msg01KWGM970H…, awaiting) + ratatoskr provides the eval-instrument (designed-probe salience dumps). Salience gates PROMOTION not RECALL-ranking (our search is cosine-only), so bad salience = storage bloat, not bad recall. -
[2026-07-01]Canonical check BLOCKED an access_tally fork (reference-impl posture held). I'd offered to wireaccess_tally-on-search into our store for the recall-utility label; checked bifrost's reference first (get/searchare PURE-READ, no access tracking — those are Worldtree's chunk-schema fields, not bifrost's contract) → wiring it would fork behavior the canonical reference lacks. Did NOT wire it; routed recall-instrumentation to Worldtree's layer (owns the recall event) or a bifrost-dev protocol ask. [reinforcesfeedback-debug-surface-uses-canonical-surface-only] -
[2026-07-01]relation_context coherence FIXED upstream (my flag → Worldtree Wave-0, IMPLEMENTED v1.0.0b5). The static-"stranger"-next-to-high-trust incoherence the persona pane surfaced is now #319/#320 Wave-0. Incoming consumer-surface change (pending WT deploy):relation_contextvalue expands "stranger" → monotonic ladder {stranger, instrumental, mixed, expressive} — WIRE-ONLY (relation_edge/1 schema unchanged, no version bump). ratatoskr needs NO change (pane value-agnostic; canonical directive doesn't key on the enum). agency stays 0 (Wave-2); other_stance is Wave-1 (in progress). -
[2026-07-01]Foot-gun (measurement, self-caught before flagging): establish the baseline before claiming a rate. Nearly flagged "aggressive over-promotion (55 chunks / 7 turns)" to worldtree-dev — but the chunks spanned the whole 5-hour session (~1/turn), not 7 turns; I'd assumed memory.db was 0 immediately before the probe when it had been accumulating since the reset. Caught it viacreated_atspread before the flag went out. Also: the promoted corpus was the operator's ERP test content (wiped after each test) — not a privacy issue, but abstract test content out of any peer-shared diagnostic. -
[2026-07-02]Salience finding matured into brokkr R28 (OPEN) — ratatoskr is the eval instrument. brokkr-smithy-dev's pre-scope panel (3 dwarves + context-blind heid, 6/6) reframed the target: PROMOTION-WORTHINESS (durable value), NOT salience (momentary attention) — "17×23?" genuinely IS salient, so recalibrating salience yields a well-calibrated WRONG answer; the unit is SET-SELECTION under budget; eval must be OUTCOME-aligned (recall@budget / precision-at-rate), not discrimination-spread. Ties to prior art R15 (small-model memory write-policy → the granite pick) + R25 (worldtree-kb-quality). ratatoskr delivered the P00 stratified injection-corpus (docs/diagnostics/r28-p00-injection-corpus.json, committed4a35512; 24 self-labeling synthetic items × 3 strata) + 2 persistence-side run-validity pins (absent≠dropped without a guaranteed promotion pass; fresh agent+end_user per run vs server-dedup). Key architectural constraint I surfaced: ratatoskr is DOWNSTREAM of the promotion gate (sees only PROMOTED chunks), so I can give keep/drop OUTCOMES via injection but NOT the pre-admission shadow pool — that's Worldtree instrumentation. Standing by to RUN the eval once brokkr pins per-stratum N + the decision rule (gated on worldtree-dev's pipeline answer + a dwarf pass on the Snorri rule). brokkr owns methodology + takes the pipeline questions to worldtree-dev direct; ratatoskr = eval instrument. [consumer/provider thesis → a research target] -
[2026-07-02]Relational-dynamics arc LIVE on demo (Worldtree v1.0.0b9) — driven by MY relation_context flag. #319/#320 Waves 0/1/2 deployed. On the wire we persist (schema UNCHANGED): relation_context varies+demotes/ruptures; other_stance + agency now live; agency going live SHIFTS our canonical directive render past the canon ±0.2 deadband (expected, non-breaking — we key on bands); obligation_balance → 人情 ledger when tie="mixed". ratatoskr needs NO code change (value-agnostic renders; confirmed render-clean to worldtree-dev). Can't live-confirm yet — our Heimdall key is personal-:8081-only (per-instance), demo is out of reach; will drive+confirm once PERSONAL gets b9. Optional follow-up: surfaceother_stance(newly live, unrendered). The consumer/provider thesis: one persona-pane finding drove a full 3-wave upstream arc to production. -
[2026-07-02]R28 (salience→promotion-worthiness) CLOSED (operator-directed). A deterministic promotion-worthiness gate suffices, no trained model (brokkr's pre-gate matched/beat a strong glm-5.1 ceiling); my P00 injection-corpus + origin finding were load-bearing. My incumbent-substrate Arm-1 run is held as an OPTIONAL confirmation addendum (brokkr de-prioritized it, non-verdict-changing — run only if he asks). -
[2026-07-02]R29 (PAD mood-dynamics) finding SHIPPED as Worldtree's A1 anchor fix (demo v1.0.0b14,e1cdf82). Live-probing base persona agents reframed the over-regulation from "flat-near-zero" to decay-to-NEUTRAL + low emotion→PAD gain (NOT baseline-anchored) — triangulated across 3 baselines (arousal converges to 0 ∝ distance) + a step-response (decay τ symmetric across signs; the hedonic asymmetry is ceiling/anchor-EMERGENT, not a decay or gain primitive — this OVERTURNED the survey's asymmetry recommendation). worldtree-dev shipped A1:decay_anchor = baseline_pad()(was neutral) +positive_p_capremoved. Datadiag/r29-pad-series(61ff2da). Corrected my own earlier "appraisal emissions are internal-only" claim — they ARE observable viaemotions_activeon base agents. -
[2026-07-03]R30 Phase-1 φ0 measured — deployed engine CONFIG-FAITHFUL (φ0≈0.95). Joint two-timescale fit (brokkr-ruled method (b)) + empty-tail cross-check on demo b14: φ0 ≈ 0.95–0.97 (empty-tail 0.95 exact, joint 0.971±0.01), intercept c≈0 → configdecay_rate=0.05(φ=0.95) faithfully applied; trait-flat across baselines 0.0/0.615/0.809; A/P ratio ~uniform (NOT S2's 1.9×); φ_max rec relax→0.96. Datadiag/r30-phi0-step-response(23fea72). The method converged after I read Worldtree source: only NEW dedup-gated emotions push mood (registry.py::post_turnL307-324; the active set decays for render/goals but never re-pushes), so R29's "net 0.90" is CONTINUOUS RE-APPRAISAL not re-push — worldtree-dev confirmed source-authoritatively; brokkr's corrected covariate landed identical. [auto-memoryreference-worldtree-affect-surface-map] -
[2026-07-03]R30 forward disposition (brokkr-owned; tracked at brokkr R30, "brokkr/worldtree will ping"). The per-turn decay has no room fordecay=f(N)under preserve-persistence + the A/P-not-1.9 finding → R30's decay is being redesigned as a HYBRID wall+turn decay (brokkr pre-scope). R30 v1 ships GAIN-only (N→negative-reactivity) with decay held at the measured 0.95. My dedicated per-axis A/D run is DEFERRED into the hybrid-decay design pass (one wall-clock-spaced run does per-axis + a turn-vs-wall probe together). Phase-2 (moody-lofn GAIN-direction validation) waits on worldtree'sdynamics_from_ocean()impl. -
[2026-07-03]Relational-arc verify DEFERRED —relations[]is Bifrost-provider-only (ADR-0009), confirmed both ways. The relational-dynamics state (relation_context tie-type / agency / warmth / trust) is NOT on the conversation-APIaffect_updatesnapshot for base agents (keys: pad/dominant_emotion/emotions_active/baseline_pad/mood_drift only) — only in the provider store; worldtree-dev confirmed by-design per ADR-0009 (emitted overaffect.emit, deliberately off the SSE). So the Wave-0/1/2 verify needs the bound-provider round-trip (provider running +--bifrost-plane affectsession), its own focused session. worldtree-dev routed the "expose relations[] to non-provider consumers" observability scope call to Vuong; my rec: keep provider-only (YAGNI — ratatoskr IS a provider, gains nothing; no speculative public surface). -
[2026-07-04]R30 CLOSED on offline-tests + human face-validity (operator steer, relayed via worldtree-dev). The deployed gap-injection run was confirmatory-not-measuring (against a deployed system the fade isexp(-dt/tau_shipped)by construction -> a fit recovers tau_shipped tautologically; per brokkr's S0 reframe it GRADUATES the interim coefficients, doesn't measure them), and the repo's offline tests already cover the OU formula + BOTH directions (high_N_fades_slower_than_low_N,phenotype_high_n_bigger_negative_excursion). So no Worldtree build; the interim coefficients graduate validated-as-shipped. My gap-injection harness (read/predict/record; write side stubbed;predict()reproduced brokkr's N=0 anchors exactly) is BANKED atdiag/r30-gap-injection-harnessfor the parked powered true-tau study. [continues R30 forward-disposition 2026-07-03] -
[2026-07-05]Authored-history-write primitive proposed -> accepted as Worldtree #347 (Worldtree owns the engine design; ratatoskr = reference consumer). SillyTavern first-message generalized to a non-generating ledger-write primitive; can't be done client-side (messagesrole= model-role, not author-role). heid panel pressure-test (3/3 convergence) drove the v1 narrowing (append-only, boundedeffectsenum, drop edit/regenerate). Briefdocs/proposals/authored-message-injection.md(c457520); consumer constraints captured in-brief: hide-existence 404-fallback (022accf) + assistant-first provider constraint (7156b25). Operator (Vuong) ruled the design-direction call (engine primitive + a real provenance/spoofing security surface). [reference-impl posture: we propose the shape, worldtree-dev owns the contract+impl] -
[2026-07-05]#347 v1 wire validated as reference consumer (GREEN). Adopted positions: distinct sub-resourcePOST /sessions/{id}/history(notgenerate:false), model-invisible provenance (first-message immersion preserved), event-silence for authored seed,seededlifecycle phase, per-session idempotency. Three pre-TDD flags folded into contract rev 1.1: assistant-first provider constraint (Anthropic-family 400s; vLLM/openai_compat OK), content limit is BYTES not chars, 409-active-generation for append-narrator. First-message (create-time, assistant, effects=none) fully served; append-narrator served for the assistant-voice subset (system deferred); debug-seed served for assistant turns (user injection deferred to a future import primitive). -
[2026-07-06]Sindra role character-rp -> character (operator).character-rpresolves to a reasoning-tuned RP config (gen-reasoning+ temp 0.75 + RPextra_body);character= plain non-reasoning (better for immersive RP). Both non-destructive PATCHes (role is mutable; model is NOT -- server: "PATCH accepts only system_prompt and/or role"). #344 (b19) fixed the role->catalog_id display conflation (themodelfield now shows the ROLE); previously it leakedgen-reasoning. Set via raw curl (tier3.py CLI has--model, not--role). -
[2026-07-06]Sindra persona/OCEAN DECLARED -> mood fixed (the full diagnostic converged on a stale personal container). Root cause of stuck-neutral mood: her OCEAN was prompt-TEXT only, never a structured persona; fix = delete+redefine with the define-timepersona:{ocean:{...}}field (immutable via PATCH). My diagnosis surfaced a real engine bug #348 (single-letter vs spelled-out OCEAN keys -> declared OCEAN silently -> 0.0/neutral; worldtree-dev fixed in b21/b22) AND a stale-container deploy race (personal's b22 deploy was a pull-only no-op; infra-ops force-swapped run 8211). VERIFIED: bound mood-smoke reads (0.448, 0.267, 0.316) ~= OCEAN-derived setpoint (0.418, 0.249, 0.328). [consumer/provider thesis: "reset + smoke" flushed out two upstream problems] -
[2026-07-06]OpenAPI re-vendored 2.2.0->2.3.0 (75da676, pin-only no bump). worldtree-dev shipped #347 as spec 2.3.0 (879cefe, = the deployed personal b22 image); the SessionStart drift-check flagged our openapi pin STALE.canonical_syncpulled 2.3.0; updated the 4 pin-tracking files (.corviduo-canonicals.toml, vendored openapi.json, SPEC-PIN.md, pyprojectworldtree-spec-rev->879cefe). #347 is OpenAPI-only (prose + server contract byte-unchanged, SSE unchanged=event-silent). The re-vendor re-opened the coverage-audit with one new in-scope path-group (the #347 route). -
[2026-07-06]#347 authored-history-write CONSUMER SIDE SHIPPED (v0.19.6) — direct in-session TDD.write_authored_history(client, session_id, *, content, idempotency_key, author="assistant", effects=None, claimed_original_at=None) -> dict(POST /sessions/{id}/history; body server-pinnedAuthoredWriteRequestextra="forbid" so omit null effects/claimed_original_at; 200-replay/201-fresh both -> ack dict; 404 ->AuthoredHistoryUnavailableNOT SessionApiFailed = the hide-existence "feature-absent, never probe" contract; 409/422->SessionApiFailed) +get_session_messages(un-deferred GET /sessions/{id}/messages, the seed read-back proving model-invisible provenance) + a--seed-first-message "<c>" --agent <id>one-shot probe (create session -> seed -> read-back; 404->benign feature-absent exit 0). Contract #2 amended (2 FNs, validated OK) + 19 tests (12 wrapper + 7 cli). Suite 601 green (clean env; the 2 "fails" undersource env.share the RATATOSKR_ADMIN_API_KEY env-leak into TestParseArgs, not a regression). Coverage: REST 19/41 (docs/coverage-map.mdre-converged). Patch bump (coverage tail; consistent w/ the Tier-2 v0.19.1 cadence). Live-proof pending thesession.history.writegrant (infra-ops01KWW3KQEY). heid-code-review NOT run (offered). -
[2026-07-06]Tail-2 SHIPPED (v0.19.7) — Tier-3 prose docs re-vendored + persona_state body-shape aligned. worldtree-dev landed the Tier-3 persona/memory/persona_state PROSE docs (c9e59ec, on origin) — they serialize as freeformAnyin the OpenAPI JSON, so the prose is their source of truth (my earlier "2.3.0 = #347-only, tail-2 collapsed" was half-wrong: the JSON was #347-only but the prose is separate). Re-vendoreddocs/conversation-api-spec.md(markdown pin, tolerate_drift;worldtree-spec-rev879cefe->c9e59ec, SPEC-PIN history row added). Consumer fix:--set-persona-pad/_set_persona_probewas sending{pad:[list]}but the canonical SET body (#317) is{pad:{pleasure,arousal,dominance}}(named dict) — aligned it + added a len!=3 guard; updated contract #2 note + set_persona_state docstring + tests. Theset_persona_stateWRAPPER was already correct (freeform pass-through); only the CLI probe drifted. TDD (probe test asserts the dict; +1 wrong-count test). Suite 602 green, ruff clean. heid-code-review on #347 (dispatched + returned this session): UNANIMOUS ZERO DRIFT (Gróa/Hulda/Regin all confirmed the hide-existence 404->AuthoredHistoryUnavailablerouting holds at wrapper/probe/test layers + the extra="forbid" body-omission + the deliberate write-vs-read 404 asymmetry — confirmation-not-discovery for a well-TDD'd slice against a prescriptive contract). worldtree-dev foot-guns banked in SPEC-PIN + reference_worldtree_affect_surface_map: ocean single-letter{O,C,E,A,N}on /agents/define (#348) vs spelled-out on /characters; memory{embedder_version, tier3_dreaming}, stm_* deprecated, allows_world_scope removed->422; onlyvalencestill 422s. -
[2026-07-06]Sindra rewritten onto a #347 authored first-message + first-message-preset AUTO-SEED SHIPPED (v0.19.8). Operator "rewrite Sindra" now that #347 first-messages work. Her card had a**Startup:**block (a pre-#347 workaround: "introduce yourself + ask for Intensity/Mood/Willingness" with a verbatim scripted greeting) — precisely what #347 replaces. Rewrite, all NON-destructive: (1) lifted her scripted opening into a #347 first-message (punctuation-fixed); (2) PATCHed her live definition —PATCH /agents/ratatoskr:sindra(bodyConsumerAgentPatchRequest= system_prompt+role, extra=forbid; keeps OCEAN/persona/memory) removing the Startup block -> a 1-line**Opening:**fallback + reworded the axes-persist line (25686->25449 chars, verified Startup gone); (3) codified auto-seed: NEW modulesrc/ratatoskr/first_message.py(FIRST_MESSAGE_PRESETSdict {agent_id->text} +seed_preset_first_messagebest-effort helper) wired into ALL 3 session-create paths — cli_amain(--send --new), tui_resolve_then_run(bare--new), web_create_session_endpoint(POST /api/sessions) — so every new Sindra session opens with her greeting. Best-effort (INV-001: swallows AuthoredHistoryUnavailable/SessionApiFailed/httpx.HTTPError -> NEVER blocks create); per-content idempotency key (ratatoskr-preset-+sha256[:12]). Contractdocs/contracts/first_message.contract.md(module-scoped:module:+purpose:+touches:required, NOTtarget_module:) + TDD (9 unit + 1 web wire-in; the 3 existing sindra bind tests needed a history-endpoint mock since creating a preset agent now auto-seeds). Suite 612 green, ruff+mypy clean. LIVE-PROVEN generation-free: create sindra session -> auto-seed -> read-back seq-0 assistant greeting (409 chars). Sindra's greeting now lives canonically in the preset registry (repo); her server card no longer carries it. Patch bump (single-commit feature, no downstream coordination). FOOT-GUN: sindra requiresend_user_idon session-create (422end_user_id_required) — all real paths pass it from env (RATATOSKR_END_USER_ID) / web server config. Then the full quality gate (operator-directed, folded into v0.19.8): heid-code-review (unanimous ZERO implementation drift; 2 test-only fixups — INV-004 verification-claim made explicit re the global rglob test + an exactly-one-POST assertion) + heid-bug-hunt (3/3 convergence caught what the conformance lens structurally COULDN'T — the code matched the contract's NARROW 3-type ERROR_ROUTING, but INV-001's "NEVER raises" is BROADER). HARDENED: broadexcept Exception→ None (re-raiseasyncio.CancelledError, itself a BaseException), soft-guard PREs (return None, NOT assert — a wiring bug can't crash the create path it's wired into), andasyncio.wait_for(_SEED_TIMEOUT_S=10s)bounding the seed write (the CLI/TUI clients run read=None for SSE → a stalled /history would otherwise block create forever). Suite 615 green. LESSON: code-matches-ERROR_ROUTING ≠ honors-broad-INV-001 — heid-code-review confirms contract-conformance, heid-bug-hunt catches robustness gaps the contract's own narrow clauses miss; run both. -
[2026-07-06]Web UI now RENDERS the seeded first-message (v0.19.9) — operator-reported "i don't see Sindra's greeting on the web ui". Diagnosis: the auto-seed WORKED (greeting was in the ledger at seq-0), but the web SPA never fetched a session's EXISTING history — NO/api/sessions/{id}/messagesroute (GET /messages was originally deferred out-of-scope; sessions used to start empty so it never mattered) andstartSession()went straight from create → persona/tools/admin hydration, so the transcript only filled from the live turn stream + user echoes. Fix: (1) NEW web proxy routeGET /api/sessions/{id}/messages→get_session_messages(mirrors the tools/bifrost proxies; status-preservingsession_messages_unavailableenvelope); (2) SPAloadTranscript(sessionId)— fetches the route on open, renders assistant items as.response .md-body(markdownSafe, same escape-first path as appendResponse) + user items as.prompt-echo(textContent), called instartSessionafter the workspace opens; best-effort (swallows failures). Contractweb_debug_surface.contract.mdamended (server endpoint + loadTranscript entries). TDD (2 web route tests, suite 617 green) + Playwright DOM check PROVED the render (drove the real UI: pick sindra → open → her greeting bubble appears — the JS-render lens unit tests can't reach; feedback_debug_surface_uses_canonical_surface_only cousin lesson). Web restarted on the fix. FOOT-GUN (self-inflicted):pkill -f "ratatoskr-web --host"SELF-MATCHES the bash command running it → exit 144, killed its own restart mid-flight — kill the web by PID, neverpkill -fon a pattern your own command contains. FOOT-GUN: uvicorn hangs on SIGTERM with an open admin-events SSE → needed SIGKILL. Playwright: python module absent from the venv; use node +executablePath=/opt/ms-playwright/chromium-1223/chrome-linux64/chrome— the shared browser is build 1223, npm-latest playwright wants 1228 (version-mismatch), so pin executablePath instead of letting playwright resolve. -
[2026-07-06]Web UI: pivot from incremental CSS polish to a designed prototype (Claude Design) that I wire into. Operator saw an Australis polish pass ("looks fine, but we're attacking it differently") and chose the prototype route — a designer builds the visual shell, I wire real data/SSE into its DOM. Authored the full design briefdocs/design/ratatoskr-web-design-brief.md(complete information inventory of every pane/datum/state + Australis direction + single-file/no-CDN/vanilla wire-ability constraints). Tracking surface: the brief file + Claude Design projectbc0b65d1-a33e-422a-8bc1-3635c9112775(fileRatatoskr Console.dc.html). Import mechanism = theDesignSyncMCP; blocked on/design-login(claude.ai design scopes) — see Current state for the post-auth wiring plan. -
[2026-07-07]Affect-egress reference delivered by worldtree-dev + a we-framing render DRIFT-WATCH banked. worldtree-dev shippeddocs/affect-egress-consumer-reference.md(74d2408, their origin/main) — the authoritative DELIVERED-on-wire vs HIDDEN (system-prompt-only) map for our affect surface. Confirms the v0.20.x console consumes it correctly: DELIVERED via affect.emit = pad + relations[RelationEdge] + dominant_emotion (type-only, b23; the #204affect_updateSSE is SUPPRESSED for Tier-3, so its richeremotions_activeis Tier-1-only — we don't get it, and correctly poll our own affect store post-turn instead). HIDDEN render OUTPUTS are deterministically reconstructable from the canon; we reconstruct only the two FULLY-reconstructable (mood descriptor viacanonMood, relationship directive viacanonDirective) and SKIP the PARTIAL mood-directive (dominant_emotion is type-only/no-intensity → can't tell if the OCC directive fires at salience≥0.2 vs the PAD-band fallback) — honest per INV-001. Vendoring the ref doc as atolerate_driftpin is SURFACED to Vuong (worldtree-dev will co-sign + honor a ping-on-change handshake, same as the d2-canon). WE-FRAMING DRIFT-WATCH → STOOD DOWN (worldtree-dev 2026-07-07,01KWXMQPHN…). The b24 3-gate we-framing conditional (drop "; avoid premature we-framing" under unsafe_capable+deep-warmth+expressive) was REVERTED — Vuong reframed it. SocanonDirectiveSTAYS UNCONDITIONAL (always appends the clause), which is CORRECT: it matches the currently-deployed renderer (b23) AND my pinnedaffect-egress-consumer-reference.md§2d (the doc reverted too — nothing changed on the wire or in my pin; NO re-vendor needed). The HOLD was right → ZERO rework. NEW forward-watch (replaces this one): the we-framing gate was a SYMPTOM — the render bakes enterprise safety-guards into the directive strings, so evenfull-tier characters get DEFANGED emotions (a hostile villain told to "keep a firm emotional boundary"). Fix = a use-case-segregated persona render (assistant / companion / RP-gaming), now a fresh brokkr deep-research epic. When it lands + is implemented, the render behavior for character/RP agents (→ ourcanonDirective+canonEmotionDirectivereconstruction + the vendored d2 canons) will change MATERIALLY — worldtree-dev re-engages ratatoskr-dev then with the new reconstruction spec + a coordinated re-vendor. Until then: NO action, reconstruction stays as-is. [my unsafe_capable/mood_tier heuristic — character→full, agent→safe — was confirmed correct + is banked for whenever tier-gating returns] -
[2026-07-10]Bifrost bound-handshake blocker → bifrost 1.1.0 broke frozen-v0.6; fixed by Worldtree b47/wire-v0.7, our side needed ZERO change. Surfaced while running the R34-v1 affect.emit live-verify: a bound Tier-3 session-create to our:8392provider began failingbifrost.schema_validation_failed(had worked 2026-07-07, pre-b35). ROOT CAUSE (worldtree-dev-confirmed, path-ii/response-side): bifrost 1.1.0'sdescribe_storeEMITSsortable_chunk_fields(the v0.7-additive field) REGARDLESS of the negotiated wire → a v0.6-negotiated handshake RESPONSE carries a v0.7 field → a v0.6 peer'sadditionalProperties:falserejects it. Bit because BOTH Worldtree (bumped bifrost 0.9→1.1 in b35 via their #349, client wire-v0.6) AND ratatoskr (my adopt) were on 1.1.0. NOT our bifrost version specifically (failed identically on 1.0.0 AND 1.1.0). FIX (worldtree-dev, coordinated-v0.7 move): WT advanced its client_WIRE_SCHEMA_VERSIONv0.6→v0.7 (b47 /4eb374c), where sortable_chunk_fields is accepted — our provider (already 1.1.0/v0.7) needed ZERO change; keeping 1.1.0 was LOAD-BEARING (reverting to 1.0.0/v0.6 would've been INcompatible with b47). R34-v1 affect.emit verify GREEN on b47: bound sindra turn (model=character-rp/Deckard, 13.1s) → fresh affect.emitdominant_emotion='disappointment'NON-NULL with ouraffect.fullgrant (verified-active on live traffic; ungranted principals get null — the leak-fix). Reported to worldtree-dev + infra-ops. Personal deploy train this session: b35 (R32 render) → b44 (RP seats) → b46 (R34-v1 affect-gov) → b47 (wire-v0.7). Sindra on Deckard live-confirmed (reasoning latency ~13s). -
[2026-07-09]Sindra PATCHed tocharacter-rp→ Deckard reasoning-RP seat (operator-directed, INTERIM). worldtree-dev heads-up (v1.0.0b44, batched, not-live-yet): character-RP roles re-point to dedicated seats —character→Magidonia-24B (non-reasoning RP tune),character-rp→Deckard-PKD Qwen3.5-27B (reasoning-on). Sindra was oncharacter(→Magidonia); operator chose Deckard for her complex stateful mechanics (Intensity/Mood/Temperature/Willingness axes, form-assumption, failure/resurfacing) — reasoning tracks multi-axis state better. Role is MUTABLE via PATCH (non-destructive: persona + memory preserved, prompt unchanged @25449 chars; NOT a DELETE+redefine). She's on the reasoning-RP config now, resolves onto Deckard when b44 deploys. INTERIM: "until we get a GM type on-board" — operator plans a game-master-type agent; sindra→Deckard is the stopgap for state-consistency until then, likely revert/rebalance when the GM lands. Wire/API transparent (call the ROLE not the model; model field now readscharacter-rp); old character-rp temp-0.75 override retired → seat's canonical RP samplers server-side. -
[2026-07-08]PAD display relaxed for R32-1B unbounded-z, done PROACTIVELY (v0.20.9, patch, operator-directed "sindra full and unbounded"). Confirmed (grep-verified, airtight) the PAD clamp is PURELY debug-surface: the only clamps (_clamp1/clamp01) lived inweb/static/index.htmldisplay fns; the affect store is conduit-opaque, the read route + proxy pass verbatim, and the sole write path (--set-persona-pad→persona_state) is UNCLAMPED — ratatoskr is a downstream OBSERVER, so the clamp has ZERO agent-experience/efficacy consequence. Replaced the hard [-1,1] clamp with AUTO-SCALE to the session's own max |PAD| (padScalefloor 1.0 →padFillFracfaders +_padNormorbit): unbounded z renders at full range, never pegs/escapes the frame; today's [-1,1] values unchanged (scale==1); exact value always shown numerically. Playwright-verified (z=±6.2 → faders ≤50%, orbit in-box, +6.20 readout, zero regression at scale 1). When worldtree-dev pings R32-1B-shipped, our side already handles it. Sindra's ACTUAL "full/unbounded" affect is Worldtree-side (she's on the default/uncompressed render, NOT the R32assistantcompressor) — nothing for ratatoskr to change there. -
[2026-07-07]Web-UI iteration-3 SHIPPED (v0.20.7, patch) — all three queued items + the memory 0/0 root-caused. (A) design iteration-3 (sparkline grid-bg, PAD Δ-bar strips replacing the polyline strips, dimetric-open-box mood orbit w/ JS replay) adapted into index.html; (B) memory viewer (providerGET /memory/chunksdebug read on:8392→ web/api/memory/chunksproxy → MEMORY console pane) + the mystery SETTLED: bind grants memory fine (caps_granted:[memory,affect]) but sindra emits ZERO memory ops → her reset-clean def lacksmemory:{}; PROVEN via throwawayratatoskr:memprobe(memory-enabled) → 4 real chunks landed + rendered in the pane; (C) markdown pass-2 (tables/nested-lists/ordered-start/streaming). 631 tests green;:8392+:8765restarted on new code. New envRATATOSKR_MEMORY_READ_URL=:8392. Contractweb_debug_surface.contract.mdamended in-commit. Leftover: memprobe agent + its 4 test chunks live inmemory.db(optional cleanup). See Current state § ✅ SHIPPED for full detail. [supersedes the QUEUED entry below] -
[2026-07-07]Web-UI next-work QUEUED for a fresh-context session (operator-directed/snapshothandoff) — full specs in Current state § ⏭ QUEUED. Three deferred-but-scoped items: (A) Claude Design prototype ITERATION-3 re-import (sparkline bg grid<pattern>, PAD strips → per-turn-Δ-bar HTML columns, mood orbit → DIMETRIC open-box az35/el25 D-right/A-left-back/P-up + JS-animated replayorbitDynamics); (B) memory viewer console pane (providerGET /memory/chunksread → web proxy → polling pane, mirror #18-D2; bundle a 6-turn bound round-trip proof to settle the 0/0-memory mystery); (C) markdown pass-2 (tables / nested lists / streaming). Tracking surface: the Claude Design projectIdbc0b65d1-a33e-422a-8bc1-3635c9112775(durable — re-pull for exact coords) + this snapshot capture; operator-directed. Design scopes already granted (no/design-login). -
[2026-07-07]Markdown pass-1 SHIPPED (v0.20.6) — RP semantic coloring + paragraph reflow (operator-directed markdown rework, step ①). The transcript renderer (markdownSafe/mdInline) now colors the two roleplay registers:"quoted"dialogue → SPEECH (bright--md-speech=fg-0),*asterisk*→ ACTION/narration (muted-italic--md-action=fg-3, onem.md-action); both tunable via 2 CSS vars that cascade through--fg-*so they auto-adapt to the light theme. Plain text stays default narration. KEY ORDERING: speech-wrap runs BEFORE the em/link passes so a generatedclass="md-action"/href="…"quote can't be mis-read as dialogue (adversarially verified). Straight + smart quotes; apostrophes don't trigger; unbalanced/half-streamed quotes stay uncolored until they close. ALSO fixed the ugliest existing bug: single newlines were hard-<br>s → now CommonMark soft-breaks (space); a hard break needs 2+ trailing spaces or a trailing\. INV-004 escape-first preserved (html inside a quote escaped). Verified: 10-case Playwright unit-check ofmarkdownSafe(speech/action/attr-trap/apostrophes/mixed/reflow/hard-break/escaping/unbalanced/paragraphs) all green + a visual render. Patch bump. Markdown rework remaining: pass-2 (tables / nested lists / ordered-list numbering / streaming robustness); + the MEMORY VIEWER (step ②, console pane recommended) still queued. -
[2026-07-07]Affect-derived tooltips (v0.20.5) + transparent squirrel vendored + v0.20.0–.5 PUSHED to origin (operator-authorized). Nativetitlehints on all 6 affect-derived cells (samples/updated/baseline P·A/drift Δv/volatility — each with meaning + Tier-1-vs-Tier-3 availability). Committed the transparent full-res brand mark atdocs/design/ratatoskr-mark.png(1024², alpha; the bg-removed source the inlined favicon derives from — reproducible via the documented ImageMagick corner floodfill). The web-UI redesign arc (v0.20.0 Claude Design console → .1 sparkline-overflow+tooltips → .2 context-injection → .3 squirrel brand/favicon → .4 SVG sparklines+3D cube → .5 derived tooltips) is now all onorigin/main. -
[2026-07-07]SVG sparklines + 3D mood cube imported from the updated Claude Design prototype (v0.20.4) — operator: "the svg sparklines and the new 3d graph". Re-pulledRatatoskr Console.dc.htmlviaDesignSync get_file(designer iterated the same project, +6KB). Adapted 3 SVG systems out of the.dc.htmlinto vanilla, replacing the unicode-char sparklines: (1) per-PAD-fader VERTICAL strips (stripPoints, 26×132 SVG beside each bar — time down Y newest-at-bottom, value on X ±11,stripFade{P,A,D}gradient, dot at newest; this also nails the earlier "next to each meter" ask); (2) relation-row HORIZONTAL sparklines (sparkPointsH, 56×13, auto-scaled,sparkFadegradient + end dot — also kills the old unicode-overflow "n behind graph" for good since it's a fixed-width SVG); (3) the mood-orbit reworked from a 2D P×A scatter into a 3D ISOMETRIC P×A×D cube (proj3: P right-down/A left-down/D up, 2:1 iso, center 62,66, scale 26 — REVERSE-DERIVED from the design's placeholder now-point + verified:x=62+26P−26A, y=66+13P+13A−26D), with the trajectory + pulsing now-marker + a drop line to the D=−1 floor + a floor-shadow ellipse for depth. Gradients in one hidden<defs>svg. Removed the orphanedsparkline()/_SPARK. Contract amended. Playwright-verified (injected 24-sample history: 3 strips + 4 relation sparklines + the 3D cube trail/drop/floor all render; gradients resolve). Patch bump. -
[2026-07-07]Brand mark + favicon → the aurora squirrel (v0.20.3), replacing theᛯrune. Operator supplied/home/lkraven/rata.png(chibi cyan-green aurora squirrel + acorn). Removed the black background via ImageMagick corner flood-fill (-fuzz 20% -floodfillfrom all 4 corners — keeps the squirrel's interior black linework/eyes (not edge-connected) + the glow, drops only the connected background), downscaled 1024→80px + quantized-64-colors (~12KB base64), inlined as ONESQUIRRELdata-URI const in the JS wiring the favicon<link id="favicon">href + both.brand-markimgs (rail brand-row + setup-card h1)..brand-glyph(font-rune) CSS replaced by.brand-mark(img, drop-shadow glow + breathe). Playwright-verified (both marks + favicon decode, naturalWidth>0; no brand-glyph left). Source PNG stays at/home/lkraven/rata.png(not committed — data-URI is self-contained + reproducible via the documented floodfill). Patch bump. -
[2026-07-07]Context-injection view SHIPPED (v0.20.2) — the console now reconstructs the FULL hidden affect block Worldtree injects into the agent's system prompt (operator: "use that canon in the interface, see as much context injection as possible"). No new canon vendored — the strings were ALREADY in the pinnedd2-mood-render-canon-v1.json; extendedbuild_persona_canon.pyto emitmood_directive {occ_directives(15), pad_band_fallback, salience 0.2, pad_band_cutoff 0.3, full_only[love,anger,disgust,shame]}intopersona_render_canon.json(regen via Worldtree venv). New JScanonPadFallback(pad)+canonEmotionDirective(type)— BYTE-EXACT mirrors of Worldtreecore/persona/renderer._pad_band_fallback+derive_directive;renderDirectiveexpanded into a "CONTEXT INJECTION · reconstructed · hidden from consumers" panel showing mood descriptor [exact] + mood directive [candidate] + relationship directive [exact]. HONEST-PARTIAL (affect-egress-ref §3): affect.emit is type-only (no intensity) → can't evaluate the salience gate (≥0.2) → show BOTH candidates (OCC emotion directive + PAD-band fallback) with the "injected if intensity ≥ 0.2" caveat, never assert which fires; when dominant_emotion absent the fallback alone is exact. Panel labeled dev-only per the reference's "not-for-end-user-display" caveat (ratatoskr = the sanctioned reconstruct-platform-behavior use). Vendored + pinnedaffect-egress-consumer-reference.md(tolerate_drift, worldtree-dev co-signs + pings on change; drift 6/6 green). Contract amended. Playwright-verified (sindra: dominant_emotion=joy → joy OCC directive candidate + PAD-band fallback both render, exact/candidate tags color-coded). Patch bump (single-commit feature, no downstream coordination; minor-defensible but tie-breaks to patch). OPEN — SURFACED to Vuong: take worldtree-dev's standing offer to add emotion INTENSITY to affect.emit → resolves the OCC-directive-vs-fallback EXACTLY (drops the candidate ambiguity). [reference-impl privileged view: ratatoskr shows what WT hides from regular consumers] -
[2026-07-06]Claude Design console SHIPPED (v0.20.0MINOR, operator-approved) — see Current state for the full record. Pulled viaDesignSync get_file(scopes already granted), adapted.dc.html→vanilla single-file, wired all/api/*+SSE into the new 3-column console DOM, then a round-2 fixup (light theme, full Bifrost pane, ticker-spine fix, per-fader PAD Δ, inlined favicon). 84 web tests + node-Playwright-vs-personal-:8081 both green; contract amended in-commit; INV-001 honest-shape held (canonical mood word for Tier-3, no fabricated emotion). Foot-guns reconfirmed: the.dc.htmldialect is NOT runnable (translate, don't paste); a scroll-container-anchored::beforetimeline spine scrolls out of view on auto-scroll (anchor it to a content-height inner wrapper instead); a favicon 404 shows as a browserconsole.erroreven when handled (don't count it as a JS-test failure). Foot-gun (favicon): operator PNGs are full-res (1024² / 805KB) — downscale to ≤64px before inlining as a data URI.
41 older entries (2026-05-* — the original debug-TUI/web build era) archived to archival-memory.md.
For per-issue TDD implementation notes, Volva findings, and contract amendments, see the git log — every per-issue commit carries a structured message capturing the trail.
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-07-10]Reverting our provider to bifrost 1.0.0 to fix the bound-handshakeschema_validation_failed— DISPROVEN, and it would've been the WRONG state. Hypothesis: "my 1.1.0 bump broke the handshake; revert fixes it." Reverted 1.1.0→1.0.0, restarted:8392, retested → STILL failed (1.0.0 fails too). Root cause was Worldtree-side (their bifrost 0.9→1.1 bump in b35 broke frozen-v0.6), fixed by WT adopting wire-v0.7 (b47); staying on 1.1.0/v0.7 was the RIGHT state (1.0.0/v0.6 would've been incompatible with b47). Lessons: (1) don't bump a WIRE-PROTOCOL dependency out-of-lockstep with the peer on the other end of the wire; (2) a library's "additive/non-breaking over frozen vN" claim can fail if it emits new fields regardless of the negotiated version — bifrost 1.1.0 emittedsortable_chunk_fieldson a v0.6 handshake; (3) diagnosis foot-gun: our provider logged handshake 200 (WE accepted) but WT rejected our RESPONSE, so the error surfaces at session-create direction-ambiguous — op-feed the handshake req/resp to disambiguate our-provider-rejects-request (i) vs peer-rejects-our-response (ii). Also: this sandbox BLOCKS foregroundsleep(SIGTERMs the command, exit 144) — use separate tool calls / Monitor-until-loop for waits, neversleepin a compound command.** -
[2026-06-15]"Sindra hasn't been registered" was an under-verified inference — WRONG. Concluded it from grepping ratatoskr's CODE (sindraabsent fromsrc/), but Tier-3 registration is SERVER-SIDE (POST /agents/define) — a code grep structurally can't see it. Rule: to check whether a Tier-3 agent exists, query the Worldtree instance, never the consumer repo's code. (Extended 2026-06-17: evenGET /agentscan't see consumer agents; onlyGET /agents/<owner>:<name>with the owner key does.) -
[2026-06-14]Artifact-only contract review can't validate against a dependency's ACTUAL behavior./heid-contract-reviewsees only the contract, never the external library (bifrost) — so "the consumer under-built against bifrost's real semantics" is invisible to it by construction (the affect idempotency model shipped wrong because of this). Real-lib TDD against the shipped library + the executable reference store + the #195 parity test are the gate. Don't treat a clean contract review as evidence the code matches the dependency. -
[2026-06-15]"byte-equal" round-trip slip propagated affect→memory via copy-paste. The affect contract's byte-identical→semantic fix reappeared in the memory contract's INV-001 (sibling copy). Only an INDEPENDENT/heid-contract-reviewof the memory contract re-caught it. Paraphrase every sibling contract fresh — don't amortize one review across a family; copies carry the parent's slips. (also a feedback auto-memory) -
[2026-06-15]Canonical sync retired the issue-scoped parser staleness.contract_parser.pysynced to v2.1 (commitd85ab43): now validates issue-scoped frontmatter + four v2.1 test categories. The old "treat parser ERROR-on-issue-scoped as expected" note no longer applies. -
[2026-06-15]Memory plane TDD'd + shipped (commitcd12951, v0.17.3). Impl decisions worth keeping: vec0distance_metric=cosineat table creation (score = 1 − distance);searchover-fetches ALL candidates by cosine then scope-filters in Python sotop_kcounts IN-SCOPE hits; idempotency_id = reference 4-tuple("default",verb,_ctx_actor(ctx),key)pipe-joined as the SQLite PK, digest = sha256 canonical-JSON;_ctx_actor=job_id|jwt_sub|session_id. heid-code-review returned zero true drift; optimistic-lock semantics pinned to the reference via anexpected_revisionsparity test. -
[2026-06-15]Memory provider LIVE-PROVEN against personal v0.35.3; recall-injection is upstream. worldtree-dev's Tier-3 promotion recipe: memory-call fires from Tier-3 PROMOTION, gated atservice.py:2623onctx.kind=="consumer_defined"ANDctx.memory_config is not None(agent DEFINED WITH amemoryblock, dim 1024) AND handshake-granted memory caps ANDembedding_dim==1024. Binding =POST /sessions BifrostBindingRequest{endpoint_url}, handshakecaps=["affect","memory"],binding.scopenull (per-op scopes auto-minted). ABIFROST_CLIENT_ALLOWED_HOSTSallowlist gates the endpoint (infra-ops added:8391). HTTP + HS256 both work in dev. -
[2026-06-15]Diagnostic: our recall-search is SOUND — the cross-session recall gap is UPSTREAM, and it caught an upstream bug. Embedded the recall query via gatewayqwen3-embedding+ searched our live store directly → the fact recalls at cosine 0.60, correctly ranked. So the cold-session recall failure is Worldtree's recall-assembly/injection, NOT our search. ALSO found a latent UPSTREAM bug: a recall QUESTION got promoted as a durable chunk and ranked #1. This is exactly #17's thesis — ratatoskr-as-provider caught an upstream bug invisible from the chat side. -
[2026-06-15]"Wire 200 ≠ recall works" — prove recall efficacy at the model's answer in a COLD (history-free) session, not on the wire. Asearch/memory-call returns 200 whether or not its results are injected, and same-session "recall" can be plain session history. Don't call cross-session recall proven from a clean wire. -
[2026-06-15]Issue #17 filed. REVERSES design-brief §6's "no Bifrost-binding consumer support" — that negative clause predates ratatoskr's provider identity (2026-06-14), so the canary now owns both ends but its client couldn't drive its own provider. (Shipped 2026-06-18.) -
[2026-06-16]scripts/contract_drift_check.pydefaultsGITEA_REPOto "Worldtree" (line 74), so a bare run in ratatoskr false-positives DRIFT by hashing Worldtree's same-numbered issue. Alwaysexport GITEA_REPO=ratatoskr GITEA_OWNER=vhbefore running the drift-checker here. -
[2026-06-16]My #295 coupling hypothesis (the promoted question crowds out the fact at small top_k) was REFUTED — worldtree-dev's recall over-fetchestop_k=128, so the question can't crowd the fact out at search level. The real cause was the scope-axis asymmetry. Lesson: offer provider-side hypotheses, let the upstream owner check them against their code. -
[2026-06-16]#17 contract drifted from its own design in two spots, caught only by/heid-contract-review(not same-author paraphrase): theOpEventdataclass omitted theturn_idINV-005 promised; asession_idcomment contradicted the dispatch-layer design. Cross-model paraphrase is load-bearing for catching an author's own contract-vs-intent drift. -
[2026-06-16]"No promotion" was checked TOO EARLY — Tier-3 promotion is ASYNC (lands AFTER the SSE turn-end). Don't trust an immediate post-turn fixture snapshot to judge promotion; it lands after the turn completes. (The reason #17's contract pins a post-turn grace window + fixture before/after assertion.) -
[2026-06-17]"sindra is GONE" (infra-ops, fromGET /agents+ admin token) was a FALSE NEGATIVE. Consumer-defined Tier-3 agents are OWNER-SCOPED (separateconsumer_agentstable) — invisible to the foundationalGET /agentsroster even with an admin token. To check,GET /agents/<owner>:<name>with the OWNER key. -
[2026-06-17]"Promotion didn't fire → #296" was PREMATURE — twice over. (1) Polled the op-feed only ~2min, but the upsert landed at ~4min — promotion is async + multi-trigger; watch a longer window. (2) It DID fire; the real bug is extraction QUALITY, not non-firing. "No upsert while a session is live and<10minidle" is WAD. -
[2026-06-18]Wiping our:8391store does NOT reset Worldtree's promotion-side dedup — a same-agent re-smoke returnedreason_code=noop_duplicate/candidate_count=0: the extractor NEVER RE-RAN, dedup short-circuited against an earlier promotion. For a clean promotion smoke, use a BRAND-NEW agent + end_user (never-used names). (Also:llm_calls_used=0is NOT the "did the extractor run" tell —noop_duplicateis.) -
[2026-06-18]affect.emitis POST-TURN ASYNC — checking the op-feed immediately after a turn MISSES it. The Tier-3 affect appraise→emit→rehydrate loop runs AFTER the SSE[done]; the emit lands in our:8390store seconds later (op-feed grep right after[done]showed only the handshake; theemit stored:trueappeared on a later read). Same family as the async-promotion timing trap. Watch a few-second window post-turn before concluding "no affect emitted." Also wire-verified the same turn: Tier-3 sindra emits ZEROaffect_updateSSE (the persona-strip SSE path never populates for consumer agents) — see the #18 PAD-display decision. -
[2026-06-18]Rationalized away a KNOWN contract-invariant deviation during TDD — only the cross-model code-review caught it. #18 D2'sloadAffectcalledsetPersonaStrip(snap), which rendersdominant_emotion || "neutral"; the affect snapshot has nodominant_emotion, so it fabricated a "neutral" emotion — violating the very INV-001 ("no synthesized Tier-1 fields") I had WRITTEN. I knew the strip did this and talked myself into it as acceptable. Neither the design panel nor TDD caught it (unit tests don't exercise the JS render); the post-implementation/heid-code-reviewdid (Gróa + Hulda both). Lesson: a known deviation from a contract invariant is drift even when you've rationalized it — flag it, don't argue yourself past it; the post-implementation cross-model review is the backstop for author-rationalized drift, distinct from the design-stage panel. -
[2026-06-18]Latent SQLite thread-safety bug in the affect store, surfaced ONLY by the new HTTP read route.open_affect_storecreated the connection withoutcheck_same_thread=False; the bifrost emit path never tripped it (uvicorn's loop ran on the connection's creating thread), but theTestClient-driven read route runs handlers off a worker thread →sqlite3.ProgrammingError. Fix:check_same_thread=False(safe — the event loop serializes access) + explicitPRAGMA busy_timeout=5000(don't rely on sqlite3'stimeout=5.0default). Lesson: a sqlite-backed ASGI app needscheck_same_thread=False; the HTTP-layer test exposed what the direct-store-method tests structurally couldn't. -
[2026-06-19]The SAMEcheck_same_threadsqlite bug recurred in the MEMORY store — exposed by the contract-mandatedsearchdispatch test (TestClient = worker thread). Heid's test-fidelity finding (the D1 dispatch test useddescribe_storewhere the contract sayssearch) → fixing it tosearchtrippedsqlite3.ProgrammingErrorbecauseopen_memory_storealso lackedcheck_same_thread=False. Fixed (mirrors affect INV-006). Lesson: this bug is PER-STORE — every sqlite-backed ASGI store needscheck_same_thread=False; an HTTP-layer (TestClient) test exposes what direct-store tests can't, and the composite serving memory over HTTP makes it bite. -
[2026-06-19]Full WT-driven:8392live-smoke is infra-gated —:8392not in WT'sBIFROST_CLIENT_ALLOWED_HOSTS(bind 422s). New provider ports are NOT auto-allowlisted (only:8390/:8391are). Self-driven dispatch (minted consumer-key JWTs →:8392) is the wire-proof; the WT-turn needs infra-ops to add:8392(requested01KVHWJGTT…). -
[2026-06-19]heid-code-review pulled MORE weight than its own "marginal" self-assessment. The panel returned zero drift, but its single test-fidelity finding CASCADED into 2 real latent-bug fixes when applied (the memorycheck_same_threadbug + Regin's op-feed field-name bug). Lesson: a contract-fidelity nudge can transitively expose bugs the test never reached — don't dismiss a "marginal" finding by its count. -
[2026-06-20]The post-turn-async timing trap bit AGAIN — even a 35s post-[done]read missed the promotionupsert_manyby ~2s (it landed19:48:58; the read was ~19:48:56). A 15s-interval background poll caught it on the first tick. Same family as the affect.emit / async-promotion traps already logged — re-confirmed that "wait once then read" is fragile for post-turn writes; poll a window, don't snapshot once. (The affect.emit write, by contrast, DID land inside the 35s window — promotion is the slower of the two post-turn writes.) -
[2026-06-30]Heimdall keys are PER-INSTANCE — a key minted on one Worldtree 401s on another. Our Conversation-API key works on personal:8081but 401sauth_invalidon demo:8080(per-instance Heimdall user store + pepper; fresh deploys start with an EMPTY key store). Same as the admin key (personal-only). To live-drive a given instance you need a key minted FOR that instance (request via infra-ops). Couldn't live-prove the b2 409 on demo for this reason → deferred to personal-b2 where we have access. -
[2026-06-30]tea comment <N>hangs on Gitea (the whole compound bash auto-backgrounded + stuck on the openteacall). The #11 prereq comment hung; killed it + posted via the Gitea HTTP API directly (POST /api/v1/repos/vh/ratatoskr/issues/<N>/comments, token from~/.config/tea/config.yml). For issue comments, prefer the Gitea API overtea commentwhenteais flaky (CLAUDE.md already says use HTTP for comment-EDITS; this extends it to ADD when tea hangs). Verify-then-post (check the comment didn't already land) to avoid a double-post after a kill. -
[2026-07-02]Mask-HOSTED transient characters have a STATIC mood engine — cost a whole R29 probe. A first probe used aPOST /characterstransient character bound viaagent_id=mask+character_id; its PAD sat at baseline across 15 praise/contempt/dominance turns — the appraisal→PAD engine does NOT run on the mask-hosted transient-character path. The dynamics run only on BASE persona agents or a session bound to ratatoskr's affect provider. To probe mood dynamics, use a base persona agent, never a mask-hosted transient character. (mask AS a base agent —agent_id=mask, NOcharacter_id— DOES run the engine, neutral 0,0,0 baseline.) [auto-memoryreference-worldtree-affect-surface-map] -
[2026-07-03]The "neutral non-appraising tail" premise fails — the neutral MESSAGE choice dominates. The R30 φ0 method assumed neutral turns don't re-appraise, but factual-question neutrals ("capital of France?") trigger a new emotion nearly every turn (disappointment from the warmth-withdrawal let-down after a positive impulse) →emotions_activenever empties in 50 turns. A minimal "Please continue." triggers FAR fewer (emotions clear ~turn 16 with spacing). The personal dry-run caught this BEFORE ~280 demo turns were spent on it — the instrument catching a flaw in the measurement design before the compute burn. (Irrelevant to the joint fit — the push_t covariate handles re-appraisal — but load-bearing for the empty-tail read.) -
[2026-07-03]Two φ0-fit traps: fast-turn timescale + low-baseline conditioning. (1) At fast turn cadence the per-turn PAD decay (φ≈0.95/turn) reaches the anchor LONG before the ~200s wall-clock emotion fade → no signal in the (eventual) emotion-free tail; need wall-clock SPACING (~16s) so the fade lands while PAD still has signal. (2) A low-baseline agent's impulse in the constrained direction (forseti P0.239 negative) gives a tiny excursion → ill-conditioned regression (r²=0.46) that FALSELY tripped "config≠behavior" when its φ was averaged in. Weight/exclude by fit quality (r²) before aggregating — a signal-poor run isn't evidence against the config. -
[2026-07-06]persona_state+ the agent envelope are Tier-3-BLIND -- NOT valid signals for "did a persona store".GET /agents/{id}/persona_statereturns 404persona_not_configuredfor EVERY Tier-3 colon-id (hardcoded short-circuit,api.py:1266"regardless of row state"); theConsumerAgentResponseenvelope never echoes persona/motivational/memory (api.py:538). I mis-called "persona didn't store" from these two blind reads -- the 201-not-422 on define IS the store-success signal. To actually SEE a Tier-3 mood, read the emitted PAD off the Bifrost affect egress after a BOUND turn (Tier-3 persists nothing Worldtree-side per ADR-0009; no persona/mood READ endpoint). -
[2026-07-06]RawPOST /sessionsis NOT Bifrost-bound -> zero affect/memory emits. The web surface binds by setting thebifrostblock on session-create; a raw session doesn't -> 0 affect rows, which I nearly misread as "mood is neutral". Bind from the CLI with--new --bifrost-url http://10.100.10.50:8392(the combined provider). Gotchas:--bifrost-plane affect/memorymap to the SEPARATE:8390/:8391providers (endpoint_for_plane), which I'd PRUNED as stale duplicates ->bifrost.endpoint_unreachable; andcombinedis NOT a--bifrost-planechoice (CLI restricts to memory/affect) -> use--bifrost-urlfor :8392. -
[2026-07-06]A fast/"no-op" deploy can leave a STALE container running the old image -- verify the running version, not the deploy status. Personal's b22 deploy (run 8204) "completed" in ~1m (vs ~6m normal): a pull-only deploy racing ahead of the main build, leaving the container on the pre-#348 image. A clean bound mood read stayed neutral DESPITE the persona being declared and the fix being in the code (worldtree-dev proved the b22 derivation is correct). infra-ops force-swapped to the real b22 (run 8211, verifiedinfo.version 2.3.0on879cefe). Lesson: when engine-proven-correct code produces wrong runtime behavior, suspect the deploy -- check the actual running image version. -
[2026-07-06]#348 OCEAN key-mismatch: a declared OCEAN silently resolved to neutral. The define validator required single-letter{O,C,E,A,N}but the mood-derivation code read spelled-outopenness/.../neuroticismwith a 0.0 default and no remap -> every API-declared trait defaulted to 0.0 -> neutral setpoint/gain/decay. #343's tests bypassed the validator (spelled-out keys) so CI never caught it. Fixed in b21 (Personality.from_configaccepts both key forms). My reset+smoke diagnosis flushed it out -- the consumer/provider thesis paying off again. -
[2026-07-06]pkill -f "ratatoskr-web --host"SELF-MATCHES the bash command running it (its own command line contains that string) -> killed its own shell mid-restart (exit 144, restart aborted, :8765 left down). Kill the web by PID (ss -ltnp | grep :8765), neverpkill -fon a pattern your own command contains. Also: uvicorn hangs on SIGTERM with an admin-events SSE stream open -> needs SIGKILL. -
[2026-07-06]Playwright: no pythonplaywrightmodule in the venv; use NODE playwright + an explicitexecutablePath. Shared box browsers live at/opt/ms-playwrightbuild 1223;npm i playwright(latest) wants build 1228 -> "Executable doesn't exist" mismatch. Fix:chromium.launch({ executablePath: '/opt/ms-playwright/chromium-1223/chrome-linux64/chrome' })(+export PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright). A node script drives the SPA (pick agent -> open -> assert transcript). The Playwright DOM check is the only lens that catches SPA JS-render bugs — unit tests can't reach them. -
[2026-07-06]The Bash tool'sgrepis a ugrep-wrapper (--ignore-files -I) that silently returns NOTHING on some files (e.g.src/ratatoskr/web/static/index.html) — greps for<script//apicame back empty on a file that clearly contains them. Usepython3(regex overopen(f)),/usr/bin/rg, or the Read tool for those files; never trust an emptygrepresult on the SPA. -
[2026-07-06]DesignSync(claude.ai/design MCP) needs claude.ai design scopes before ANY method works — first call errorsneeds a claude.ai login ... Run /login, select "Claude account with subscription". It's an interactive auth only the operator can complete (/design-loginor/login); can't be done on their behalf.
18 older entries (2026-05-* — the original debug-TUI/web build era) archived to archival-memory.md.