Context
Surfaced during PR #11714 Cycle-2 re-review (review PRR_kwDODSospM8AAAABAnZslw). RA-3 verified that GraphService RLS makes visibility:'team' load-bearing for kb-manifest:<tenantId> nodes read by the offline KB reconciliation daemon. That same verification exposed an adjacent risk for kb-config:<tenantId> nodes written by #11637 and consumed by #11640.
Duplicate sweep before filing: ask_knowledge_base('kb-config visibility team offline reconciliation daemon getTenantConfig RLS follow-up', type='ticket') surfaced historical RLS tickets (#10010/#10011 family) but no KB config daemon-readability ticket; exact rg over resources/content/issues / resources/content/discussions found RLS history but no kb-config follow-up.
The Problem
KnowledgeBaseIngestionService.setTenantConfig() writes kb-config:<tenantId> via GraphService.upsertNode() with tenant config properties and version, but it does not set visibility:'team'.
Verified code path:
GraphService.isRlsVisible() exposes a node when userId is absent, matches the requester, sharedEntity is true, or visibility === 'team' (ai/services/memory-core/GraphService.mjs:21-34).
GraphService.upsertNode() stamps properties.userId when a request-scoped identity exists and p.userId is absent (ai/services/memory-core/GraphService.mjs:199-237).
GraphService.getNodeRecord() resolves the active requester and returns null when isRlsVisible() rejects the node (ai/services/memory-core/GraphService.mjs:623-637).
setTenantConfig() writes kb-config:<tenantId> without visibility:'team' (ai/services/knowledge-base/KnowledgeBaseIngestionService.mjs:882-904).
KbReconciliationService.fetchTenantConfigVersion() reads that config offline through KnowledgeBaseIngestionService.getTenantConfig() and degrades to version 0 when no graph config is visible (ai/daemons/KbReconciliationService.mjs:220-226, 300-304).
If setTenantConfig() runs under request context, the config node can become owned by that request identity. The offline reconciliation daemon has no request identity, so getTenantConfig() may fail-soft to the default/yaml tier (version: 0). That would silently weaken or disable #11640 config-staleness detection for the affected tenant.
The Architectural Reality
This is not a #11714 merge blocker. PR #11714 correctly marked the new kb-manifest:<tenantId> node as visibility:'team' and documented why. This ticket audits the older sibling config node from #11637 under the same verified RLS model.
The owning substrate is the KB ingestion service / Native Edge Graph RLS boundary, not the reconciliation diff engine. The fix should preserve tenant write authorization (resolveTenantContext) while ensuring daemon-owned offline reads observe the same config state that request-authored writes create.
The Fix
Audit and, if verified, make kb-config:<tenantId> daemon-readable without weakening tenant write gates.
Likely implementation shape:
- Add
visibility:'team' to the KnowledgeBaseTenantConfig node properties written by setTenantConfig(), mirroring the manifest node rationale from #11714.
- Update JSDoc to state the write gate remains
resolveTenantContext, while visibility:'team' is the shared-read marker for offline daemons.
- Add focused unit coverage proving a request-authored tenant config remains readable by the offline
getTenantConfig() / reconciliation path.
- If investigation finds
setTenantConfig() cannot run under request context in production, document that evidence and close as no-code.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
KnowledgeBaseTenantConfig graph node (kb-config:<tenantId>) |
#11637 + GraphService RLS verified in PR #11714 Cycle-2 |
Request-authored tenant config nodes must remain readable to offline KB daemons |
If no request identity stamped the node, current ownerless visibility remains valid |
JSDoc on setTenantConfig() |
Unit test with request-context write + offline read |
KbReconciliationService.fetchTenantConfigVersion() |
#11640 config-staleness daemon |
Offline daemon reads the real tenant config version, not fallback 0, when graph config exists |
Missing/invisible config still degrades fail-soft to 0 only when truly absent/unreadable |
Inline test name / PR body |
Focused daemon or service test proving version visibility |
Acceptance Criteria
Out of Scope
- Changing the broader
GraphService RLS policy.
- Changing
kb-manifest:<tenantId> behavior from #11714.
- Reworking tenant config storage architecture from #11637.
- Altering #11640 reconciliation diff semantics beyond correctly reading the existing config version.
Avoided Traps
| Trap |
Why rejected |
| Treating this as a #11714 blocker |
#11714 fixed the manifest node it introduced; the config-node risk is adjacent and pre-existing. |
| Bypassing RLS in the daemon |
Would hide a policy mismatch and risk cross-tenant leakage. The safer shape is explicit shared-read metadata on the node that needs daemon visibility. |
Removing resolveTenantContext write gate |
Tenant write authorization and offline read visibility are different concerns. |
| Broad #10011 RLS refactor |
The observed risk is narrow and can be tested on the KB config surface. |
Related
- PR #11714 —
kb-manifest:<tenantId> freshness/RLS review that surfaced this audit.
- #11711 — manifest-orphan reconciliation V1.x increment.
- #11637 — tenant config storage implementation.
- #11640 — reconciliation daemon that consumes tenant config version.
- #11628 — Phase 4 operations/observability epic.
- #10011 — Native Edge Graph RLS substrate.
Origin Session ID
d13c94dd-e721-4e28-ac9e-4d0b3c0f66de
Handoff Retrieval Hints
query_raw_memories({query: 'kb-config visibility team offline reconciliation daemon RLS'})
- PR #11714 Cycle-2 review
PRR_kwDODSospM8AAAABAnZslw
- Author response
IC_kwDODSospM8AAAABDKi56g
- Code anchors:
KnowledgeBaseIngestionService.setTenantConfig, GraphService.isRlsVisible, KbReconciliationService.fetchTenantConfigVersion
Context
Surfaced during PR #11714 Cycle-2 re-review (review
PRR_kwDODSospM8AAAABAnZslw). RA-3 verified thatGraphServiceRLS makesvisibility:'team'load-bearing forkb-manifest:<tenantId>nodes read by the offline KB reconciliation daemon. That same verification exposed an adjacent risk forkb-config:<tenantId>nodes written by #11637 and consumed by #11640.Duplicate sweep before filing:
ask_knowledge_base('kb-config visibility team offline reconciliation daemon getTenantConfig RLS follow-up', type='ticket')surfaced historical RLS tickets (#10010/#10011 family) but no KB config daemon-readability ticket; exactrgoverresources/content/issues/resources/content/discussionsfound RLS history but nokb-configfollow-up.The Problem
KnowledgeBaseIngestionService.setTenantConfig()writeskb-config:<tenantId>viaGraphService.upsertNode()with tenant config properties andversion, but it does not setvisibility:'team'.Verified code path:
GraphService.isRlsVisible()exposes a node whenuserIdis absent, matches the requester,sharedEntityis true, orvisibility === 'team'(ai/services/memory-core/GraphService.mjs:21-34).GraphService.upsertNode()stampsproperties.userIdwhen a request-scoped identity exists andp.userIdis absent (ai/services/memory-core/GraphService.mjs:199-237).GraphService.getNodeRecord()resolves the active requester and returnsnullwhenisRlsVisible()rejects the node (ai/services/memory-core/GraphService.mjs:623-637).setTenantConfig()writeskb-config:<tenantId>withoutvisibility:'team'(ai/services/knowledge-base/KnowledgeBaseIngestionService.mjs:882-904).KbReconciliationService.fetchTenantConfigVersion()reads that config offline throughKnowledgeBaseIngestionService.getTenantConfig()and degrades to version0when no graph config is visible (ai/daemons/KbReconciliationService.mjs:220-226,300-304).If
setTenantConfig()runs under request context, the config node can become owned by that request identity. The offline reconciliation daemon has no request identity, sogetTenantConfig()may fail-soft to the default/yaml tier (version: 0). That would silently weaken or disable #11640 config-staleness detection for the affected tenant.The Architectural Reality
This is not a #11714 merge blocker. PR #11714 correctly marked the new
kb-manifest:<tenantId>node asvisibility:'team'and documented why. This ticket audits the older sibling config node from #11637 under the same verified RLS model.The owning substrate is the KB ingestion service / Native Edge Graph RLS boundary, not the reconciliation diff engine. The fix should preserve tenant write authorization (
resolveTenantContext) while ensuring daemon-owned offline reads observe the same config state that request-authored writes create.The Fix
Audit and, if verified, make
kb-config:<tenantId>daemon-readable without weakening tenant write gates.Likely implementation shape:
visibility:'team'to theKnowledgeBaseTenantConfignode properties written bysetTenantConfig(), mirroring the manifest node rationale from #11714.resolveTenantContext, whilevisibility:'team'is the shared-read marker for offline daemons.getTenantConfig()/ reconciliation path.setTenantConfig()cannot run under request context in production, document that evidence and close as no-code.Contract Ledger
KnowledgeBaseTenantConfiggraph node (kb-config:<tenantId>)setTenantConfig()KbReconciliationService.fetchTenantConfigVersion()0, when graph config exists0only when truly absent/unreadableAcceptance Criteria
setTenantConfig()currently createskb-confignodes invisible to offlinegetTenantConfig().setTenantConfig()marks the config node daemon-readable (likelyvisibility:'team') without bypassingresolveTenantContexttenant-write protection.KnowledgeBaseTenantConfig.versionincrement semantics.Out of Scope
GraphServiceRLS policy.kb-manifest:<tenantId>behavior from #11714.Avoided Traps
resolveTenantContextwrite gateRelated
kb-manifest:<tenantId>freshness/RLS review that surfaced this audit.Origin Session ID
d13c94dd-e721-4e28-ac9e-4d0b3c0f66deHandoff Retrieval Hints
query_raw_memories({query: 'kb-config visibility team offline reconciliation daemon RLS'})PRR_kwDODSospM8AAAABAnZslwIC_kwDODSospM8AAAABDKi56gKnowledgeBaseIngestionService.setTenantConfig,GraphService.isRlsVisible,KbReconciliationService.fetchTenantConfigVersion