DUNIN7 · Loomworks · Findings
Plain-language summary
The Companion design named four capabilities the ideal Companion needs that the engine doesn't obviously provide. This record confirms each and — more usefully — establishes what building each one costs, reshaping "four things to build" into a sorted queue:
host_account.current_engagement_id), not a subsystem. Settled (field, not table).Confirmed. No engine notion of "current engagement." Addressing resolves on display_identifier client-side (EngagementList.tsx:137 — address = row.display_identifier ?? project_id).
What converse persists between turns: only a flat conversation_turns log. No conversation_id, no session_id, no session/context table. Each turn row carries (person_id, engagement_id?, role, content, classified_intent, …). A "conversation" is implicit — the set of turns for a person+engagement pair — not a stored entity. Converse is stateless per call plus a caller-supplied project_id every turn; the only cross-turn memory is the turn log read back as history.
Durable "here": no conversation/session record exists to extend. The natural home is a new structure — minimally a per-person focused-engagement field on the existing host_account (which already holds personal_engagement_id + companion_name), or a dedicated session/context table.
Verdict: CONFIRMED-GAP · both (engine persists; Companion sets/reads) · needs new structure — minimal form is current_engagement_id on host_account.
Confirmed. No single per-engagement four-room composite exists. /operator/dashboard is a cross-engagement rollup, not per-engagement. /operator/projects/{id}/story is a stub ("coming soon") — not built, and narrative-shaped, not state-shaped. memory_status is partial (Memory↔Manifestation freshness only).
Assemblable today from existing per-room list endpoints (assertions, manifestations, shape-events, renders, considerations, /me/dashboard/needs_you) via fan-out — no new engine route strictly required. A clean one-call "where is Goosey" would want a new aggregate endpoint for efficiency and a stable contract, but does not require one.
Verdict: CONFIRMED-GAP · both (fan-out works now; optimal is a new aggregate) · needs new structure only if done engine-side; otherwise orchestration over existing routes.
Confirmed input-only. Voice input is browser SpeechRecognition (useVoiceListening.ts). Voice output: none — no speechSynthesis / .speak( in the frontend, no TTS / audio-out in the engine.
Where it would live: pure frontend. The engine already returns companion_message text — that is the spoken form. TTS is the browser SpeechSynthesis API reading it; no engine involvement.
Verdict: CONFIRMED-GAP · Companion work only · extends existing — speak companion_message via browser SpeechSynthesis; no new structure, no engine change.
Confirmed absent. No anaphora/ordinal/referent resolution in converse. The classifier extracts explicit references only (commit_assertion pulls held_reference as the literal number). The one cross-turn carry is the commit-confirmation turn (an explicit number, not a resolved anaphor). No structured "things just shown" referent set with object ids between turns. (Converse passes recent_turns into the prompt, so the model may loosely resolve anaphora, but nothing deterministic maps "the second one" → an id.)
Seam — both, by path: Companion-side is the natural near-term home (the frontend owns the rendered ordered lists, so it can map "the second" → a concrete handle before the converse call). Engine-side resolution (the pure-voice path, no list rendered) needs a referent set persisted per conversation — which ties straight back to gap 1.
Verdict: CONFIRMED-GAP · both (Companion-side substitution; engine-side for pure-voice) · needs new structure engine-side (a per-conversation referent set, dependent on gap 1's structure); Companion-side extends existing rendering state.
There is no per-conversation server-side state object. A "conversation" today is implicit — just the set of turns for a person+engagement pair in conversation_turns. So the Companion has nowhere durable to hold anything between turns except the flat turn log.
Both the spine (current-engagement, gap 1) and the engine-side of reference resolution (the referent set, gap 4) need exactly this missing place. Build gap 1's structure and gap 4's engine-side extends it; skip it and both stay blocked. This is the single load-bearing structural decision in the queue. Gaps 2 and 3 are independent of this root.
Settled — field on host_account, not a session table
The Companion's "here" lives as a current_engagement_id field on host_account — single focused engagement per person.
host_account already holds the per-person host state (personal_engagement_id, companion_name), so this extends an existing table rather than introducing a subsystem.When gap 4's engine-side is built, its per-conversation referent set hangs off this same per-person context.
| Order | Gap | Engine work | Companion work | Depends on | Cost |
|---|---|---|---|---|---|
| A | 3 — TTS | none | speak companion_message via browser SpeechSynthesis | nothing | nearly free, frontend-only |
| B | 1 — Current-engagement (spine) | add current_engagement_id to host_account; converse reads/writes | set/read focus; "open X" → set focus | nothing | small engine change + wiring |
| C | 4 — Reference resolution | per-conversation referent set (extends B) | substitute "the second" from rendered lists (can precede) | engine-side: B | Companion-side cheap now; engine-side rides on B |
| D | 2 — Composite read | optional new aggregate endpoint | fan-out over existing reads works now | nothing (optimization) | do when fan-out gets slow |
Reading the order: A (TTS) ships independently anytime — depends on nothing, gives the Companion a voice (the missing half of voice-first). B (spine) is the keystone — small (current_engagement_id) but unblocks voice-direct and is the structure C extends. C — the Companion-side can come early; the engine-side waits for B. D is an optimization, not a blocker — fan-out works today.
Each queue item becomes its own scoping note → CR, drafted fresh per session discipline. The natural first scoping note is B — the current-engagement spine, now fully grounded: a current_engagement_id field on host_account, converse reading/writing it, the Companion setting it on "open X" and reading it for "here". A — TTS is an independent, near-free frontend piece that can be scoped in parallel whenever wanted.