Skip to content

Turn the UI-extension registry into ADR-082's build-time frontend extension API #3317

Closed as not planned
8 of 8 issues completed
Closed as not planned

Description

@mstomar125

Goal

Core's desktop frontend exposes the edition-neutral frontend extension API that ADR-082 (proposed) §2 defines.

  • nodespace-sync's Pro frontend plugs into it at build time through virtual:nodespace-extensions.
  • Core never imports, names or branches on it.
  • With nothing injected, every host renders nothing and no hook runs. That is the free app.

After this parent, #3327 (settings surfaces) and #3333 (sync lifecycle, collaboration UI, workspace root) move the Pro UI out by registering it through this API, without editing core's shell.

Context

The code is at core main @ af8ddb61. Locate code by symbol, because line numbers drift.

The registry is Pro-shaped. In packages/desktop-app/src/lib/plugins/:

  • ui-extensions.ts keys every contribution by variant: ProSyncVariant.
  • ui-extensions.svelte.ts filters by resolveProSyncVariant(), imports proSync and labsFlags, and registers the Pro UI through a side-effect import './pro-plugin'.
  • extension-outlet.svelte has no {:catch} branch and no error boundary.

Hard-coded Pro call sites with no extension point:

  • Account, "Add synced database…" and the tenant badge, in settings-sidebar.svelte, settings-pane.svelte and database-settings.svelte.
  • proSync.start() in the onMount of app-shell.svelte.
  • activateProSync in database.svelte.ts, and membership.invalidateForDatabaseSwitch() in evictAndReloadActiveDatabase().
  • boundTenantCollection || ROOT_COLLECTION_ID in the collectionsTree getter of collections.svelte.ts.
  • The proSync block in captureStoreDump() in debug-channel.ts.

How the Pro GUI is built today.

  • nodespace-sync's scripts/build-pro-dmg.sh and .github/workflows/release.yml run bunx tauri build --config src-tauri/tauri.pro.conf.json inside core's packages/desktop-app, at core HEAD.
  • Tauri's beforeBuildCommand (bun run build) inherits that environment.
  • So deleting the side-effect import would silently strip the Pro UI from the next Pro build. That deletion is Stop importing the Pro extension in core; register it only through the build entry #3316, and it waits for the pin gate.
  • Once the Pro app builds from one pinned core revision, core merges first. nodespace-sync then sets NODESPACE_EXTENSIONS in the pin-bump PR that crosses the change (ADR-081 migration step 4, ADR-083 §7).
  • Every other Turn the UI-extension registry into ADR-082's build-time frontend extension API #3317 sub-issue is additive, or preserves behaviour for the Pro build, so none of them needs a gate.

The contract

#3317, #3327, #3333 and their SYNC gates all use this one set of names, which are ADR-082's. Plans or gates that still say NODESPACE_PLUGIN_ENTRY, $lib/plugin-api, lifecycle.activeDatabaseChanged, databaseCachesCleared or hiddenCollectionIds are renamed to these before filing.

Item Name and shape Added in
Build entry virtual:nodespace-extensions, set by NODESPACE_EXTENSIONS=<module path>. Default-exports readonly NodespaceExtension[], which is [] in core. src/routes/+layout.svelte calls registerExtensions(extensions) #3311
Host API src/lib/extension-api/, aliased as @nodespace/extension-api, with /ui and /testing entries #3311 (alias, types), #3315 (services)
Version EXTENSION_API_VERSION = { major: 1, minor: 0 }; each extension declares apiVersion: 1 #3310
Extension NodespaceExtension { id, apiVersion, chrome?, viewerTabs?, settingsSections?, settingsSlots?, collectionTreeRoots?, lifecycle?, debugDump? } #3310, #3312, #3313, #3314
Contribution { id, when?, priority?, load }. Hosts key each one by <extension id>/<contribution id> #3310
Chrome Slots app-shell-overlay and app-shell-modal #3310
Viewer tab { nodeType, label }, props { nodeId } #3310
Settings section { label, after? }, props { navigate } #3312
Settings slots database.actions (no props) and database.row (props { databaseId }) #3312
Collection-tree roots collectionTreeRoots(): readonly string[], unioned across extensions; a throw is treated as empty #3314
Lifecycle lifecycle.start(), which runs only under Tauri and may return a cleanup; lifecycle.onDatabaseActivated(databaseId), which is synchronous, so the extension serializes its own async work; a top-level debugDump() #3313

EXTENSION_API_VERSION stays at 1.0 for the whole of #3317. The compatibility policy and the surface snapshot take effect when #3315 merges. From then on, an additive change bumps the minor version and a breaking change bumps the major version.

Sub-issues, in order

Key Title Size Direct dependencies
#3309 Move the Pro variant machine and DATABASE_SETTINGS_NODE_ID out of the generic registry files M NodeSpaceAI/nodespace-docs#90 (ADR-082), #3299
#3310 Adopt ADR-082's contribution contract: ids, when() predicates, keyed and isolated outlets M #3309
#3311 Add the virtual:nodespace-extensions build entry and the extension-api import alias M #3310
#3312 Add settings-section and database settings-slot extension points M #3310
#3313 Add start, onDatabaseActivated and debugDump extension lifecycle hooks M #3310
#3314 Add the collectionTreeRoots extension point to the sidebar collection tree S #3310
#3315 Publish the extension host API: services, ui and testing entries, version policy M #3311, #3312, #3313, #3314
#3316 Stop importing the Pro extension in core; register it only through the build entry S #3315, SYNC pin gate
D2, C1.1 ──► C3.1a ──► C3.1b ──┬──► C3.2 ──┐
                               ├──► C3.3 ──┤
                               ├──► C3.4 ──┼──► C3.6 ──► C3.5 ◄── SYNC: pin gate
                               └──► C3.7 ──┘

Once #3310 is on main, #3311, #3312, #3313 and #3314 can run in parallel.

Why this order:

Pin gate (#3316 only): SYNC:Pro app and Pro daemon build from one pinned core revision, not core HEAD (ADR-081 step 1).

Hand-offs to other parents

The other plans must match these before filing.

Parent acceptance criteria

Core ships no Pro extension:

  • No module under packages/desktop-app/src/lib or src/routes imports pro-plugin.
  • bun run --cwd packages/desktop-app build emits no Pro extension: the string Collaboration is off for this database is absent from build/.
  • None of proSync, labsFlags.syncEnabled, ProSyncVariant or resolveProSyncVariant appears in any of these files:
    • ui-extensions.ts, ui-extensions.svelte.ts, extension-outlet.svelte, chrome-slot-outlet.svelte;
    • extension-lifecycle.ts, settings-categories.ts;
    • src/lib/extension-api/*.
  • No pro-sync-pill class remains.

Extension points:

  • Every contribution kind and hook in the contract table exists and is documented.
  • Core tests exercise each of them through the fixture extension in src/tests/fixtures/test-extension/, including every failure row of ADR-082 §2.4.

Build entry:

  • virtual:nodespace-extensions is [] in core.
  • NODESPACE_EXTENSIONS=<path> injects that module, together with Tailwind content, dev fs.allow, resolve.dedupe and the Vitest include.
  • A missing path fails the build.
  • Core's release workflow fails when the variable is set.

Host API:

  • @nodespace/extension-api publishes the ADR-082 §2.6 host API, with its compatibility policy and a surface snapshot.
  • No core module imports it.

The free app:

At every step:

Dependencies

Notes

Ratchet. Every PR runs bun run scripts/check-pro-boundary.ts, pastes the printed BASELINES and lowers every marker it pays down. The only raises allowed are relocations under #3299's rule, declared in the PR description.

Coding rules:

  • Code comments cite ADRs, never GitHub issue numbers.
  • Use createLogger, never raw console.*.
  • No lint suppression.
  • Run bun run test, never bun test.
  • Documentation beyond code comments belongs in nodespace-docs (the ADR-082 docs follow-up).

Filing. Core's CLAUDE.md says never to assign an issue at creation. Self-assign at pickup, with bun run gh:assign <N> @me.


Plan key: C3 (pro-boundary DAG).

🤖 Generated with Claude Code

Activity

  1. self-assigned this
    on Sep 30, 2026
  2. added
    pro-boundaryPro/core boundary: Pro code moves to nodespace-sync (ADR-081..085)
    on Sep 30, 2026
  3. mstomar125 commented on Oct 1, 2026

    @mstomar125
    ContributorAuthor

    Superseded by #3414 (umbrella against the final ADR-081–085)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

pro-boundaryPro/core boundary: Pro code moves to nodespace-sync (ADR-081..085)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions