DUNIN7 · Loomworks · Decision Record
Plain-language summary
An engagement has more than one identifier, each doing a different job. The confusion that prompted this record came from treating the human-friendly E#### as if it carried identity everywhere — it does not. The settled position:
E#### is a local label — friendly, but only unique within one instance. Shared by all members in that instance; in a federated future, 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 "running out of E numbers" worry is a non-issue — a 64-bit counter backs it. 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 needs. Collapsing them caused the confusion.
| Job | Who uses it | Handle | Scope |
|---|---|---|---|
| Speak / recall | Operator, by voice | the name ("Goosey") | local, human |
| Personal terse handle | Operator, tersely | operator_sequence_number ("23"; Personal = "0") | per-person, in one instance |
| Cross-person, in-instance | all members | E#### (display_identifier) | instance-local, shared |
| 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.
uuid4(), globally unique by construction, app-generated, exposed externally (the {engagement_id} in every route, the project_id in converse/dashboard). Exception: Administrative uses fixed UUID …-0001.display_identifier (E####) — 'E' || lpad(nextval(seq), 4, '0'), a single instance-wide sequence starting at 1, computed on insert. UNIQUE, NOT NULL, text. Shared by every member; interleaved across all people and engagements.operator_sequence_number — bigint, per-person. 0 reserved for that person's Personal engagement, then 1, 2, 3…, gap-tolerant. Shown next to E#### in the engagement list (frontend references operator_sequence_number === 23).external_id, global_id, instance_id, foray_*, etc. on engagements. The only abandoned external-reference field anywhere was memory_events.foray_tx_ref (FORAY ref, never written, dropped in migration 0072) — not engagement identity.config.py has only a mode flag. DestinationInstance is a (label, url) account-routing target — never carries engagement identity.Model A — in-instance · federation absent
UNIQUE(person, engagement)). Many persons co-inhabit one engagement row (E0005 commons has 38).contributors, kind ∈ {human, agent}, commit_authority requires human — the R-B20 gate). Both in-instance.DestinationInstance routes accounts at signup, 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 sees the same one. The cross-instance E#### collision cannot occur today; it is purely a federation-future concern.
4.1 The UUID is the engagement's identity. Globally unique, already exposed. Cross-boundary reference uses the UUID. Sharing 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, shared by all members. In the federation future, the receiving instance assigns its own local E#### to the same UUID — one UUID, many E####s. E#### never needs to be globally unique; two parties holding different E####s for the same UUID is expected.
4.3 The UUID is preserved on share, never regenerated. The load-bearing constraint of 4.2: the receiving instance stores the same UUID (wrapped in a fresh local E####). A new UUID would split identity and break the scheme. UUID is the invariant; E#### the variable.
4.4 Humans never reference E#### across instances. In-instance: address by name / operator_sequence_number / E####. Cross-instance: always the machine path (message + UUID / link). Each instance shows its own E####; the human acts on the shared thing, not 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 positions that were wrong and corrected against engine fact. Recorded so the reasoning is reconstructable, not just the destination.
E#### is the universal handle." Treated early on as if it carried identity across boundaries. Corrected: E#### is a per-instance sequence; the UUID is globally unique. The universal handle existed all along — the unsayable UUID, not the friendly E####.M0001 and a reserved 0001–0999 band. Raised from Operator recollection. Corrected by CC: no M prefix, no reserved band. One prefix (E), sequence starts at 1, E0001 is Administrative (oldest, not reserved-numbered). Personal is an ordinary E#### engagement row (visibility='personal'), not a separate object class.operator_sequence_number — with Personal at 0, as designed. Two of the design's three layers are in fact implemented.operator_sequence_number === 23 directly.0/Personal, not consuming the sequence. The engine gives Personal an ordinary E#### (consuming the sequence) and sets operator_sequence_number = 0. The 0 survives; "doesn't consume the global sequence" and "dual-addressed by name" are not implemented as designed. Noted, not resolved here.lpad(…, 4, '0') 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, ~290 million years to exhaust. Not a real-world limit.display_identifier is text — a longer string costs nothing structural.The one forward caveat is a frontend one: surfaces showing the ID must not hard-code a four-character width or assume exactly four digits — the ID can grow to E10000+. 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.)
Two known properties (recorded, not problems to fix): E#### is an instance-wide shared sequence, so numbers climb with total instance activity, not one Operator's. And a monotonic public sequence leaks the instance's engagement count and growth rate to anyone seeing E#### 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####.
This settles engagement addressing — 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.
E#### to an engagement whose UUID originated elsewhere. Does not exist today (Model A only). The build that 4.2 implies, when federation is taken up.contributors table is an in-instance bearer-token participant. The Operator's "Contributor" in the federation sense (own instance) is a different, future role. Keep distinct; do not overload the word.foray_tx_ref shows the codebase reached there once). Strategic, parked — not required by the machine-mediated-sharing model (4.1).