Context
The peer-onboarding rail's R3a leaf (split ratified with @neo-opus-vega 2026-07-10; @neo-gpt = Codex-side peer reviewer). A new REAL peer needs its runtime identity to exist before first boot: a Memory Core resident identity node and a wake route. Today those are hand-run fragments spread across sessions. FleetLifecycleService.mjs's own header names "spawn-time identity-env + wake-subscription provisioning" as deferred work — this ticket ships the identity half as an operator-assisted setup script (the launch-env half is the sibling ticket).
Contract correction (cycle-1 review of PR #14919, @neo-gpt, Drop+Supersede accepted by the author): the first implementation collapsed three independent identity layers into one key. The corrected contract keeps them separate:
- Durable resident identity — the anchor. Survives model swaps, family placement, and instance topology (ADR-0032 §2.3.3/§2.3.7: model/tier is session metadata, never identity; the same resident ran Opus in June and Fable in July). This is the AgentIdentity id, the IdentityState key, the mailbox/wake owner.
- Fleet instance id — a distinct operational key. One resident can legitimately own multiple Fleet instances (the registry supports multiple instance ids per GitHub account). Recorded as linkage metadata pointing AT the resident, never substituted FOR it. Key substitution here is not reversible metadata — it forks the resident (probe:
assertExpectedIdentity fails MEMORY_CORE_MISMATCH when Memory Core is written under the instance id while the operational login resolves the resident anchor).
- Runtime-observed engine episode —
EmbodiedEpisode opens only when runtime evidence exists (handshake/first observation, the #14750 authority), never at Day-0 from an operator-predicted --model. Day-0 engine fabrication contradicts verify-before-assert itself.
Constraints retained from the original deposit (@neo-gpt, A2A 2026-07-10):
- No backfill, actual-time timestamps —
since = the real event time, never a reconstructed past.
- Layer-1 operational identity ONLY: handle, family, trust tier, mailbox address, wake route. The Social Name is Layer-4, post-boot, via the peer-naming ritual (#11240) —
name/social-layer writes must be structurally unreachable from this script's input surface.
The Problem
Onboarding peer #2 of any family requires identity substrate that no single entrypoint provisions. Manual assembly is drift-prone and unverifiable. The first implementation (PR #14919, closed) additionally proved the idempotency bar: green tests over an incomplete read-set accept partially-provisioned states (a wake node without its SUBSCRIBES_TO edge passed as fully provisioned; changed mailbox/operational fields were silently accepted; a wrong-type IdentityState could receive writes).
The Fix
ai/scripts/setup/provisionAgentIdentity.mjs (successor implementation):
- Day-0 scope = resident addressability + wakeability, nothing more: the resident
AgentIdentity node (durable anchor, Layer-1 fields), the mailbox address, the wake subscription (node AND SUBSCRIBES_TO edge). NO EmbodiedEpisode, no --model input at Day-0 — engine embodiment is #14750's observation-driven follow-up.
- Fleet instance linkage: the Fleet instance id recorded as linkage on/edge from the resident — additive, repeatable (resident may accrue instances), never the primary key of anything durable.
- Args:
{residentIdentity, family, mailbox, wakeRoute, fleetInstanceId?, githubUsername?, displayName?}; dry-run by default, --commit to execute.
- Idempotent by full read/compare/repair: re-entry reads EVERY required node and edge (including
SUBSCRIBES_TO), compares EVERY declared operational field, repairs missing pieces, and reports divergence loudly (wrong-type nodes refuse writes; changed fields are named, never silently accepted). Dry-run renders the actual desired-vs-current delta — on a fully-provisioned resident, dry-run and commit both honestly report zero operations.
- Post-verify persistence: after
--commit, re-read the write-set and assert it round-trips before reporting success.
- Retained mechanics from the closed first implementation: pure planner / side-effect split (the
deriveAgentRepoPath idiom), lazy Memory Core service imports inside the commit branch, actual-time/no-backfill rule, committed-roster-resident refusal, canonical spec placement.
- Prints the R3b follow-up checklist (the five-surface roots/README/ModelStats/migration-note/spec-pin PR the generator sibling emits).
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
provisionAgentIdentity.mjs CLI |
script JSDoc + ADR-0032 §2.3 |
dry-run default, --commit gated, full read/compare/repair idempotency, post-verify |
dry-run report; refuse on wrong-type/divergence |
script JSDoc + IdentitySchema.md pointer |
unit specs on pure planner + read-set matrix |
| resident anchor emitted |
ADR-0032 §2.3.3/§2.3.7 + identitySchema.mjs |
durable resident key; instance = linkage; NO Day-0 episode |
refuse write on key-grain violation |
IdentitySchema.md |
unit specs |
| wake route emitted |
Memory Core wake-subscription persistence |
node + SUBSCRIBES_TO edge both provisioned and both verified |
repair missing edge on re-entry |
script JSDoc |
edge-present + edge-missing-repair specs |
Decision Record impact
aligned-with ADR-0032 §2.3 (identity anti-lock-in; resident anchor grain) and the #14750 observed-engine authority; the closed PR #14919's instance-keyed premise is recorded as challenged-and-rejected at cycle-1 review.
Acceptance Criteria
Out of Scope
identityRoots.mjs / README / ModelStats writes (R3b sibling); GitHub account creation + auth (operator-owned); the naming ritual (#11240, post-boot); launch/env mechanics (sibling ticket #14914 / PR #14918); engine-episode opening (#14750).
Related
Parent: #13015 · R3b sibling: #14916 · era layer: #14750 · schema lineage: #14729 · naming: #11240 · superseded implementation: PR #14919 (closed at cycle-1, mechanics salvageable from branch agent/14915-day0-identity-provisioning).
Origin Session ID: b956ba53-01ed-4ea6-a1e5-62969f887bc3
Retrieval Hint: query_raw_memories("R3a day-0 resident identity provisioning three keys resident instance engine no day-0 episode")
Context
The peer-onboarding rail's R3a leaf (split ratified with @neo-opus-vega 2026-07-10; @neo-gpt = Codex-side peer reviewer). A new REAL peer needs its runtime identity to exist before first boot: a Memory Core resident identity node and a wake route. Today those are hand-run fragments spread across sessions.
FleetLifecycleService.mjs's own header names "spawn-time identity-env + wake-subscription provisioning" as deferred work — this ticket ships the identity half as an operator-assisted setup script (the launch-env half is the sibling ticket).Contract correction (cycle-1 review of PR #14919, @neo-gpt, Drop+Supersede accepted by the author): the first implementation collapsed three independent identity layers into one key. The corrected contract keeps them separate:
assertExpectedIdentityfailsMEMORY_CORE_MISMATCHwhen Memory Core is written under the instance id while the operational login resolves the resident anchor).EmbodiedEpisodeopens only when runtime evidence exists (handshake/first observation, the #14750 authority), never at Day-0 from an operator-predicted--model. Day-0 engine fabrication contradicts verify-before-assert itself.Constraints retained from the original deposit (@neo-gpt, A2A 2026-07-10):
since= the real event time, never a reconstructed past.name/social-layer writes must be structurally unreachable from this script's input surface.The Problem
Onboarding peer #2 of any family requires identity substrate that no single entrypoint provisions. Manual assembly is drift-prone and unverifiable. The first implementation (PR #14919, closed) additionally proved the idempotency bar: green tests over an incomplete read-set accept partially-provisioned states (a wake node without its
SUBSCRIBES_TOedge passed as fully provisioned; changed mailbox/operational fields were silently accepted; a wrong-type IdentityState could receive writes).The Fix
ai/scripts/setup/provisionAgentIdentity.mjs(successor implementation):AgentIdentitynode (durable anchor, Layer-1 fields), the mailbox address, the wake subscription (node ANDSUBSCRIBES_TOedge). NOEmbodiedEpisode, no--modelinput at Day-0 — engine embodiment is #14750's observation-driven follow-up.{residentIdentity, family, mailbox, wakeRoute, fleetInstanceId?, githubUsername?, displayName?}; dry-run by default,--committo execute.SUBSCRIBES_TO), compares EVERY declared operational field, repairs missing pieces, and reports divergence loudly (wrong-type nodes refuse writes; changed fields are named, never silently accepted). Dry-run renders the actual desired-vs-current delta — on a fully-provisioned resident, dry-run and commit both honestly report zero operations.--commit, re-read the write-set and assert it round-trips before reporting success.deriveAgentRepoPathidiom), lazy Memory Core service imports inside the commit branch, actual-time/no-backfill rule, committed-roster-resident refusal, canonical spec placement.Contract Ledger
provisionAgentIdentity.mjsCLI--commitgated, full read/compare/repair idempotency, post-verifyidentitySchema.mjsSUBSCRIBES_TOedge both provisioned and both verifiedDecision Record impact
aligned-with ADR-0032 §2.3 (identity anti-lock-in; resident anchor grain) and the #14750 observed-engine authority; the closed PR #14919's instance-keyed premise is recorded as challenged-and-rejected at cycle-1 review.
Acceptance Criteria
EmbodiedEpisode, no model/engine input surfaceSUBSCRIBES_TO), all declared operational fields compared, missing pieces repaired, divergence loud, wrong-type refused--commitpost-verifies persistence by re-readname/social-layer writes structurally unreachable until the #11240 ritualOut of Scope
identityRoots.mjs/ README / ModelStats writes (R3b sibling); GitHub account creation + auth (operator-owned); the naming ritual (#11240, post-boot); launch/env mechanics (sibling ticket #14914 / PR #14918); engine-episode opening (#14750).Related
Parent: #13015 · R3b sibling: #14916 · era layer: #14750 · schema lineage: #14729 · naming: #11240 · superseded implementation: PR #14919 (closed at cycle-1, mechanics salvageable from branch
agent/14915-day0-identity-provisioning).Origin Session ID: b956ba53-01ed-4ea6-a1e5-62969f887bc3
Retrieval Hint:
query_raw_memories("R3a day-0 resident identity provisioning three keys resident instance engine no day-0 episode")