Version. 0.2
Date. 2026-05-29
Status. Scoping note, decisions resolved. Orientation-first. Frames the source-identification work (Memory-completion item C), states what is settled, and records the Operator's resolution of the five open decisions (D1–D5, §8). The next artifact is the Change Request. Construction detail (build steps, exact migrations) is deferred to the CR.
Provenance. Claude.ai session 2026-05-29, grounded in: the roles design documents (person-layer discovery, Phase 14 CR), live substrate code read by Claude Code (ActorRef, conversation_turns write path, append_event), and loomworks-foray-integration-grounding-investigation-v0_1 (the live dunin7-foray SDK contract). Operator: Marvin Percival.
Companion to. loomworks-foray-integration-grounding-investigation-v0_1 (the FORAY facts this note references rather than re-derives); the architecture specification v0.4; the Loomworks candidate seed v0.9; queued-directions §15.3.
Format note. Markdown-primary; HTML companion pending (no Markdown-to-HTML render specialist yet — queued-directions §3.3).
What this document does. It scopes source identification — making Loomworks Memory reliably record who or what asserted each piece of knowledge, and under what authority — as the first piece of the Memory-completion arc. It frames the problem against the real current code, names the one structural decision already made (unify provenance onto the existing ActorRef model), and surfaces the decisions still open for the Operator.
Why it matters. Memory is the foundation; everything derived from it (Manifestation, Shaping, Rendering, and any downstream AI or external use) inherits whatever Memory records. Today Memory records source two different ways — richly for assertions, crudely for conversation — and the surface discards what the substrate knows. If Memory cannot give one honest answer to "who asserted this," every derived artifact carries that ambiguity. This work closes that gap and lays the data foundation for FORAY attestation.
What changed in understanding since the work was named. Reading the live code (not summaries) established that the unification is smaller and safer than expected: the conversation-turn provenance path is one table, one writer, two call sites, and references no actor model today — so the change is additive (introduce an actor representation where none exists), not a refactor of competing logic.
What the Operator decided. All five decisions (D1–D5, §8) are resolved on the defaults: honest name+designation labels; first pass covers conversation_turns only (seed-conversation surface deferred); domain_expert gets a recognized-expertise marker but no workflow; FORAY-completeness at option (a) (data complete + seam-marked, no SDK dependency); the conversation path adopts the companion ActorRef kind. In and out of scope are stated. No construction is committed by this note — the Change Request is next.
Every piece of knowledge in Loomworks came from someone or something. Memory should always be able to answer: who put this here, and under what authority. Four source-kinds matter: Operator (the principal, governance authority), Contributor (invited participant), Domain Expert (recognized expertise), and AI (the Companion, or an automated agent).
Memory records source in two different places, two different ways:
memory_events) record source richly, through ActorRef: actor id, actor kind, instruction version, plus a full PROV provenance chain. This is the honest model.conversation_turns) record source crudely: a person_id plus a role that is only ever "operator" or "companion". No actor kind, no agent identity, no provenance chain.Three consequences, all confirmed in live code:
person_id and distinguished only by role="companion". The substrate's ActorRef already has a companion kind built for exactly this (declared Phase 41) — the conversation path simply never adopted it.CenterPane, ChatView) build the author label only from role. A Contributor's turn renders under the signed-in person's name with the "(Operator)" suffix. There is no code path by which a Contributor-vs-Operator distinction reaches the label — the architecture spec §13 names this exactly.domain_expert is built but inert. It is a valid designation, placeable as a membership row, but no code derives authority or behavior from it.These are established by the roles design documents and confirmed in live code; the work builds on them, it does not revisit them.
operator, contributor, domain_expert) are per-membership rows — stackable, independent, extensible by insert.ActorRef is the rich actor model and it exists. Four kinds (contributor, agent, person, companion), id, instruction_version, display_name, and the Phase 45 delegation fields (capability_ref, approval_mode). The memory_events path already writes through it.ActorRef
The Operator chose, after weighing the alternative, to unify the conversation-turn provenance scheme onto ActorRef rather than apply a surface-only label fix. The reasoning, preserved per discovery-record discipline:
ActorRef. Conversation turns are Memory-population events; they should record source the same honest way assertions do. The cost is a substrate change to the conversation-turn path; the benefit is one source model across all of Memory. The live-code reading lowered the cost estimate materially (§4).The conversation-turn provenance path is small, self-contained, and references no actor model today — so unification is additive, not a refactor:
conversation_turns, model ConverseTurnRow), one writer (record_turn), two call sites (operator turn and companion turn, both in converse.py), plus the read-side view (ConverseTurn), _row_to_turn, and the reader (get_recent_turns).person_id + role + content fields. It imports no ActorRef, has no actor parameter, has no actor column. There is no competing actor-resolution logic to unwind — there is an absence to fill.append_event already uses: it denormalizes actor_id, actor_kind, actor_instruction_version onto columns, with the richer fields (display_name, capability_ref, approval_mode) living in the provenance JSONB.
This means the change is bounded: introduce an actor representation on the conversation-turn write path (so a companion turn is recorded as the companion kind with its own identity, an Operator turn as person, a Contributor turn as person/contributor with the contributing membership's designation available), and carry it through to the read view and the surface label. The risk is the ordinary risk of touching a Memory write path (treat it as such — stepped, tested against production-session behavior), not the larger risk of unwinding entangled logic.
This work makes the source record FORAY-complete and seam-marked, per the grounding note's option (a). It does not integrate FORAY, take a dependency on dunin7-foray, or construct FORAY JSON. The principle: Loomworks is the system of record; the data a future FORAY reflection needs must be present and durable at every source-and-commit point, derivable at any time including retroactively.
Against the real dunin7-foray SDK contract (grounding note §3), "complete" means modest: the SDK's actor surface is a single triggeredBy string and an opaque payload. So Loomworks must hold, natively and durably, enough to derive at emit time: (a) a canonical actor string, and (b) a structured actor object for the payload. The ActorRef already carries most of this; the gap to check during the CR is per-field — for each thing a future attestation would want (actor identity and kind; the authority/mandate, including model version and system-prompt hash for AI; the act — contribute vs commit; the subject — which assertion; the timestamp), name where the Loomworks-native datum lives or flag it as a capture gap.
Two structural facts from the grounding note shape this:
analysis/oracle; Domain Expert recognition ≈ certification; Operator commit ≈ verification/approval. This is why AI-cannot-commit is load-bearing: the AI can be the attestor of a contribution, never of the commit.
The reserved-location seam already exists on the memory_events path (_foray_reserved_emit). The conversation-turn path, once unified, should carry the equivalent seam so both Memory-population paths are seam-marked.
| Source-kind | Substrate today | What this work does |
|---|---|---|
| Operator | Built; operator designation gates authority; written as person ActorRef kind on the assertion path. | Carry through to conversation-turn provenance and surface label honestly. |
| Contributor | Built; contributor designation; distinguished from Operator at the membership level. | Close the surface gap — a Contributor's turn must not render as the Operator. This is the heart of the spec §13 fix. |
| Domain Expert | Designation exists; inert (no code derives meaning). | Give it a provenance meaning at minimum (a recognized-expertise marker on the source record). The full recognition flow is out of scope (§7). |
| AI | Two ActorRef kinds — agent (bearer-token agents) and companion (the conversational AI). companion is declared but unused on the conversation path. | Adopt companion on the conversation-turn path so AI turns carry distinct identity, not the Operator's. Respect and surface AI-cannot-commit. |
In scope (this work):
conversation_turns write path onto ActorRef (additive actor representation).CenterPane, ChatView), so the four source-kinds render honestly.domain_expert a provenance meaning (a source-record marker).Out of scope (named, deferred):
dunin7-foray dependency, no emit/attest wiring, no JSON construction. That is a later, separately-scoped phase.seed_conversation.py records turns onto the candidate engagement's event log, a separate mechanism from conversation_turns. Whether unification extends to it is Decision D2 below; the note's default is to leave it out of first pass.All five resolved on the defaults (Operator, 2026-05-29). Each records the resolution and the reasoning, so the CR proceeds on settled answers and the trajectory is preserved.
D1 — Surface labeling depth. RESOLVED: honest name + designation. When a Contributor's turn renders, show the contributor's name and designation (e.g. "Jane (Contributor)" / "Jane (Domain Expert)"); AI turns marked as the Companion. Minimal, honest, matches "only show what is available."
D2 — Does unification cover the seed-conversation turn surface? RESOLVED: no. First pass covers conversation_turns only. seed_conversation.py (the candidate-engagement event-log turn path, _append_operator_turn/_append_companion_turn) is a separate mechanism; bringing it into the unified model is a later, separate step. (The Operator was explicitly offered the wider scope — both turn surfaces at once — and chose the tighter first pass.)
D3 — What does domain_expert mean in the source record (first pass)? RESOLVED: recognized-expertise marker, no workflow. A marker carried on the source record / surface label when the contributing membership holds the domain_expert designation — no authority change, no new workflow. The recognition flow stays deferred.
D4 — FORAY-completeness depth. RESOLVED: option (a). Data complete and seam-marked, no dunin7-foray dependency, verified per-field against the grounding note's SDK contract. (Option (b), a thin mapping stub, was set aside to avoid touching dunin7-foray before the integration phase.)
D5 — AI source-kind granularity. RESOLVED: conversation path uses companion. The conversational AI is recorded as the companion ActorRef kind; agent remains the bearer-token path on the assertion side. Surface labels show both as the Companion to the Operator (plain-terms), with the kind distinction preserved in the substrate.
Source identification plus FORAY-completeness gives accountability and tamper-evidence — who asserted what, under what authority, unaltered, with a chain through to commit. It does not give correctness (that a claim is true) or competence (that the source was qualified). This boundary should be stated wherever the foundation's value is described, so it is not overclaimed.
The decisions are resolved; the next artifact is the source-identification Change Request: the construction decisions, the migration shape for the conversation-turn actor representation (matching append_event's pattern — three denormalized actor columns plus provenance JSONB, not a column per ActorRef field), the surface-label wiring, the per-field FORAY-completeness checklist against the SDK contract, the reserved-location seam on the conversation path (mirroring _foray_reserved_emit on memory_events), the held-then-commit chain representation, and the stepped build plan with the conversation-turn write path treated as a Memory write path (production-session-mirroring tests, not just green suite).
v0.1 (2026-05-29). Initial scoping note for source identification (Memory-completion item C). Frames the problem against live code, records the unify-onto-ActorRef decision and the set-aside surface-fix alternative, states in/out of scope, and surfaces D1–D5 for the Operator. Grounded in the roles design documents, live substrate reads, and the FORAY integration grounding note.
v0.2 (2026-05-29). Records the Operator's resolution of D1–D5 (all on the defaults; the wider D2 scope was explicitly offered and the tighter first pass chosen). Updates the summary, §8, and the next-step section accordingly. Adds two CR-level construction pointers confirmed by live-code reading (the partial-denormalization pattern; the _foray_reserved_emit seam precedent). No change to problem framing, the unification decision, or scope boundaries.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks Source Identification — Scoping Note — v0.2 — 2026-05-29