3.7 KiB
3.7 KiB
Cosmos Query Review
Date: 2026-05-05
This review covers current NoteLett repository access patterns against the Cosmos container registrations in backend/src/lib/cosmos-init.ts. All documents retain productId: "notelett" and all list routes include product/user/workspace scope filters before returning user data.
Container Partitions
| Container | Partition key | Primary access pattern |
|---|---|---|
notes, note_relationships, note_tasks, note_artifacts, note_agent_actions, note_shares, note_versions |
/workspaceId |
Workspace-scoped note surfaces and agent write audit trail |
workspaces, saved_views, note_prompts, note_prompt_schedules, note_prompt_webhooks, note_intake_rules, note_intake_jobs |
/userId |
User-owned configuration, templates, and intake state |
note_collaborators |
/sharedWithUserId |
Shared-with-me lookup; owner-side collaborator listing is note-scoped |
palace_* |
/userId |
Per-user memory palace data and maintenance operations |
Cross-Partition Or Count-Heavy Operations
| Operation | Why it can cross partitions | Guardrails |
|---|---|---|
notes.listNotes() without workspaceId |
notes is partitioned by /workspaceId, but dashboard/search surfaces can list across a user's workspaces. |
Always filters by userId and productId; paginated to caller limit. Workspace-specific calls include workspaceId. |
note_agent_actions.listPendingActions() |
Pending review queue intentionally spans workspaces in the /workspaceId container. |
Filters by userId, productId, and state; performs two count queries plus two bounded reads. |
note_shares.findShareByToken() |
Public share-token resolution starts from token/product instead of workspace. | Filters by shareToken and productId, returns one record, and rejects expired shares before public note lookup revalidates note/user/product scope. |
note_collaborators.listCollaboratorsForNote() |
The container is optimized for /sharedWithUserId; owner-side note collaborator listing starts from noteId. |
Filters by noteId and productId, limits to 100; route verifies the caller can access the note before listing. |
palace search/maintenance scans |
Palace data is /userId partitioned, but some relevance, duplicate, KG, and maintenance jobs scan hundreds to thousands of user memories/triples. |
All scans include userId and productId; limits are explicit (500, 1000, or 5000) and remain single-user partition reads. |
palace.getPalaceStats() |
Counts six Palace containers for a user. | Counts are all scoped by userId and productId; suitable for diagnostics/dashboard use. |
| Prompt template builtin merge | User templates and builtins live in separate /userId partitions. |
User partition and __builtin__ partition are queried separately, then merged in memory with limit bounds. |
Regression Coverage
backend/src/modules/repository-scope.test.ts verifies scope isolation for:
- Notes list and workspace counts across user, product, and workspace boundaries.
- Agent action workspace lists and cross-workspace pending queues across user and product boundaries.
- Share token, share list, note collaborator, and shared-with-me lookups across product, user, note, and workspace filters.
Follow-Up Notes
- If dashboard global note search becomes high traffic, prefer a user-partitioned materialized search/index container or a dedicated search service over broad
/workspaceIdfan-out. - If pending review volume grows, consider a user-partitioned projection for
draft/proposedagent actions to avoid two cross-workspace counts per queue load. - Public share tokens should remain high-entropy and globally unique; token lookup is intentionally cross-partition but bounded to one item.