Skip to content

@thoremin/sdk-ui: the app half of the extension SDK, by dependency inversion (ADR follow-up c, part 2) - #300

Merged
thorwhalen merged 3 commits into
mainfrom
adr/sdk-ui-package
Oct 5, 2026
Merged

thorwhalen merged 3 commits into
mainfrom
adr/sdk-ui-package

Conversation

@thorwhalen

@thorwhalen thorwhalen commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Follow-up (c), part 2 of the instruments-as-graphs ADR (#268): the app half of the extension SDK becomes a workspace package, @thoremin/sdk-ui, by dependency inversion. After this, the air extension imports only packages and its own files. Part 3 moves it into packages/ext-air.

Moved whole (no app dependency)

These moved into packages/sdk-ui, and core imports them from there too (one instance each):

  • primitives, the extension UI types
  • namedCollection (the zodal facade), featureDemand (the app's registry)
  • enroll/{sequenceStore,guidance,click}. TranscriptLine moved beside the guidance sinks, so they no longer reach into the trainer's store.
  • slugId went to @thoremin/sdk/util/ids.

Seams (the app itself)

Seam Extension calls The app installs it in
controls() get, setTransient (refuses an undeclared field), setHush, subscribe; controlsStore<S>() for injectable sync loops src/app/store.ts
dials dispatchDialSet/SetIn/Patch, the same names the write-path guard reads src/app/dispatchDial.ts
dials form useDialsSettings src/app/dials/useDialsSettings.ts

Seams resolve when called, never at import. Calling one before the app has provided it throws, naming the module that provides it.

Guards

  • packages_purity covers sdk-ui: React is a peer dependency, and @zodal/* and @thoremin/sdk are declared.
  • The boundary guard's SDK_APP list is gone. An extension imports packages plus itself, and its pure side never imports @thoremin/sdk-ui.
  • The air trainer component tests (pattern, sequence, chord enrolment) now run only when air is listed. This is a coverage change in the core-alone run: they used to pass there because the store's setter accepted any field. Now the host's setTransient refuses airGuitarModel / airDrumPattern when air is not listed, which is correct. The full run still covers them.
  • After review: an extension reads the controls through controlsFor<typeof ITS_MANIFEST>() (here airControls). Its dials and transient fields are typed from its own manifest, so a typo'd key or a wrong value is a type error again, as it was against the app's store. A type test pins this.

Verified

  • npm run typecheck (now including sdk-ui's React-free files), typecheck:core, npm test (2441 passed), npm run build, npm run test:core (2334 passed / 87 skipped, bundle check clean): all green.

Refs #268.

…dependency inversion (ADR follow-up c, part 2)

- moved whole into packages/sdk-ui (no app dependency): panel primitives, ExtensionUi types,
  the named-collection facade, the feature-demand registry, the trainer's sequence store,
  guidance sinks (TranscriptLine moved beside them) and click player; slugId to
  @thoremin/sdk/util/ids. Core imports them from the package too (one instance each)
- the three that ARE the app become seams: controls() (the hot store), dispatchDialSet/
  SetIn/Patch (the command write path) and useDialsSettings (the dials form), resolved at
  call time to what src/app/store.ts, dispatchDial.ts and dials/useDialsSettings.ts install
  as they load; setTransient refuses a field no listed extension declares
- the extension now imports packages and itself only; the boundary guard's SDK_APP list is
  gone, and the pure side may never import sdk-ui
- the air trainer component tests run only with the air extension listed
…ontrolsFor<typeof AIR_EXTENSION>()), so a typo'd key or wrong value is a type error again; pinned by a type test
@thorwhalen
thorwhalen merged commit 957f0e4 into main Oct 5, 2026
4 checks passed
@thorwhalen
thorwhalen deleted the adr/sdk-ui-package branch October 5, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant