diff --git a/docs/contracts/web_debug_surface.contract.md b/docs/contracts/web_debug_surface.contract.md index 0c449ba..d212c7e 100644 --- a/docs/contracts/web_debug_surface.contract.md +++ b/docs/contracts/web_debug_surface.contract.md @@ -125,11 +125,11 @@ functions: flexibility: "open" - name: "renderConsole + trend (v0.20.0 — unified persona/affect console; supersedes renderAffectPane/renderPersonaPane/setPersonaStrip)" - signature: "renderConsole(snap) ; setFader(axis,v) ; setFaderTrend(axis) ; renderOrbit() ; renderDominant(snap) ; renderDerived(snap) ; renderRelations(snap) ; renderDirective(snap) ; pushAffectHistory(snap) ; sparkline(vals) ; trendDelta(vals)" + signature: "renderConsole(snap) ; setFader(axis,v) ; setFaderTrend(axis) ; renderOrbit() ; renderDominant(snap) ; renderDerived(snap) ; renderRelations(snap) ; renderDirective(snap) ; pushAffectHistory(snap) ; sparkPointsH(vals,w,h,endX) ; stripPoints(vals) ; proj3(p,a,d) ; trendDelta(vals)" description: "ONE render path for BOTH the Tier-1 persona_state snapshot and the Tier-3 affect snapshot (renderConsole), feeding the right affect console: dominant/canonical-mood centerpiece, bipolar PAD faders (each with a turn-to-turn Δ + sparkline), a P×A mood orbit from PAD history, an affect-derived grid, relations metric rows, and the canonical directive. Replaces the v0.19.x split of renderPersonaPane (Tier-1 pane) + renderAffectPane (Tier-3 pane) + setPersonaStrip (top-bar strip, removed — PAD now lives in the console faders)." postconditions: - "POST-001: reads snap.relations (relation_edge/1: target_entity + trust_ability/benevolence/integrity + warmth as {value,confidence,evidence_count} + agency + relation_context) — the CURRENT Worldtree emit shape; falls back to the legacy flat snap.valence for an older emitter. Tier-1 fields (baseline_pad, mood_drift, dominant_emotion, emotions_active) render WHEN PRESENT, '—' when absent (Tier-3 lacks them)." - - "POST-002: each PAD fader + relation metric shows current value + Δ-vs-previous (▲/▼) + a unicode sparkline auto-scaled to its OWN observed range (flat ▄/— when sub-0.01 stable — no noise amplification), drawn from AFFECT_HIST (rolling, HIST_CAP=24, session-lived). setFaderTrend fills the per-meter Δ+spark slots; renderOrbit plots the last N (P,A) samples as a scaled trail with a pulsing current marker." + - "POST-002: SVG sparklines (v0.20.4, adapted from the design prototype; replace the unicode strings). Each PAD fader shows current value + Δ-vs-previous (▲/▼) + a VERTICAL SVG strip (`stripPoints`, 26×132, time down Y newest-at-bottom, value on X ±11, gradient-faded via stripFade{P,A,D}, dot at newest). Each relation metric shows a HORIZONTAL SVG sparkline (`sparkPointsH`, 56×13, auto-scaled to its OWN range, sparkFade gradient + end dot). renderOrbit is now a 3D ISOMETRIC P×A×D cube (`proj3`: P right-down / A left-down / D up, 2:1 iso, center 62,66, scale 26) plotting the trajectory + a pulsing now-marker + a drop line to the D=−1 floor + a floor shadow ellipse (depth cue). All drawn from AFFECT_HIST (rolling, HIST_CAP=24, session-lived); sparkline coords are computed numerics (no upstream strings → INV-004 trivially held). Gradients live in one hidden `` svg in the console." - "POST-003: pushAffectHistory dedupes by emitted_at||last_updated_at so the ~4x/turn post-turn PAD poll contributes ONE sample/turn; history is CLIENT-side only (lost on reload — durable cross-session history via a provider-side snapshot log is a deferred follow-up, NOT built here)." - "POST-004: INV-001 honesty — no fabricated Tier-1 fields. The dominant-emotion centerpiece shows a real OCC dominant_emotion (Tier-1) OR the CANONICAL mood word from canonMood(pad) (Tier-3, dimmed) OR '—'; NEVER a synthesized emotion. The affect-derived grid drops non-emitted metrics (intensity/decay-τ) and shows only real/client-derived cells (baseline/drift real for Tier-1, client-derived samples/volatility). INV-004 — every dynamic value passes through esc(); numerics go through toFixed, never innerHTML-raw." flexibility: "open" diff --git a/persistent-memory.md b/persistent-memory.md index 2f818d8..cc72ca0 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -168,7 +168,8 @@ decision. Captures rationale that won't be obvious from code alone. - `[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}/messages` route (GET /messages was originally deferred out-of-scope; sessions used to start empty so it never mattered) and `startSession()` 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 route `GET /api/sessions/{id}/messages` → `get_session_messages` (mirrors the tools/bifrost proxies; status-preserving `session_messages_unavailable` envelope); (2) SPA `loadTranscript(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 in `startSession` after the workspace opens; best-effort (swallows failures). Contract `web_debug_surface.contract.md` amended (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, never `pkill -f` on 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 brief `docs/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 project `bc0b65d1-a33e-422a-8bc1-3635c9112775` (file `Ratatoskr Console.dc.html`). Import mechanism = the `DesignSync` MCP; 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 shipped `docs/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 #204 `affect_update` SSE is SUPPRESSED for Tier-3, so its richer `emotions_active` is 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 via `canonMood`, relationship directive via `canonDirective`) 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 a `tolerate_drift` pin is SURFACED to Vuong (worldtree-dev will co-sign + honor a ping-on-change handshake, same as the d2-canon). **DRIFT-WATCH — EXACT SPEC IN HAND, HELD until deploy (worldtree-dev pinged 2026-07-07; b24 `f63dea7`, deploys next push).** Worldtree's `render_d2_canonical` now conditionally DROPS the trailing "; avoid premature we-framing" clause **IFF ALL 3 AND-gates**: (1) `unsafe_capable` = effective mood_tier=="full" (= min(persona.mood_tier, deployment_cap, heimdall_ceiling); **NOT on the wire** — thread from agent config; my heuristic: `full` for character-bound colon-id agents e.g. `ratatoskr:sindra`, `safe` for agent-scoped; likely a per-agent console override); (2) `warmth.value >= 0.8` ("deep"; on the wire `relations[].warmth`); (3) `relation_context == "expressive"` (top of the Hwang tie ladder stranger