Skip to content

Make AgentIdentity boot seeding additive and activation refresh deployment-aware #15431

Description

@neo-kimi-phoebe

Context

Two onboarding receipts now expose both sides of the same projection failure.

  1. Lag: PR feat(agentos): activate @neo-kimi-phoebe — first-boot onboarding completion (#15390) #15393 activated @neo-kimi-phoebe at 2026-07-18T02:12:50Z, while the live graph stayed unreachable until the explicit refresh/restart around 02:26Z.
  2. Rewind: PR feat(agentos): activate @neo-kimi-iris and land the Iris Social Name fields (#15581) #15582 activated @neo-kimi-iris at 2026-07-19T19:15:34Z. Iris ran the canonical identity seed and verified the active projection after merge. A later MCP startup from a stale shared runtime checkout replayed the pre-activation root, so get_node('@neo-kimi-iris', full) again showed temporarily_unreachable and who_is_online hard-gated her offline.

The second incident was reproduced and repaired on 2026-07-19. Iris's seat-local MCP config bound the correct .env but executed server source from /Users/Shared/github/neomjs/neo at stale head 396ab22238. That checkout still defined Iris as first-boot-pending. GraphService.initAsync then whole-record-upserted every existing identity at server boot. After the shared runtime was safely fast-forwarded to 090495bea7 and seedAgentIdentities.mjs ran, the full node again projected displayName: Iris, participationStatus: active, and who_is_online(verbose:true) returned Iris online at 2026-07-19T20:13:49Z.

The Problem

Boot-time provisioning and intentional canonical re-projection currently share overwrite semantics:

  • GraphService.initAsync imports the process-local IDENTITIES snapshot and upserts every root on every Memory Core startup, including roots already present in SQLite.
  • seedAgentIdentities.mjs intentionally applies the current canonical registry and preserves createdAt.
  • In a shared graph with mixed-version MCP processes, the startup path makes identity state last-boot-wins. A stale checkout can silently rewind displayName, participationStatus, status provenance, and any runtime-added properties after a correct explicit seed.
  • participationStatus is a hard wake/review/quorum gate, so this is not cosmetic projection lag: it can remove an active maintainer from routing.

This violates #10232's original self-seed contract: boot should upsert missing roots, while an existing root preserves createdAt and user-added properties. The current else branch preserves only createdAt before replaying the rest of the static record.

Selected Architecture

Separate the two write authorities.

  1. Boot-time provisioning is additive-only. GraphService.initAsync lazy-loads each canonical root and calls upsertGlobalNode(identity) only when the node is absent. Existing roots are not rewritten by process startup.
  2. Explicit seeding remains the intentional update primitive. seedAgentIdentities.mjs continues to apply canonical registry changes while preserving the persisted createdAt.
  3. The activation/status-flip gate is deployment-aware. Extend the existing onboarding/operator surface so the post-merge sequence is explicit: pull current dev in the Memory Core runtime checkout, run the canonical seed, then fail closed until the full AgentIdentity projection and who_is_online agree with the expected status.
  4. No generic merge-time GitHub Action mutates live graphs. Repository CI does not know every local/cloud Memory Core target or hold their mutation authority.

Contract Ledger

Surface Contract
GraphService.initAsync Create missing identity roots before binding; never overwrite an existing root during ordinary process boot.
seedAgentIdentities.mjs Explicitly apply the current canonical root record; preserve the persisted creation provenance.
Onboarding/status-flip operator gate Pull the target runtime checkout first, invoke explicit seeding, and verify the expected full node plus liveness projection.
get_node(..., projection:'full') / who_is_online(verbose:true) Both must report the expected participationStatus; mismatch is a failed activation gate, not a warning.

Acceptance Criteria

  • A focused regression starts with an existing identity whose persisted facts disagree with the process-local seed; GraphService startup preserves the complete persisted record, including runtime-added properties.
  • Fresh-DB coverage still proves boot-time creation of every canonical AgentIdentity and AGENT:* before identity binding.
  • Focused coverage proves explicit seeding updates canonical facts while preserving the original createdAt.
  • The activation/status-flip operator surface names the exact pull → explicit seed → full-node/liveness verification sequence and fails closed on mismatch.
  • Live recovery receipt: Iris restored to active/online at 2026-07-19T20:13:49Z after the target runtime checkout was updated and explicitly seeded.

Out of Scope

Avoided Traps

  • Keep whole-record startup writes but compare timestamps: a stale process has no authoritative transition epoch, and wall-clock ordering would not prove operator intent.
  • Merge selected runtime fields: this creates two partial canonical records and still lets old processes rewind whichever fields they own.
  • Remove the standalone seed: explicit re-projection remains necessary after intentional registry changes and fresh deployment pulls.
  • Generic post-merge re-seed: CI has no safe target-discovery or credential boundary for local and cloud Memory Core installations.

Related

Decision Record impact: none. This restores the accepted #10232 bootstrap/manual-seed split rather than introducing a new topology.

Origin Session ID: 20781e6b-ae74-48a6-ad1a-cfaba98d6590; 2026-07-19 incident continuation: 049d9856-daa8-4151-b089-8ce8c7ed56f4.

Metadata

Metadata

Assignees

Labels

aienhancementNew feature or requestmodel-experienceIssues related to Model Experience (MX), agent workflows, and AI friction

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions