Version. v0.1 Date. 2026-06-25 Author. Claude.ai, under Operator direction, grounded on Claude Code (CC) read-only engine traces. Status. Decision record. Captures what engagement identity is, what the engine actually implements, the design intent it diverged from, the misframings caught and corrected along the way, and the Operator's decisions. Settled where marked settled; deferred items named explicitly. Grounds the Companion's engagement-addressing design. Grounds on. CC read-only traces of the engagement model, ID generator (migration 0065), membership/contributor/invitation/claim wiring, and live dev data (2026-06-25). The 2026-05-17 addressing design (chat "Architectural rethink operator surface design"). The 2026-06-09 Companion architecture foundation (Scope axis built on the engagement identity scheme).
An engagement has more than one identifier, and each does a different job. The confusion that prompted this record came from treating one of them — the human-friendly E#### — as if it carried identity everywhere, when it does not. The settled position:
E#### is a local label. Friendly, human-readable, but only unique within one Loomworks instance. Shared by all members of an engagement in that instance; in a future where engagements are shared across instances, each instance assigns its own E#### to the same UUID.Today the engine is in-instance only (no federation), so the cross-instance ambiguity people worry about cannot occur yet. The decisions here govern both today's reality and the federated future when it is built. The "running out of E numbers" worry is a non-issue — the scheme is backed by a 64-bit counter (effectively inexhaustible); the only forward caveat is a frontend one (don't assume the ID is exactly four digits).
"Engagement identity" is not one thing. It is four identifiers serving four distinct needs. Collapsing them is what caused the confusion.
| Job | Who uses it | Handle | Scope |
|---|---|---|---|
| Speak / recall | the Operator, by voice | the name / title ("Goosey") | local, human |
| Personal terse handle | the Operator, tersely | operator_sequence_number ("23"; Personal = "0") | per-person, within one instance |
| Cross-person, in-instance | all members of an engagement | E#### (display_identifier) | instance-local, shared by all members |
| Cross-boundary identity | machines / sharing | the UUID (primary key) | global, unique by construction |
Every job has a handle that exists today. Nothing is missing. The problem was never a missing identifier — it was treating the instance-local E#### as the cross-boundary handle when the UUID is.
Grounded, not assumed:
uuid4(), globally unique by construction, app-generated, exposed externally (it is the {engagement_id} in every API route and the project_id in converse/dashboard). One exception: the Administrative engagement uses a fixed UUID 00000000-…-0001.display_identifier (E####) — 'E' || lpad(nextval(seq), 4, '0'), a single instance-wide Postgres sequence starting at 1, computed by the database on insert. UNIQUE, NOT NULL, text. Shared by every member of an engagement; interleaved across all people and engagements in the instance (E0001 Administrative, E0005 Loomworks commons, E0007 Goosey, etc.).operator_sequence_number — bigint, per-person, UNIQUE(person, …). 0 reserved for that person's Personal engagement, then 1, 2, 3… in creation order, gap-tolerant (archived memberships don't renumber survivors). Shown in the engagement list next to the shared E#### (the frontend references operator_sequence_number === 23).external_id, global_id, universal_id, instance_id, namespace, foray_*, or attestation on engagements. The only abandoned external-reference field in the whole schema was memory_events.foray_tx_ref (a FORAY transaction reference, added, never written, dropped in migration 0072) — not an engagement identity.config.py has only a loomworks_env mode flag. DestinationInstance (credit/jurisdiction routing) is a (label, url) routing target for account onboarding — it never carries engagement identity across instances.The question that had to be settled: when someone other than the owner participates in an engagement, are they a member inside the same instance, or an Operator of a separate instance with the engagement shared across them?
Verdict: Model A — in-instance. Federation does not exist.
person → principals, engagement → engagements, both local FKs, UNIQUE(person, engagement)). Many distinct persons can co-inhabit one engagement row in one database (E0005 commons has 38).contributors table, kind ∈ {human, agent}, commit_authority requires human — the R-B20 gate). Both are in-instance.DestinationInstance routes accounts at signup (jurisdiction), never engagements. Claiming a grant creates a person in the claim-serving instance, not a separate environment.
Consequence: within an instance, E#### is unambiguous — every member of an engagement sees the same one. The cross-instance E#### collision (two instances both minting E0007) cannot occur today, because no engagement crosses an instance boundary. It is purely a federation-future concern.
4.1 The UUID is the engagement's identity. It is globally unique by construction and already exposed. Cross-boundary reference uses the UUID. Sharing an engagement is "a message with the ID (UUID) attached" — machine-mediated, not human-typed. No human-readable cross-instance ID is required, so no instance-identifier and no namespaced E#### need to be built.
4.2 E#### is a local label, assigned per instance. Within one instance it is shared by all members (engine, today). In the federation future, when an engagement is shared across instances, the receiving instance assigns its own local E#### to the same UUID — one UUID, many E####s. The E#### never needs to be globally unique, because two parties holding different E####s for the same UUID is expected and fine.
4.3 The UUID is preserved on share, never regenerated. This is the load-bearing constraint of 4.2: the receiving instance stores the same UUID (wrapped in a fresh local E####). If a new UUID were minted, the two sides would have two identities for one engagement and the scheme breaks. The UUID is the invariant; the E#### is the variable.
4.4 Humans never reference E#### across instances. Within an instance, address by name / operator_sequence_number / E####. Across instances, always the machine path (message + UUID / link). Each instance shows its own local E####; cross-instance, the human acts on the shared thing, not on a number.
4.5 The E#### scheme stays as-is. Four-digit minimum padding, instance-wide sequence. Not changed. (See §6 — capacity is a non-issue; the only forward caveat is a frontend one.)
The path here included several positions that were wrong and were corrected against engine fact. Recorded so the reasoning is reconstructable:
E#### is the universal handle." Treated throughout early discussion as if E#### carried identity across boundaries. Corrected: E#### is a per-instance sequence (restarts per database); it is the UUID that is globally unique. The universal handle existed all along — it was just the unsayable UUID, not the friendly E####.M0001 for a personal/memory space, with the first 999 IDs reserved. Raised from Operator recollection. Corrected by CC: there is no M prefix and no reserved 0001–0999 band. One prefix (E), sequence starts at 1, E0001 is the Administrative engagement (oldest, not reserved-numbered). Personal is an ordinary E#### engagement row (visibility='personal'), not a separate object class.operator_sequence_number — with Personal correctly at 0, exactly as the design specified. Two of the design's three layers are in fact implemented (global E####/UUID, and the per-Operator number).operator_sequence_number === 23 directly. It is a live handle, not an illustration.0/Personal, not consuming the sequence. The engine gives Personal an ordinary E#### (consuming the sequence) and sets its operator_sequence_number to 0. So the 0 survives (as the personal number), but the "doesn't consume the global sequence" and "dual-addressed by name" parts are not implemented as designed. Noted as a divergence, not resolved here.E#### is 'E' || lpad(nextval(seq), 4, '0'). The lpad pads to a minimum of four digits; it does not cap. Past 9999 it becomes E10000, E10001, … — a format convention, not a limit.bigint (≈ 9.2 × 10¹⁸). At a thousand engagements per second it would take ~290 million years to exhaust. Not a real-world limit.display_identifier column is text — a longer string costs nothing structural.
The one forward caveat is a frontend one, not an engine one: surfaces that show the ID must not hard-code a four-character E#### width or assume exactly four digits — the ID can grow to E10000+. This is a render-robustness note for the engagement-list and any ID-displaying surface. No engine change. (Operator decision: leave the scheme alone; it only matters if it messes up an interface render, and the mitigation is the frontend caveat above.)
Two known properties of the E#### label (recorded, not problems to fix):
operator_sequence_number, not E####.)E#### values externally. Mitigated by decision 4.4 — E#### is not shared externally; cross-boundary sharing is via UUID/link.
The Companion addresses engagements instance-locally: by name ("open Goosey"), by operator_sequence_number ("open 23"), and/or by E#### ("open E0007") — accepting whichever the Operator reaches for, since the engine carries all three. The UUID is the invisible cross-boundary identity, never spoken, carried inside any share-message. The canonical displayed handle stays E#### (shared, unambiguous within the instance), per the engine.
This settles engagement addressing, which was the open question blocking the Companion home-surface design. The home can now be designed: engagements addressed and labeled locally, Personal as an engagement with visibility='personal' (it is an engagement — same machinery, distinguished by visibility), the UUID underneath as identity.
These are real and belong to the federation future or to adjacent strategic threads. None blocks current work.
E#### to an engagement whose UUID originated in another instance." Does not exist today (Model A only). This is the build that decision 4.2 implies, when federation is taken up.contributors table is an in-instance bearer-token participant (kind ∈ {human, agent}). The Operator's "Contributor" in the federation sense (a party with their own instance) is a different, future role. Keep these distinct; do not overload the word.memory_events.foray_tx_ref shows the codebase reached toward FORAY anchoring once. Strategic, parked — not required by the machine-mediated-sharing model (4.1).Personal); tags-as-aliases and workspaces as grouping surfaces. These were settled in design but are not (all) in the engine; reconciling them is separate from this identity record.DUNIN7 — Done In Seven LLC — Miami, Florida Engagement identity — record of decision — v0.1 — 2026-06-25