Repository navigation
Add the collectionTreeRoots extension point to the sidebar collection tree #3314
Description
Summary
Add the collection-tree roots extension point from ADR-082 §2.2 (proposed):
- An extension names collections that the sidebar tree treats as invisible containers.
- Core unions the roots from every extension with its own current root.
- With no extension registered, the tree is unchanged.
This lets nodespace-sync contribute the Pro workspace root before #3330 deletes core's own root. #3330's SYNC gate can therefore be met before #3330 merges.
Context
src/lib/stores/collections.svelte.ts:
- L6 imports
databaseStore. - The
collectionsTreegetter (L147-170) hidesdatabaseStore.activeDatabase?.boundTenantCollection || ROOT_COLLECTION_ID, which it passes as the 4th argument tobuildCollectionsTree. ROOT_COLLECTION_ID(L564-578) is the legacy workspace root.buildCollectionsTree(L580-660):- its 4th parameter is
rootCollectionId: string = ROOT_COLLECTION_ID; - it drops that id as a display parent (L623) and as a top-level row (L651-652).
- its 4th parameter is
src/tests/stores/collections.test.ts:
- L10-11 import
buildCollectionsTreeandROOT_COLLECTION_ID. - The describe 'buildCollectionsTree dynamic bound-root filtering' (L638) passes a string 4th argument at L658, L677, L700 and L740, and relies on the default at L721.
collectionsData._setTestDatadrivescollectionsTree.
ADR rules:
- ADR-082 §2.2: a root is a collection the tree does not show. A collection whose only parent is a root is shown at the top level. The host takes the union of every extension's list.
- ADR-082 §2.4: a
collectionTreeRoots()that throws is treated as empty and logged once. - ADR-083 §2 moves the Pro root out of core (Remove the tenant-binding root fallback from the collections tree #3330).
Scope
1. Contract and registry, in ui-extensions.ts
Contract. Add NodespaceExtension.collectionTreeRoots?: () => readonly string[]. Its doc says:
- it is presentation only;
- it is evaluated inside a derivation, so it must read only reactive sources and have no side effects (ADR-049).
Registry method. Add collectionTreeRoots(): ReadonlySet<string>:
- It returns the union of every extension's roots, in registration order.
- A contributor that throws counts as empty. It is logged with
log.warnonce, and not again until it next returns normally. - Non-string entries are ignored.
Why the plain registry, not the reactive wrapper:
collections.svelte.tsthen does not importui-extensions.svelte.ts, which until Stop importing the Pro extension in core; register it only through the build entry #3316 still carries the transitional Pro import.- The method is still reactive: when the caller is a derivation, the extension's function reads reactive state.
2. collections.svelte.ts
-
Change
buildCollectionsTreeto:buildCollectionsTree( collections, locallyCreatedIds, pendingIds, rootCollectionIds: ReadonlySet<string> = new Set([ROOT_COLLECTION_ID]) )
- Filter parents with
!rootCollectionIds.has(p). - Filter top-level rows with
!rootCollectionIds.has(c.id). - Update the doc.
- Filter parents with
-
In the
collectionsTreegetter, passnew Set([boundRoot || ROOT_COLLECTION_ID, ...uiExtensionRegistry.collectionTreeRoots()]). Add a comment: extension roots are unioned with core's current root, and core's root leaves with ADR-083 §2. -
Keep
ROOT_COLLECTION_ID, theboundTenantCollectionread and thedatabaseStoreimport. Remove the tenant-binding root fallback from the collections tree #3330 removes them, and makes the default set empty.
3. Fixture
Add a collectionTreeRoots option: createTestExtension({ collectionTreeRoots }).
4. Pro-boundary ratchet
Paste the printed BASELINES. No increase is expected.
Out of scope
- Removing core's own root,
ROOT_COLLECTION_IDor theboundTenantCollectionread (Remove the tenant-binding root fallback from the collections tree #3330). - The
DatabaseInfofields (Move Pro-owned data out of core's data model (ADR-083) #3345).
Steps
- Start up as core's
CLAUDE.mddescribes: a worktree namedissue-<N>-collection-tree-roots, and self-assign at pickup. - Add the registry method, with its tests.
- Change the
buildCollectionsTreesignature and thecollectionsTreegetter. - Update and extend
collections.test.ts. Then run the full frontend suite, because the store's import graph changes.
Acceptance criteria
Registry:
-
uiExtensionRegistry.collectionTreeRoots()returns the union of every extension's roots. - It returns an empty set when no extension contributes.
- It survives a throwing contributor, and logs the throw once.
The tree:
-
buildCollectionsTreehides every id in its root set. Its default set is{ROOT_COLLECTION_ID}, so existing callers are unchanged. -
collectionsData.collectionsTreehides both core's current root and every extension root. - With no extension registered, the tree is unchanged.
Checks:
-
bun run test(the full frontend suite),bun run test:changedandtest:free-userspass, andquality:fixis clean. -
bun run scripts/check-pro-boundary.tsexits 0, andBASELINESequal the printed counts.
Tests and verification
Changes to collections.test.ts:
- The four explicit 4th arguments become
new Set([...]). - New cases for
buildCollectionsTree:- when the set holds two roots, both are hidden;
- with an empty set, a container shows as an ordinary collection, with its member collections nested under it.
- A new describe, 'collectionsTree unions extension collection-tree roots'. With the fixture contributing
['ext-root']:ext-rootis hidden, and its members are shown at the top level;- core's root stays hidden;
- a throwing contributor leaves the tree intact;
- unregistering the fixture restores the previous tree.
Changes to src/tests/plugins/ui-extensions.test.ts: union order, throw safety, and the empty case.
Commands:
bun run --cwd packages/desktop-app test src/tests/stores/collections.test.ts src/tests/plugins
bun run test
bun run test:changed
bun run --cwd packages/desktop-app quality:fix
bun run scripts/check-pro-boundary.ts
Dependencies
- Adopt ADR-082's contribution contract: ids, when() predicates, keyed and isolated outlets #3310: the registry and the fixture.
Risks and rollback
- Import graph. The store gains an import of
ui-extensions.ts, which itself imports only the logger. - Rollback. Revert the PR.
Plan key: C3.7 (pro-boundary DAG).
🤖 Generated with Claude Code
Activity
Add the collectionTreeRoots extension point to the sidebar collection…