Context
A cloud-deployment empty-KB investigation exposed a resolver blind spot in the server-side pull path. The new deployment diagnostics are the right direction, but they can only report what TenantRepoSyncService.resolveTenantReposConfig() can see. Today that path delegates to KnowledgeBaseIngestionService.listConfiguredTenantRepos(), and that resolver only enumerates tenant IDs from keyed tiers: kb-config.yaml tenant keys plus aiConfig.tenantRepos[] defaults.
The current source explicitly records the limitation:
ai/services/knowledge-base/IngestionService.mjs documents that a tenant configured only through a KnowledgeBaseTenantConfig graph node is not enumerated.
test/playwright/unit/ai/services/knowledge-base/IngestionService.spec.mjs pins that behavior with the graph-only tenant exclusion case.
- Cloud configuration docs describe
KnowledgeBaseTenantConfig graph nodes as a live tier for tenant config, including tenantRepos.
Live latest-open sweep: checked the latest 20 open issues on 2026-07-01; no equivalent open ticket found. Duplicate sweep also checked graph-only tenant config tenantRepos listConfiguredTenantRepos kb-config; closest historical issue is closed #12145, which established the current tiered resolver shape. A2A in-flight sweep checked the latest 30 messages; no overlapping [lane-claim] or [lane-intent] was present.
The Problem
If a deployment stores tenantRepos only in KnowledgeBaseTenantConfig graph nodes, pull-mode sync can report zero configured repos even though repo config exists in the graph. That creates a bad operator diagnostic path:
- KB can be healthy and empty.
- Ingestion can be idle with no last run.
- Deployment state can say there are no configured tenant repos because the pull-mode resolver never discovered graph-only tenant IDs.
- Operators then chase scheduler, Git access, embedding, or Chroma problems before the real problem: the resolver's tenant discovery boundary.
This is especially sharp because setTenantConfig() already persists tenantRepos into KnowledgeBaseTenantConfig and the docs present that graph node as canonical runtime config. The old out-of-scope assumption no longer reads as a safe permanent limitation.
The Architectural Reality
TenantRepoSyncService.resolveTenantReposConfig() uses KnowledgeBaseIngestionService.listConfiguredTenantRepos(); it does not discover tenants itself.
listConfiguredTenantRepos() chooses the highest present tier per tenant: graph node > YAML bootstrap > aiConfig fallback.
- The tier ordering is sound, but the tenant-ID seed set is incomplete for graph-only config because it is derived only from YAML keys and
aiConfig.tenantRepos[] entries.
- A naive raw
kb-config:* graph scan is risky: the resolver comments intentionally cite RLS and avoiding raw node scans. The fix needs a sanctioned tenant-config discovery surface, not an ad hoc query that bypasses access boundaries.
- Deployment diagnostics added by #14396 can only classify what this resolver returns; they should distinguish true no-config from graph-only config that cannot currently be enumerated, once the resolver exposes the distinction.
The Fix
Add an ADR-compatible, RLS-respecting way for pull-mode sync to enumerate graph-backed tenant config nodes when no YAML/default tier names the tenant.
Likely shape:
- Add a
KnowledgeBaseIngestionService/GraphService helper that lists KnowledgeBaseTenantConfig identities through an allowlisted, visibility-aware graph query or maintained tenant-config index.
- Update
listConfiguredTenantRepos() to include graph-only tenant config nodes in the tenant set without weakening the existing per-tenant winner semantics.
- Preserve
tenantRepos: [] as an explicit higher-tier disable for that tenant.
- Update deployment diagnostics so
tenantRepoSync.config can distinguish true no-configured-repos from graph-config discovery errors.
- Replace or narrow the current graph-only exclusion test with coverage for graph-only discovery and RLS-safe failure behavior.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
KnowledgeBaseIngestionService.listConfiguredTenantRepos() |
ai/services/knowledge-base/IngestionService.mjs |
Enumerates effective tenantRepos for YAML, aiConfig, and graph-only tenant config nodes |
If graph discovery fails, return a bounded diagnostic error instead of silently treating the deployment as empty |
learn/agentos/cloud-deployment/Configuration.md, TenantIngestionModel.md, Troubleshooting.md |
Unit tests for graph-only config, explicit empty config, malformed config, and discovery failure |
tenantRepoSync deployment diagnostics |
DeploymentStateBridgeService.collectTenantRepoSyncSnapshot() |
Reports config state based on the expanded resolver and distinguishes true empty config from discovery degradation |
status: degraded with stable reason code when discovery fails |
learn/agentos/cloud-deployment/Troubleshooting.md |
Deployment-state unit test covering graph-discovery failure summary |
Decision Record impact
Aligned with ADR 0014 and ADR 0022. The lane remains a distinct cloud-deployable, heavy pull-ingestion task; this ticket fixes the config-discovery substrate feeding that lane. No ADR amendment is expected unless the RLS-safe graph discovery shape requires a new graph-query contract.
Acceptance Criteria
Out of Scope
- Changing the GitMirror credential boundary.
- Changing tenant repo normalization semantics.
- Re-pointing
kbSync; pull-mode remains tenant-repo-sync.
- Adding a new tenant-config write tool unless the discovery fix proves that such a tool is the missing sanctioned substrate.
Related
- #12145 — closed resolver ticket that established the current keyed-tier behavior.
- #14396 / PR #14398 — tenant-repo-sync deployment diagnostics.
- #14402 / PR #14403 — source error-code provenance for GitMirror failures.
learn/agentos/cloud-deployment/Configuration.md
learn/agentos/cloud-deployment/TenantIngestionModel.md
Retrieval Hint: "graph-only tenantRepos listConfiguredTenantRepos KnowledgeBaseTenantConfig resolver no configured repos"
Context
A cloud-deployment empty-KB investigation exposed a resolver blind spot in the server-side pull path. The new deployment diagnostics are the right direction, but they can only report what
TenantRepoSyncService.resolveTenantReposConfig()can see. Today that path delegates toKnowledgeBaseIngestionService.listConfiguredTenantRepos(), and that resolver only enumerates tenant IDs from keyed tiers:kb-config.yamltenant keys plusaiConfig.tenantRepos[]defaults.The current source explicitly records the limitation:
ai/services/knowledge-base/IngestionService.mjsdocuments that a tenant configured only through aKnowledgeBaseTenantConfiggraph node is not enumerated.test/playwright/unit/ai/services/knowledge-base/IngestionService.spec.mjspins that behavior with the graph-only tenant exclusion case.KnowledgeBaseTenantConfiggraph nodes as a live tier for tenant config, includingtenantRepos.Live latest-open sweep: checked the latest 20 open issues on 2026-07-01; no equivalent open ticket found. Duplicate sweep also checked
graph-only tenant config tenantRepos listConfiguredTenantRepos kb-config; closest historical issue is closed #12145, which established the current tiered resolver shape. A2A in-flight sweep checked the latest 30 messages; no overlapping[lane-claim]or[lane-intent]was present.The Problem
If a deployment stores
tenantReposonly inKnowledgeBaseTenantConfiggraph nodes, pull-mode sync can report zero configured repos even though repo config exists in the graph. That creates a bad operator diagnostic path:This is especially sharp because
setTenantConfig()already persiststenantReposintoKnowledgeBaseTenantConfigand the docs present that graph node as canonical runtime config. The old out-of-scope assumption no longer reads as a safe permanent limitation.The Architectural Reality
TenantRepoSyncService.resolveTenantReposConfig()usesKnowledgeBaseIngestionService.listConfiguredTenantRepos(); it does not discover tenants itself.listConfiguredTenantRepos()chooses the highest present tier per tenant: graph node > YAML bootstrap >aiConfigfallback.aiConfig.tenantRepos[]entries.kb-config:*graph scan is risky: the resolver comments intentionally cite RLS and avoiding raw node scans. The fix needs a sanctioned tenant-config discovery surface, not an ad hoc query that bypasses access boundaries.The Fix
Add an ADR-compatible, RLS-respecting way for pull-mode sync to enumerate graph-backed tenant config nodes when no YAML/default tier names the tenant.
Likely shape:
KnowledgeBaseIngestionService/GraphServicehelper that listsKnowledgeBaseTenantConfigidentities through an allowlisted, visibility-aware graph query or maintained tenant-config index.listConfiguredTenantRepos()to include graph-only tenant config nodes in the tenant set without weakening the existing per-tenant winner semantics.tenantRepos: []as an explicit higher-tier disable for that tenant.tenantRepoSync.configcan distinguish trueno-configured-reposfrom graph-config discovery errors.Contract Ledger Matrix
KnowledgeBaseIngestionService.listConfiguredTenantRepos()ai/services/knowledge-base/IngestionService.mjstenantReposfor YAML, aiConfig, and graph-only tenant config nodeslearn/agentos/cloud-deployment/Configuration.md,TenantIngestionModel.md,Troubleshooting.mdtenantRepoSyncdeployment diagnosticsDeploymentStateBridgeService.collectTenantRepoSyncSnapshot()status: degradedwith stable reason code when discovery failslearn/agentos/cloud-deployment/Troubleshooting.mdDecision Record impact
Aligned with ADR 0014 and ADR 0022. The lane remains a distinct cloud-deployable, heavy pull-ingestion task; this ticket fixes the config-discovery substrate feeding that lane. No ADR amendment is expected unless the RLS-safe graph discovery shape requires a new graph-query contract.
Acceptance Criteria
listConfiguredTenantRepos()includes tenant config nodes that exist only in the graph tier.aiConfig, winner chosen by tier presence, not array length.tenantRepos: []still explicitly disables pull-mode for that tenant.Out of Scope
kbSync; pull-mode remainstenant-repo-sync.Related
learn/agentos/cloud-deployment/Configuration.mdlearn/agentos/cloud-deployment/TenantIngestionModel.mdRetrieval Hint: "graph-only tenantRepos listConfiguredTenantRepos KnowledgeBaseTenantConfig resolver no configured repos"