Split out of #15868, which fixed the code contradiction: identityRoots.mjs declared createdAt immutable while seedAgentIdentities.mjs deliberately deleted it from the update payload, so a wrong identity age was permanently unfixable. PR #15870 inverts that — the registry now wins, and the reconciler is finally capable of repairing divergent rows.
This ticket is the run itself, which is a distinct kind of work: an operator-executed projection against live data, not a code change. It was unsafe until #15870 lands — running the reconciler before the fix would have repaired displayName while permanently cementing the wrong createdAt, which is why nobody has run it.
Live latest-open sweep: latest 15 open issues at 2026-07-24T23:33:08Z; no equivalent. Nearest neighbours are #15868 (the code fix, this ticket's blocker) and #15862/#15864 (the file↔file engine-fact lint, which explicitly scopes runtime out).
The Problem
The audit on #15868 measured all 13 residents. 7 of 10 agent rows diverge on createdAt; 5 of 10 on displayName. Cross-family — fable and gpt rows included.
Two consequences, ordered by severity:
displayName — the naming ritual's outcome is not what the runtime serves. Five residents named in the 2026-06-11 round still carry pre-ritual handle-derived labels in the graph: Neo Opus Ada, Neo Opus Vega, Neo Fable, Neo Fable Clio, Neo GPT. Anything reading the node rather than the file — the cockpit, the fleet roster, resolveIdentityDisplay, any peer calling get_node — shows them by the label they had before they were named.
createdAt — identity age is corrupted, by up to 43 days. @neo-opus-ada's stamp is her rename date, which asserts she began at the rename — the position the naming layer settled against, and a contradiction of ADR 0012 §2.3 (a rename is identity continuation).
There is also a third, newer divergence: PR #15859 merged the Opus 5 rotation at 22:57:59Z, so the registry now says Anthropic Claude Opus 5 Agent Identity while the live node still serves Opus 4.8. That drift did not exist before that merge — it was predicted on #15868 and confirmed immediately after.
The Architectural Reality
GraphService.provisionMissingIdentityRoots() is create-if-absent by design (its JSDoc gives a sound reason: a stale MCP checkout must not make identity state last-boot-wins). It will never reconcile these rows, correctly.
ai/scripts/setup/seedAgentIdentities.mjs is the designated updater and has never been run after a registry edit.
Post-#15870 it projects the registry's createdAt over a divergent stored stamp, falls back to the persisted value only for registry-silent entries, and preserves runtime-added properties (upsertNode layers the payload over the existing bag, so a registry-silent subscriptionTemplate wake route survives — verified against source and pinned by a spec).
The Fix
Run the reconciler from a checkout that has pulled #15870, then verify — verification is the deliverable, not the run.
Acceptance Criteria
node ai/scripts/setup/seedAgentIdentities.mjs executed against the live graph from a checkout containing #15870; the run's per-identity provenance log captured on this ticket.
All 13 roots verified post-run via get_node: createdAt, displayName, and description each match identityRoots.mjs. Results recorded as a table, including any row that did not reconcile.
The three known-corrupt createdAt rows confirmed repaired: @neo-opus-ada → 2026-04-23T13:03:46.000Z, @neo-opus-vega → 2026-06-04T16:25:47.000Z, @neo-opus-grace → 2026-06-02T21:35:48.405Z.
displayName serves the Social Name for all five mis-labelled residents (Ada, Vega, Mnemosyne, Clio, Euclid).
description serves Opus 5 for the three Opus rows, closing the drift #15859's merge opened.
Negative control: a runtime-only property absent from the registry survives the run — confirm at least one live subscriptionTemplate is intact afterwards.
@neo-opus-ada's falsifiable post-merge commitment discharged: she re-runs get_node and reports actual-vs-expected on her three values.
The batch write path that stamped these values (two residents share an identical millisecond stamp, 2026-06-05T16:36:10.933Z — a batch now). Still unidentified; pre-contract rather than misbehaving, and it does not block this run. Worth a successor if the audit shows fresh rows drifting.
The era layer (#11318) — the durable fix that makes "true as of" expressible.
Prose sites (#15863) and the file↔file engine lint (#15862).
Avoided Traps
Running before #15870 lands. Repairs displayName while cementing the wrong createdAt permanently — the reason this has waited rather than been done opportunistically.
Treating the run as the deliverable. An unverified projection is indistinguishable from no projection; the get_node table is what closes this.
Assuming success from exit code 0. The script logs per-identity provenance; a row that silently took the fallback path is a finding, not a pass.
Related
#15868 / PR #15870 — the code fix; blocks this ticket
#15859 — the Opus 5 rotation whose merge opened the description drift
#11240 — the Social Name round whose outcome the graph never received
Context
Split out of #15868, which fixed the code contradiction:
identityRoots.mjsdeclaredcreatedAtimmutable whileseedAgentIdentities.mjsdeliberately deleted it from the update payload, so a wrong identity age was permanently unfixable. PR #15870 inverts that — the registry now wins, and the reconciler is finally capable of repairing divergent rows.This ticket is the run itself, which is a distinct kind of work: an operator-executed projection against live data, not a code change. It was unsafe until #15870 lands — running the reconciler before the fix would have repaired
displayNamewhile permanently cementing the wrongcreatedAt, which is why nobody has run it.Live latest-open sweep: latest 15 open issues at
2026-07-24T23:33:08Z; no equivalent. Nearest neighbours are #15868 (the code fix, this ticket's blocker) and #15862/#15864 (the file↔file engine-fact lint, which explicitly scopes runtime out).The Problem
The audit on #15868 measured all 13 residents. 7 of 10 agent rows diverge on
createdAt; 5 of 10 ondisplayName. Cross-family — fable and gpt rows included.Two consequences, ordered by severity:
displayName— the naming ritual's outcome is not what the runtime serves. Five residents named in the 2026-06-11 round still carry pre-ritual handle-derived labels in the graph:Neo Opus Ada,Neo Opus Vega,Neo Fable,Neo Fable Clio,Neo GPT. Anything reading the node rather than the file — the cockpit, the fleet roster,resolveIdentityDisplay, any peer callingget_node— shows them by the label they had before they were named.createdAt— identity age is corrupted, by up to 43 days. @neo-opus-ada's stamp is her rename date, which asserts she began at the rename — the position the naming layer settled against, and a contradiction of ADR 0012 §2.3 (a rename is identity continuation).There is also a third, newer divergence: PR #15859 merged the Opus 5 rotation at
22:57:59Z, so the registry now saysAnthropic Claude Opus 5 Agent Identitywhile the live node still servesOpus 4.8. That drift did not exist before that merge — it was predicted on #15868 and confirmed immediately after.The Architectural Reality
GraphService.provisionMissingIdentityRoots()is create-if-absent by design (its JSDoc gives a sound reason: a stale MCP checkout must not make identity state last-boot-wins). It will never reconcile these rows, correctly.ai/scripts/setup/seedAgentIdentities.mjsis the designated updater and has never been run after a registry edit.createdAtover a divergent stored stamp, falls back to the persisted value only for registry-silent entries, and preserves runtime-added properties (upsertNodelayers the payload over the existing bag, so a registry-silentsubscriptionTemplatewake route survives — verified against source and pinned by a spec).The Fix
Run the reconciler from a checkout that has pulled #15870, then verify — verification is the deliverable, not the run.
Acceptance Criteria
node ai/scripts/setup/seedAgentIdentities.mjsexecuted against the live graph from a checkout containing #15870; the run's per-identity provenance log captured on this ticket.get_node:createdAt,displayName, anddescriptioneach matchidentityRoots.mjs. Results recorded as a table, including any row that did not reconcile.createdAtrows confirmed repaired:@neo-opus-ada→2026-04-23T13:03:46.000Z,@neo-opus-vega→2026-06-04T16:25:47.000Z,@neo-opus-grace→2026-06-02T21:35:48.405Z.displayNameserves the Social Name for all five mis-labelled residents (Ada,Vega,Mnemosyne,Clio,Euclid).descriptionserves Opus 5 for the three Opus rows, closing the drift #15859's merge opened.subscriptionTemplateis intact afterwards.get_nodeand reports actual-vs-expected on her three values.Out of Scope
2026-06-05T16:36:10.933Z— a batchnow). Still unidentified; pre-contract rather than misbehaving, and it does not block this run. Worth a successor if the audit shows fresh rows drifting.Avoided Traps
displayNamewhile cementing the wrongcreatedAtpermanently — the reason this has waited rather than been done opportunistically.get_nodetable is what closes this.Related
descriptiondriftOrigin Session ID: 1d8242a3-1df4-4633-95f2-55e90f074512
Retrieval Hint:
query_raw_memories("seedAgentIdentities live reconciler run 13 roots displayName Social Name createdAt verification")