> Fresh agent? You're Track B. You own the cue-list sequence engine, the pad launcher panel, the priority-aware mixer, and the master clock + tap tempo. Track A owns the hardware backends and node graph; you do not touch their files.
30-second context
XOSC is feature-complete for v0.5 except DMX. We're building a v0.6 milestone in two parallel tracks. Track A ships v0.6.0-alpha (graph-only DMX) and v0.6.0-beta (full hardware support). You ship v0.6.0 on top of that.
Working dir: /var/www/xosc. Repo: github.com/saintpetejackboy/xosc-app. The repo is public; you have push access. All workflows are GitHub Actions and run on tag pushes.
Reference implementation: https://github.com/saintpetejackboy/TagTable (private; clone with gh repo clone saintpetejackboy/TagTable /tmp/tagtable). Specifically your reference is:
src/renderer/app.jslines 3637–3879 —startLightingTemplate,lightingMasterTick,stopLightingTemplate,blackout,getTopLightingLayer,applyDimmer. This is the rAF-driven layer engine you're porting + generalizing from RGB-only to multi-role.src/main/config.jslines 437+ —generatePresetTemplates, the 8 built-in cue lists you're seeding.src/main/osc.jsparseTemplateCommand— TagTable's/template/start <name|index>OSC interface, useful as a reference for how external triggers integrate.
Read in this order
1. docs/ideas/dmx/README.md — the umbrella plan + the cross-track contract.
2. docs/ideas/dmx/04-sequence-engine.md — your main spec.
3. docs/ideas/dmx/05-pad-launcher.md — the Pads view UI.
4. docs/ideas/dmx/06-mixer.md — the function that composes layers + graph writes per universe.
5. docs/ideas/dmx/07-clock-and-tempo.md — shared clocks + tap tempo.
6. docs/ideas/dmx/08-logging-and-diag.md — logger source / diagnostics surface.
7. docs/ideas/dmx/09-themes-and-ui.md — theme variables + reusable components.
8. docs/ideas/dmx/10-rollout-plan.md — milestone phasing.
You can skip 01–03 (Track A's hardware specs). The only Track A file you depend on is src/features/dmx/universeBusClient.ts — its write / writeMany / snapshot API is your contract.
Your shippable milestone
- v0.6.0 — full sequence engine, pad launcher, mixer, clocks, tap tempo. Tag
v0.6.0. ~5–7 days.
What you'll write (exhaustive)
src/features/dmx/
├── sequenceEngine.ts # rAF-driven master tick + active layers
├── sequenceOps.ts # start / stop / blackout API
├── sequenceFade.ts # fade math (per role per layer)
├── sequencePresets.ts # 8 built-in sequences (general fixtures)
├── sequenceTypes.ts # Sequence, SequenceCue, SequenceLayer, FadeState
├── mixer.ts # composeFrame — per-frame composition
└── mixerInspector.ts # debug-panel-friendly snapshot
src/features/sequences/
├── PadView.tsx # main grid view
├── Pad.tsx # one pad cell
├── PadEditorModal.tsx # editor modal
├── EffectGenerator.tsx # 4 preset cue fillers (chase, pulse, color cycle, fade rainbow)
├── ClockBar.tsx # topbar widget with BPM + tap + reset per clock
├── padTypes.ts # persisted pad list shape
├── usePads.ts # hook returning pad list + active layers
├── padHotkeys.ts # registers pad hotkeys with the keyboard input layer
├── clocks.ts # SharedClock map + tap tempo
├── useClockSync.ts # external clock source plumbing (MIDI clock — v0.6.1, optional)
├── pad-view.css
├── pad.css
├── pad-editor-modal.css
└── clock-bar.css
src/components/ui/
└── HexInput.tsx # extracted from CustomThemeEditor's swatch+native-color trick (shared with Track A's color usage)
Plus extensions to:
src/store/store.ts—settings.dmx.sequences,settings.dmx.clocks,ui.activeView("graph" | "pads"), pad list shape.src/components/Topbar.tsx— Pads / Graph toggle, Clock bar slot.src/App.tsx— render<PadView />vs<GraphCanvas />based onui.activeView.src/features/inputs/keyboard.ts— pad hotkey signatures (delegated throughpadHotkeys.ts).CHANGELOG.md— one v0.6.0 block.docs/ideas/next-session-handoff.md— update at the end.
The contract you depend on (Track A publishes)
// src/features/dmx/universeBusClient.ts
export const universeBus = {
write(universeId: string, channel: number, value: number, source: WriteSource): void;
writeMany(universeId: string, writes: Array<[ch: number, val: number]>, source: WriteSource): void;
snapshot(universeId: string): Uint8Array;
};
interface WriteSource {
kind: "graph" | "pad" | "art-net-in";
nodeId?: string;
priority?: number;
}
Your sequence engine calls writeMany once per rAF tick per active layer. Your mixer composes intents before writing — the mixer replaces Track A's last-write-wins placeholder. After your mixer ships, the call shape doesn't change; the internal composition does.
Pad-to-graph integration (the headline feature)
Pads must appear as virtual sources/sinks on the input bus so the graph can wire to them:
- A pad firing emits a synthetic input event with signature
pad:<padId>:hotkey. Same shape as keyboard, MIDI, OSC events; the existing router handles it without modification. - A cue that has
emit: { signature, value? }fires that exact event when the cue lands. Lets a "bass drop" cue trigger an OSC packet at the right beat. - A pad's hotkey can come from any source — keyboard, MIDI, OSC, gamepad — by binding an input node to the
pad:<padId>:hotkeysignature.
Don't do
- Don't write any of
electron/dmx/*. That's Track A. - Don't ship beat-locked playback in v0.6.0. Tap-tempo gives you a BPM number you display. Layered against beats is a v0.6.1 polish.
- Don't ship MIDI / OSC clock sync in v0.6.0 unless Track A is fully stalled and you have nothing else to do.
- Don't replace
universeBus's function signatures. You replace the composition rules; Track A's clients keep working. - Don't put the sequence engine in main. Renderer-side rAF is the right call (TagTable does this, and it lets the pad UI be reactive without IPC).
Operational
npm run typecheck— both projects.npx electron-vite build— Electron + renderer.cd web && npm run build— registers any new docs pages.bash scripts/bump-version.sh patch|minor|major.gh run list --limit 5— check workflows after a tag push.- Live site:
https://xosc.gamingworld.uk.
Sanity checks before claiming v0.6.0 done
- [ ]
npm run typecheckclean. - [ ]
npx electron-vite buildclean. - [ ]
cd web && npm run buildregisters all new docs pages. - [ ] CHANGELOG block written.
- [ ]
bump-version.shrun. - [ ] At least 8 built-in sequences seed on first run.
- [ ] Tap tempo + named clocks tested.
- [ ] Pad hotkeys round-trip (keyboard fires pad; pad fires synthetic event back into bus).
- [ ] Mixer respects slot exclusivity AND mix modes (verify with two layers writing the same channel).
- [ ] Graph + pad writing the same universe — graph-write wins (this is intentional;
06-mixer.mdcovers it). - [ ] Tag
v0.6.0and push.
If Track A isn't done yet
If Track A's universeBus doesn't exist, don't stub it yourself. Coordinate with the user. The pad UI can be built against a stub, but the engine has nowhere to write until Track A ships. The right move is to update docs/ideas/next-session-handoff.md saying you're blocked on the alpha and pick a different task (docs sweep, multi-window canvas if user yes).