#16738 must build ownerPrincipal as an opaque id backed by (authProvider, normalizedProviderBaseUrl, providerUserId). Two design questions are open at D#16764 — whether normalization is frozen or versioned, and which mechanism owns the transport half — and the discussion has been reasoning about current behaviour from descriptions rather than measurements. Twice, an assumption survived several exchanges before a source read falsified it.
This leaf delivers the measurement layer those questions need: a witness matrix that pins what is TRUE TODAY on every axis, selects no design option, and changes the moment any of it changes.
The Problem
The open questions cannot be priced without knowing the current state, and the current state was repeatedly mis-stated:
Transport normalization was believed to be leaf-owned. It is not; it is duplicated at two consumer sites.
metadata.parse was believed to normalize a leaf value for every consumer. It does not; it is an env decoder covering one of three entry points.
The re-key hazard was framed as a risk a new principal design might introduce. It is the current keying.
Each was corrected only by reading the producer. A durable instrument prevents the next re-derivation, and gives the fold numbers instead of estimates.
The Architectural Reality
ai/configBase.mjs:528 / :537 — the two auth base-URL leaves; neither declares a custom metadata.parse.
ai/mcp/server/shared/services/AuthService.mjs:733 / :904 — the only normalization in the system, a trailing-slash strip duplicated at both consumer sites; providerBaseUrl is set from the stripped local while the leaf keeps its own spelling.
ai/ConfigProvider.mjs:321 — #applyEnvLayer calls decode(meta.env, {env, warn}), so parse receives the env var NAME, runs only in the env layer, and is skipped when a runtime override exists.
ai/mcp/server/memory-core/Server.mjs:577 — the durable AgentIdentity graph node id is normalizeAgentIdentityNodeId(userId), and userId is the provider login; :601 persists providerUserId as a property of that same row.
ai/graph/normalizeAgentIdentityNodeId.mjs — pure module, one argument, no provider coordinate.
The Fix
One spec, test/playwright/unit/ai/mcp/server/shared/services/ownerPrincipalNormalizationAxes.spec.mjs, asserting only present-tense facts. It uses createConfigProxy(Neo.create(RootConfigBase)) so assertions hit the real declarations rather than a replica leaf, and isolates by construction per ADR-0019 §4/B4 without mutating the shared singleton. Where no pure export exists the assertion is source-anchored with its bound stated in the spec.
Decision Record impact
aligned-with ADR 0019 — consumes §4/B4 isolation and §5.2 leaf-parser semantics; asserts no change to either.
Acceptance Criteria
The auth base-URL leaf is shown to normalize on no axis (trailing slash, case, default port, relative root)
Blast radius recorded: five spellings of one host resolve to five distinct principal coordinates, red-proved against a collapsed-class expectation
The two-reader divergence is pinned: one leaf, two spellings, strip living at two consumer sites
metadata.parse reach measured across all three entry points (env layer, leaf default, setEnvOverride)
Login-keyed ownership population sized: producers, comparison sites, and which stable fields already travel
First-write schedule pinned: the durable graph key and when it is first written
Every assertion is present-tense and selects no design option, so any landing normalization flips it deliberately
Out of Scope
Choosing frozen vs versioned normalization, or the transport mechanism — both belong to D#16764
Implementing the ownerPrincipal derivation itself, which stays with #16738
Changing any runtime behaviour; this leaf is test-only
Avoided Traps
A replica leaf. Declaring a lookalike leaf in the spec would assert the framework's behaviour rather than this repository's configuration — a test that cannot fail. The real RootConfigBase is constructed instead.
Importing a service to reach its logic.AuthService builds its verifiers inside factories and exports no pure normalizer; the unit-test workflow forbids importing a connect-on-init singleton for its logic, so those arms are source-anchored with the bound written into the spec.
Asserting a target shape. Encoding the expected post-fix behaviour would pin a decision the fold has not taken and would silently rot into a false contract.
Related
Refs #16738 · D#16764 · #15598 (whose Contract Ledger declared this normalization with a unit: base-url resolution spec evidence row that was never written) · ADR 0019
Live latest-open sweep: checked latest 20 open issues at 2026-08-09T12:31:14Z; no equivalent found. A2A in-flight sweep: no claim on this scope.
tobiu referenced in commit 231fb43 - "test(ai): witness matrix for owner-principal normalization and identity keying (#16782) (#16783) on Aug 9, 2026, 3:18 PM
tobiu referenced in commit b186cb2 - "test(ai): correct the blast-radius count — 4 principal coordinates, not 5 (#16782) (#16789) on Aug 9, 2026, 5:44 PM
Context
#16738must buildownerPrincipalas an opaque id backed by(authProvider, normalizedProviderBaseUrl, providerUserId). Two design questions are open atD#16764— whether normalization is frozen or versioned, and which mechanism owns the transport half — and the discussion has been reasoning about current behaviour from descriptions rather than measurements. Twice, an assumption survived several exchanges before a source read falsified it.This leaf delivers the measurement layer those questions need: a witness matrix that pins what is TRUE TODAY on every axis, selects no design option, and changes the moment any of it changes.
The Problem
The open questions cannot be priced without knowing the current state, and the current state was repeatedly mis-stated:
metadata.parsewas believed to normalize a leaf value for every consumer. It does not; it is an env decoder covering one of three entry points.Each was corrected only by reading the producer. A durable instrument prevents the next re-derivation, and gives the fold numbers instead of estimates.
The Architectural Reality
ai/configBase.mjs:528/:537— the two auth base-URL leaves; neither declares a custommetadata.parse.ai/mcp/server/shared/services/AuthService.mjs:733/:904— the only normalization in the system, a trailing-slash strip duplicated at both consumer sites;providerBaseUrlis set from the stripped local while the leaf keeps its own spelling.ai/ConfigProvider.mjs:321—#applyEnvLayercallsdecode(meta.env, {env, warn}), soparsereceives the env var NAME, runs only in the env layer, and is skipped when a runtime override exists.ai/mcp/server/memory-core/Server.mjs:577— the durable AgentIdentity graph node id isnormalizeAgentIdentityNodeId(userId), anduserIdis the provider login;:601persistsproviderUserIdas a property of that same row.ai/graph/normalizeAgentIdentityNodeId.mjs— pure module, one argument, no provider coordinate.The Fix
One spec,
test/playwright/unit/ai/mcp/server/shared/services/ownerPrincipalNormalizationAxes.spec.mjs, asserting only present-tense facts. It usescreateConfigProxy(Neo.create(RootConfigBase))so assertions hit the real declarations rather than a replica leaf, and isolates by construction per ADR-0019 §4/B4 without mutating the shared singleton. Where no pure export exists the assertion is source-anchored with its bound stated in the spec.Decision Record impact
aligned-with ADR 0019— consumes §4/B4 isolation and §5.2 leaf-parser semantics; asserts no change to either.Acceptance Criteria
metadata.parsereach measured across all three entry points (env layer, leaf default,setEnvOverride)Out of Scope
D#16764ownerPrincipalderivation itself, which stays with#16738Avoided Traps
RootConfigBaseis constructed instead.AuthServicebuilds its verifiers inside factories and exports no pure normalizer; the unit-test workflow forbids importing a connect-on-init singleton for its logic, so those arms are source-anchored with the bound written into the spec.Related
Refs #16738 ·
D#16764·#15598(whose Contract Ledger declared this normalization with aunit: base-url resolution specevidence row that was never written) ·ADR 0019Live latest-open sweep: checked latest 20 open issues at 2026-08-09T12:31:14Z; no equivalent found. A2A in-flight sweep: no claim on this scope.
Retrieval Hint: "owner principal normalization witness matrix leaf parse reach first-write graph key login"