Context
Split from #13488 (item b). #13488 delivers item (a) — the named unboundIdentityError self-diagnosis at the ~17 identity-gated throw sites. This ticket tracks item (b): surfacing the bound/unbound identity status proactively (at boot / via healthcheck) so an identity regression fails-fast or is queryable, instead of surfacing lazily at the first identity-gated tool call.
Carries the cloud-safety guardrail from @neo-opus-grace's cross-family design review of #13488.
Release classification: boardless (agent-os stability/self-healing — not v13 release-blocking).
Dup sweep: covered by the #13488 sweep (latest 25 open, 2026-06-19); this is a documented split, no equivalent exists.
The Problem
Server.logIdentityStatus() (ai/mcp/server/memory-core/Server.mjs:441-450) already computes the bound/unbound status at boot and logs it — but only to the server log, invisible to the agent, and not surfaced via the healthcheck tool. So a stale/regressed identity stays silently unbound until the first A2A call fails.
The Architectural Reality
Server.logIdentityStatus() (L441-450) already builds Identity: <userId> via <source> — bound to X / unbound (no matching AgentIdentity node).
Server.bindAgentIdentity() (L404-435) — the lookup; L429 logs the named miss.
aiConfig.auth.mode (ai/config.mjs:108, 'oidc' | 'gitlab-pat', via the AiConfig SSOT per ADR 0019) + AuthService.setup():67 branch — distinguishes single-identity-at-boot (stdio) from per-request identity (SSE multi-user).
The Fix
- Surface the identity-resolution status (userId, source, bound/unbound) as a field in the
healthcheck tool output — queryable, not log-only.
- Mode-aware fail-loud (CRITICAL — cloud guardrail): flag/fail-fast the unbound-at-boot case ONLY under the local stdio single-identity path (
this.stdioIdentity with source:'env-var' and a null agentIdentityNodeId — the regression). Under gitlab-pat/multi-user mode, identity is per-request from the bearer (no single boot identity) → the check MUST be a no-op there; a naive boot-fatal check would fail-fast-kill the multi-user MC boot. Preserve the existing source:'unresolved' single-tenant non-fatal fallthrough.
Cloud-impact AC (from @neo-opus-grace)
- Boot/healthcheck identity enforcement is gated on local single-identity mode; under per-request (
gitlab-pat) multi-user mode it is a verified no-op — confirmed not to abort multi-user MC boot. PR must carry an explicit cloud-impact line.
Decision Record impact
none — observability over the existing identity-resolution design; aligned with #10692 (RequestContextService) + #11318.
Acceptance Criteria
Out of Scope
- The named throw-site errors (item a — delivered in #13488 via
RequestContextService.unboundIdentityError).
- Harness config-recovery / stale-snapshot fragility (separate harness/operator note).
Related
- Split from #13488 (item a delivered there); parent epic #13012. Builds on the
unboundIdentityError helper from #13488.
Handoff Retrieval Hints
- Retrieval Hint:
logIdentityStatus healthcheck mode-aware gitlab-pat no-op boot identity
Origin Session ID: 0f7b7d69-7c6c-4699-b17f-09044426f2e3
Context
Split from #13488 (item b). #13488 delivers item (a) — the named
unboundIdentityErrorself-diagnosis at the ~17 identity-gated throw sites. This ticket tracks item (b): surfacing the bound/unbound identity status proactively (at boot / viahealthcheck) so an identity regression fails-fast or is queryable, instead of surfacing lazily at the first identity-gated tool call.Carries the cloud-safety guardrail from @neo-opus-grace's cross-family design review of #13488.
Release classification: boardless (agent-os stability/self-healing — not v13 release-blocking). Dup sweep: covered by the #13488 sweep (latest 25 open, 2026-06-19); this is a documented split, no equivalent exists.
The Problem
Server.logIdentityStatus()(ai/mcp/server/memory-core/Server.mjs:441-450) already computes the bound/unbound status at boot and logs it — but only to the server log, invisible to the agent, and not surfaced via thehealthchecktool. So a stale/regressed identity stays silently unbound until the first A2A call fails.The Architectural Reality
Server.logIdentityStatus()(L441-450) already buildsIdentity: <userId> via <source> — bound to X / unbound (no matching AgentIdentity node).Server.bindAgentIdentity()(L404-435) — the lookup; L429 logs the named miss.aiConfig.auth.mode(ai/config.mjs:108,'oidc' | 'gitlab-pat', via the AiConfig SSOT per ADR 0019) +AuthService.setup():67branch — distinguishes single-identity-at-boot (stdio) from per-request identity (SSE multi-user).The Fix
healthchecktool output — queryable, not log-only.this.stdioIdentitywithsource:'env-var'and a nullagentIdentityNodeId— the regression). Undergitlab-pat/multi-user mode, identity is per-request from the bearer (no single boot identity) → the check MUST be a no-op there; a naive boot-fatal check would fail-fast-kill the multi-user MC boot. Preserve the existingsource:'unresolved'single-tenant non-fatal fallthrough.Cloud-impact AC (from @neo-opus-grace)
gitlab-pat) multi-user mode it is a verified no-op — confirmed not to abort multi-user MC boot. PR must carry an explicit cloud-impact line.Decision Record impact
none— observability over the existing identity-resolution design; aligned with #10692 (RequestContextService) + #11318.Acceptance Criteria
healthcheckoutput includes resolved identity status (userId / source / bound|unbound).env-varpresent-but-unresolvable case.gitlab-pat/per-request multi-user mode (verified not to abort boot).Out of Scope
RequestContextService.unboundIdentityError).Related
unboundIdentityErrorhelper from #13488.Handoff Retrieval Hints
logIdentityStatus healthcheck mode-aware gitlab-pat no-op boot identityOrigin Session ID: 0f7b7d69-7c6c-4699-b17f-09044426f2e3