Context
Residual cleanup carved from #13571 / PR #13572 (per @neo-gpt's close-target reconciliation on the #13572 review). The read boundaries now tolerate both stored user_id forms (@-prefixed and normalized), so reads are correct regardless of the stored form. This ticket eliminates the inconsistency at its source: the write path still stamps the @-prefixed getAgentIdentityNodeId() form into the user_id column, while MemoryService (and others) write the normalized (no-@) form — the mixed-form column the read-side now has to defensively tolerate.
Problem
GraphService.upsertNode / linkNodes stamp currentUserId from getAgentIdentityNodeId() — the @-prefixed node id, explicitly documented as NOT the isolation key in RequestContextService — into the user_id isolation column. Writers therefore disagree on the stored form, producing the mixed-form column that forced the both-form read tolerance in PR #13572.
Fix
- Stamp the canonical isolation key (
normalizeUserId(getUserId() ?? getAgentIdentityNodeId()) — the same canonicalization PR #13572 applied on the read side) on the write path (upsertNode / linkNodes), so all new rows store one normalized form.
- Once writers are consistent, evaluate whether to (a) simplify the read-side both-form tolerance back to a single predicate, and (b) run a one-time migration to normalize existing
@-form rows — optional, since reads already tolerate both forms (not correctness-critical).
Acceptance Criteria
Out of Scope
- The read-side fix (delivered in PR #13572).
- Forced migration of existing rows (separate decision; reads already tolerate both forms).
Related
Source bug: #13571 (the @-form/mixed-form inconsistency). Read-side mitigation: PR #13572. Isolation-key contract: getUserId() is the isolation key; getAgentIdentityNodeId() is not.
Context
Residual cleanup carved from #13571 / PR #13572 (per @neo-gpt's close-target reconciliation on the #13572 review). The read boundaries now tolerate both stored
user_idforms (@-prefixed and normalized), so reads are correct regardless of the stored form. This ticket eliminates the inconsistency at its source: the write path still stamps the@-prefixedgetAgentIdentityNodeId()form into theuser_idcolumn, whileMemoryService(and others) write the normalized (no-@) form — the mixed-form column the read-side now has to defensively tolerate.Problem
GraphService.upsertNode/linkNodesstampcurrentUserIdfromgetAgentIdentityNodeId()— the@-prefixed node id, explicitly documented as NOT the isolation key inRequestContextService— into theuser_idisolation column. Writers therefore disagree on the stored form, producing the mixed-form column that forced the both-form read tolerance in PR #13572.Fix
normalizeUserId(getUserId() ?? getAgentIdentityNodeId())— the same canonicalization PR #13572 applied on the read side) on the write path (upsertNode/linkNodes), so all new rows store one normalized form.@-form rows — optional, since reads already tolerate both forms (not correctness-critical).Acceptance Criteria
upsertNode/linkNodesstamp the normalized canonical key intouser_id(no leading@).user_idis normalized.Out of Scope
Related
Source bug: #13571 (the @-form/mixed-form inconsistency). Read-side mitigation: PR #13572. Isolation-key contract:
getUserId()is the isolation key;getAgentIdentityNodeId()is not.