DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path investigations/loomworks-companion-bin3-capability-findings-v0_1.md

Companion Bin-3 capability gaps — findings and engine-work queue

Version. v0.1 Date. 2026-06-25 Author. Claude.ai, grounded on Claude Code (CC) read-only engine traces. Status. Findings record. Confirms the four Bin-3 capability gaps from the Companion design, sorts them by what building each entails (engine / Companion / both), surfaces the structural insight that two of them share a root, and records the Operator's structure decision. Produces a grounded, cost-sorted build queue. No build authorized; each item becomes its own scoping note → CR, fresh. Grounds on. CC read-only traces (2026-06-25) of /operator/converse, the conversation/turn wiring, orchestration aggregates, and the voice frontend. The Companion design document (loomworks-companion-design-v0_1), which named these four as Bin 3 ("genuinely missing engine capability"). The engagement-identity decision record (addressing context).


Plain-language summary

The Companion design named four capabilities the ideal Companion needs that the engine doesn't obviously provide. This record confirms each against the live engine and — more usefully — establishes what building each one actually costs. The result reshapes "four things to build" into a sorted queue:

The one real structural decision — where the Companion's "here" lives — is settled: a current_engagement_id field on host_account. Everything else follows from cost and dependency.


1. The four gaps, confirmed and costed

Gap 1 — Conversational context / current-engagement (the spine)

Confirmed. No engine notion of "current engagement." Addressing resolves on display_identifier client-side (EngagementList.tsx:137address = row.display_identifier ?? project_id).

What converse persists between turns: only a flat conversation_turns log. There is no conversation_id, no session_id, no session/context table. Each turn row carries (person_id, engagement_id?, role, content, classified_intent, …); record_turn writes one row per turn, get_recent_turns reads history keyed by (person_id, engagement_id). So a "conversation" is implicit — the set of turns for a person+engagement pair — not a stored entity. Converse is stateless per call plus a project_id (engagement id) supplied by the caller every turn; the only cross-turn memory is the turn log read back as history.

Durable "here": there is no conversation/session record 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, the per-person host state), or a dedicated session/context table.

Verdict: CONFIRMED-GAP · both (engine persists current-engagement; Companion sets/reads it) · needs new structure — minimal form is a new current_engagement_id field on host_account.

Gap 2 — Composite engagement-state read

Confirmed. No single per-engagement four-room composite exists. /operator/dashboard is a cross-engagement rollup (active / needs-you / recent across all the person's engagements), not per-engagement. /operator/projects/{id}/story is a stub — returns "Project narratives are coming soon"; not built, and narrative-shaped, not state-shaped. memory_status is partial — Memory↔Manifestation freshness counts plus downstream impact for one engagement, but not the full held / manifested / shaped / rendered / needs-me.

Assemblable today from existing per-room list endpoints — assertions (held/committed), manifestations, shape-events, renders, considerations, /me/dashboard/needs_you — via client/orchestration fan-out, no new engine route strictly required. A clean one-call "where is Goosey" would want a new engine aggregate endpoint for efficiency and a stable contract, but does not require one.

Verdict: CONFIRMED-GAP · both (Companion-side fan-out over existing reads works now; optimal is a new engine aggregate) · needs new structure only if done engine-side (a new composite endpoint); otherwise orchestration over existing routes.

Gap 3 — Voice output (text-to-speech)

Confirmed input-only. Voice input is browser SpeechRecognition (useVoiceListening.ts + engagement-navigation). Voice output: none — no speechSynthesis / SpeechSynthesisUtterance / .speak( anywhere in the frontend, and no spoken-form / TTS / audio-out of a response anywhere 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 companion_message; no engine involvement (no spoken-form endpoint, no server-side audio synthesis).

Verdict: CONFIRMED-GAP · Companion work only · extends existing — speak the existing companion_message via browser SpeechSynthesis; no new structure, no engine change.

Gap 4 — Reference resolution ("that one" / "the second" / "it")

Confirmed absent. No anaphora / ordinal / referent resolution in converse today. The classifier extracts explicit references only — commit_assertion pulls held_reference as the literal number(s) the Operator named ("commit held 3" → "3"). The single cross-turn carry is the commit-confirmation turn (carry held_reference forward on "yes") — an explicit number, not a resolved anaphor/ordinal. There is no structured "things just shown" referent set with object ids maintained between turns. (Converse does pass recent_turns history into the LLM prompt, so the model may loosely resolve some anaphora, but nothing deterministic maps "the second one" → a specific id.)

Seam — both, by path:

Verdict: CONFIRMED-GAP · both (Companion-side substitution for surface-rendered references; engine-side for pure-conversational/voice) · needs new structure engine-side (a per-conversation referent set, dependent on the same missing session structure as gap 1); Companion-side extends existing rendering state.


2. The structural insight — gaps 1 and 4 share a root

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 whole queue.

Gaps 2 and 3 are independent of this root: gap 2 is assemblable now (a new endpoint is an optimization), and gap 3 is frontend-only and engine-free.


3. The structure decision (Operator, settled)

The Companion's "here" lives as a current_engagement_id field on host_account — single focused engagement per person.

When gap 4's engine-side is built, its per-conversation referent set hangs off this same per-person context (extending the field's home, or promoted alongside it if the table is introduced by then).


4. The build queue (cost- and dependency-sorted)

| 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 it | set/read the focus; resolve "open X" → set focus | nothing | small engine change + Companion wiring | | C | 4 — Reference resolution | per-conversation referent set (extends B's structure) | substitute "the second" from rendered lists (can precede C) | engine-side depends on B | Companion-side cheap now; engine-side rides on B | | D | 2 — Composite read | optional new aggregate endpoint | fan-out over existing per-room reads works now | nothing (optimization) | do when fan-out gets slow or a stable contract is wanted |

Reading the order:


5. Next step

Each queue item becomes its own scoping note → CR, drafted fresh per session discipline (scoping a build is a distinct activity from this design/investigation thread).

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.


What this record does not do


DUNIN7 — Done In Seven LLC — Miami, Florida Companion Bin-3 capability gaps — findings and engine-work queue — v0.1 — 2026-06-25