LearnNewsExamplesServices
Frontmatter
id14750
titleRetire flat era-owned facts from identityRoots: migrate the 8 consumer read-paths onto the hydration index
stateOpen
labels
enhancementaiarchitecture
assigneesneo-opus-vega
createdAtJul 4, 2026, 2:28 PM
updatedAtJul 27, 2026, 1:14 AM
githubUrlhttps://github.com/neomjs/neo/issues/14750
authorneo-fable
commentsCount3
parentIssue14677
subIssues
15277 Era-chain-first family read spine + era-owned flat-fact retirement
subIssuesCompleted1
subIssuesTotal1
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Retire flat era-owned facts from identityRoots: migrate the 8 consumer read-paths onto the hydration index

Open Backlog/active-chunk-4 enhancementaiarchitecture
neo-fable
neo-fable commented on Jul 4, 2026, 2:28 PM

Context

The retirement half split from #14731 at implementation (consumer-census-driven): the migration module now expresses every live agent resident through the identity schema (seed eras carry the flat facts verbatim; THE PROPERTY proven on production data), but the flat era-owned fields cannot leave ai/graph/identityRoots.mjs until every consumer's read path moves.

The consumer census (V-B-A'd 2026-07-04)

modelFamily/family are load-bearing in: ai/services/memory-core/MailboxService.mjs (A2A AGENT:<family>/<model> alias resolution — ambiguity rejection reads properties.modelFamily), ai/services/memory-core/WakeSubscriptionService.mjs (family-filtered wake routing), ai/services/graph/agentFamilyResolution.mjs (the resolver itself). Zero consumers read contextWindowInput/capability facts outside the schema family. 8+ modules import identityRoots for identity-level operational fields (trust tiers, wake routes, mailbox addresses) — those stay identity-level by design and are NOT part of this retirement.

The Fix

  • Each family/model read-path consumes buildHydrationIndex(...).index.currentEra.family (with isIndexCurrent gating any cached read) instead of the flat property — one consumer per commit, regression per consumer (alias resolution and wake routing must behave identically before/after).
  • Then delete the era-owned flat fields (modelFamily duplicates, ...) STALE — CORRECTED 2026-07-17 (V-B-A, @neo-opus-vega). DO NOT DELETE modelFamily. This line is dangerous: two live specs pin modelFamily as identity-level, NOT era-owned. identityRootsMigration.spec.mjs:82 asserts the retired set is exactly ['family', 'tier', 'contextWindowInput', 'parallelToolCalls', 'thoughtBudget', 'hosting', 'sunsetTriggers']modelFamily is deliberately absent (:81: "modelFamily stays — identity-level"), and identityRoots.mjs:7-8 names only the family duplicate as retired. Deleting modelFamily would null @neo-gpt-emmy's family (the sole flat-fallback resident) and break AGENT:gpt/* A2A alias resolution + family-keyed wake routing. The era-owned deletions this line asks for already shipped — the identity keeps only the anchor, social layer, and identity-level lifecycle/operational fields.
  • The graph-seeding path writes IdentityState + EmbodiedEpisode nodes from the migration module's output.

Status (V-B-A 2026-07-17, @neo-opus-vega — live, not inferred)

3 of 4 ACs are already MET; only AC4 remains, and it is blocked. Verified by running the specs (identityRootsMigration + identityHydration + agentFamilyResolution: 16 passed) and by a controlled coverage probe over the live registry:

era-chain covered: 7/11 residents
NOT covered:  @system · @tobiu · AGENT:*   -> non-agents; migrateResident refuses them BY DESIGN
              @neo-gpt-emmy (flat='gpt')   -> the ONLY real flat-fallback resident
control: a non-resident resolves to null  -> PASS (the probe can see a miss, so 7/11 is real)
  • AC1 — MET. identityRootsMigration.spec.mjs:82 mechanically asserts all seven era-owned fields are gone.
  • AC2 — MET. Consumers are already era-chain-first (agentFamilyResolution.mjs:61-73, harnessRouting.mjs:88, WakeSubscriptionService.mjs:620); agentFamilyResolution.spec.mjs:16 IS the regression AC.
  • AC3 — N/A by design. No consumer caches the index, so no gate applies (agentFamilyResolution.mjs:50-52).
  • AC4 — NOT met, BLOCKED. Needs the graph-seeding slice to give post-epoch residents an observation-owned era, plus the bearer-audited backfill (ERA_BACKFILL_CANDIDATES still lists @neo-opus-vega).

The mechanical witness already exists and I rediscovered it the long way: agentFamilyResolution.spec.mjs:42 pins the fallback population exactly — expect(postEpoch.map(i => i.id)).toEqual(['@neo-gpt-emmy']). Per agentFamilyResolution.mjs:56: "when it reaches zero, the era-owned flat fields can leave." Population is 1. That single resident is this ticket's entire remaining blocker.

Acceptance Criteria

  • Zero era-owned facts flat on identityRoots.mjs entries.
  • A2A alias resolution + wake family-routing regressions green before AND after each consumer move.
  • Cached identity reads gated by isIndexCurrent.
  • The reflexive-landing fixture ↔ production agreement AC (carried from #14731) lands here, meaningful once bearer-audited era backfill exists.

Blocked-by

PR chain #14729#14730 → the #14731 migration PR (this consumes all three).

Related

Parent #14677 · #14731 (the migration module + the census) · the backfill candidates exported by identityRootsMigration.mjs.

Claimable — high care (A2A routing is production-critical); the per-consumer-per-commit discipline is the safety rail.

Origin Session ID: b9b95ac6-42f5-47a3-b58f-6071f79657e8 Retrieval Hint: "identityRoots retirement flat facts consumer migration hydration index alias resolution wake routing"

tobiu referenced in commit 0a33032 - "feat(graph): retire era-owned flat facts from identityRoots — consumers onto the hydration index (#14750) (#15259) on Jul 16, 2026, 6:24 PM