> Fresh agent? Start here. v0.6.0 (DMX) just landed on feat/dmx-track-b (Track A + B merged into one branch). This file is rewritten between sessions; nothing here is older than the last session.
30-second context
XOSC is an Electron + React + Zustand + ReactFlow app. Source nodes (USB inputs / Resolume parameter feedback / inbound OSC) wire on a graph canvas to sink nodes (OSC out / Resolume WS out / monitor) with optional transform nodes (clamp / scale / hold-timer / latch / etc.) in the middle. Loop protection is real: depth-cap at 8 hops + Resolume echo guard. No phone-home, loopback default, Windows 11 first.
Working dir: /var/www/xosc. Repo: github.com/saintpetejackboy/xosc-app. Site: https://xosc.gamingworld.uk (lives in web/dist, served by Apache on this same box).
Current HEAD: v0.6.0 in flight on feat/dmx-track-b. Two parallel agents (Track A: hardware + dmx-out/dmx-in nodes; Track B: sequences + pads + mixer) shipped onto the same branch. typecheck clean, npx electron-vite build clean, cd web && npm run build clean. Not yet tagged. v0.5.4 was the last released line and is feature-complete on main.
What's at HEAD that you might not expect
These shipped in v0.5.x so the older architecture-limits and dispatch-semantics docs are slightly stale:
- Multi-hop dispatch. Router walks
source → [transform …] → sink.MAX_DISPATCH_DEPTH = 8bounds cycles. transformnodes (mid-graph, both sink + source). Ops: clamp, scale, invert, curve, hold-timer (tap/hold split), double-tap, threshold (high/low hysteresis), latch, edge (rise/fall/both). State per(transformNodeId, sourceId)insrc/features/mapping/transformOps.ts.osc-insource nodes.electron/osc-listener.ts+src/features/inputs/useOscInputs.ts. Refcountednode-oscServer per(bindHost, port). Address patterns supportand?. Default loopback; LAN-visible is opt-in.monitorsink node. Visual-only. Side-channelsrc/features/mapping/monitorBus.tskeeps 60 Hz traffic out of Zustand. Sparkline + last 6 raw samples. Refresh throttled to ~30 Hz.- Per-edge dispatch modes.
MappingEdge.dispatch?: { mode: parallel|sequential|after-prev, order, delayMs?, requirePrevOk? }. Sequential / after-prev await each previous edge. - Per-edge throttle override.
MappingEdge.throttleMs?overrides engine default (25 ms) on a single wire. - Toggle-state cleanup on rebind is automatic.
useMappingRouterfingerprints each sink's incoming-edge set + triggerMode and drops thetoggleStateentry when the fingerprint shifts. - Resolume echo guard is on by default per
resolume-innode. Disable in the node inspector if you want the round-trip back. - Save debug log to file lives in DebugPanel next to Copy. JSONL.
- Cycle warning (v0.5.2). Static-time detection of feedback loops, including loops that close through Resolume parameter equality. Edges in a cycle render amber +
⟳ looppill. Non-blocking — runtime depth cap and echo guard still rule. - Per-sink event-rate gauge (v0.5.2). OSC-out and Resolume-out nodes show
events/spill viasinkRateBusside-channel. Amber > 60/s, red > 200/s. Hidden at 0. - Resolume swagger autocomplete (v0.5.2). Composition refresh also pulls
/api/v1/docs/swagger.jsonand unions paths into the picker so dynamic / effect params autocomplete even before Resolume materializes them in/composition. - Global hotkeys (v0.5.2). Per-keyboard-input Toggle in the inspector. When on, that key is registered via
electron.globalShortcutand works while XOSC is unfocused. globalShortcut has no native key-up; XOSC synthesizes a release after 120 ms. - Custom theme editor (v0.5.3). Six-seed editor under Settings. Pick bg / surface / accent / accent-2 / text / border; the rest of the 18 vars are derived via chroma-js.
Theme.seeds?carries the chosen seeds back so the editor re-hydrates. Live-applies on every change. Seesrc/features/settings/CustomThemeEditor.tsxandsrc/theme/buildCustomTheme.ts. - mDNS host picker (v0.5.3). Main runs
bonjour-servicebrowsers for_resolume._tcp,_osc._udp, and_companion._tcp. Renderer-sideusePeers()(out-of-Zustand, viauseSyncExternalStore) feeds aHostPickerdropdown attached to ResolumeOut / ResolumeIn / OSC Out inspectors. Selecting a peer fills both host and port. Advertise is off — listening only. - Serial output node (v0.5.4). New
serial-outsink kind. Payload template with{v}/{V}/{s}placeholders + line ending. Shares the open port with a serial-input node on the same path (one bridge handle, both directions). New IPCserial:write. Seesrc/features/mapping/SerialOutNode.tsxandsrc/features/inspector/SerialOutInspector.tsx. - Wires breathe on traffic (v0.5.4). New
edgeFireBusemits per-edge dispatch signals;ColorMixEdgere-mounts a sibling halo path keyed on a pulse counter so its CSS keyframe restarts each fire. Single press → single pulse; 60 Hz axis traffic → continuous halo. - Serial parser controls (v0.5.4).
SerialBindUInow has Raw-bytes toggle + Delimiter field (escape syntax decoded on open).
Where things live (verify if changed)
| Concern | File |
|---|---|
| Custom theme builder | src/theme/buildCustomTheme.ts |
| Custom theme editor UI | src/features/settings/CustomThemeEditor.tsx |
| mDNS browser (main) | electron/peer-discovery.ts |
| Renderer peer bus | src/features/discovery/peerBus.ts |
| Host picker dropdown | src/components/ui/HostPicker.tsx |
| Serial-out reconciler | src/features/inputs/useSerialOutputs.ts |
| Serial-out node + inspector | src/features/mapping/SerialOutNode.tsx, src/features/inspector/SerialOutInspector.tsx |
| Edge fire bus | src/features/mapping/edgeFireBus.ts |
| Event bus | src/features/inputs/inputBus.ts |
| Dispatch tracker (lineage + echo guard) | src/features/inputs/dispatchTracker.ts |
| Router | src/features/mapping/useMappingRouter.ts |
| Transform op evaluator | src/features/mapping/transformOps.ts |
| Monitor side-channel | src/features/mapping/monitorBus.ts |
| Sink-rate side-channel | src/features/mapping/sinkRateBus.ts |
| Cycle detector | src/store/cycleDetect.ts |
| Global hotkey policy (renderer) | src/features/inputs/useGlobalHotkeys.ts |
| Global hotkey reconciler (main) | electron/global-hotkeys.ts |
| OSC inbound bridge (main) | electron/osc-listener.ts |
| OSC inbound reconciler (renderer) | src/features/inputs/useOscInputs.ts |
| Resolume WS bridge | electron/resolume-bridge.ts |
| OSC engine | electron/osc-engine.ts |
| Resolume subscriptions | src/features/inputs/useResolumeSubscriptions.ts |
| Resolume composition + swagger | src/features/inputs/useResolumeComposition.ts |
| Store | src/store/store.ts |
| Canvas | src/features/mapping/GraphCanvas.tsx |
| Inspector dispatcher | src/features/inspector/Inspector.tsx |
| Edge inspector | src/features/inspector/EdgeInspector.tsx |
What v0.5 was about (shipped — backlog items 1–15 + v0.5.4 polish)
See docs/ideas/v0.5-backlog.md for the canonical list. Everything except #16 (DMX) is shipped. v0.5.4 also wrapped three loose ends from competitor-gap.md: serial-out node, wire-breathes-on-fire, and the serial parser UI controls.
Remaining open:
- #16 DMX integration (XL) — planned in
docs/ideas/dmx/. The originaldmx-feasibility.mdis superseded by the dmx/ folder, which contains the full v0.6 plan split into two parallel tracks (Track A: hardware + graph nodes; Track B: sequences + pad launcher + mixer). Each track has a sealed handoff (HANDOFF-A-hardware.md,HANDOFF-B-sequences.md) so two agents can work in parallel. User has explicitly green-lit DMX as the v0.6 milestone.
Suggested next session
The v0.5 line is feature-complete except DMX. If the user wants to keep moving without DMX:
- Multi-window canvas — open a second window with the same graph (in the "structural calls" list of v0.5-backlog.md). Useful when monitoring on a control surface laptop. Needs user yes/no first.
- Plugin system / community node types — premature; the backlog says revisit at v0.7.
- Docs sweep —
docs/inputs/anddocs/mapping/are mostly current after v0.5.4 but a fresh pass would help.
Important user decisions (stable)
- No portable / zero-install build. User explicitly declined.
- No glossy / neon visual redesign. v0.4's look stays.
- Footer attribution is "Meiux Meiux LLC". Peak Technologies appears only in NSIS installer metadata.
- DMX is parked until user re-prioritises.
- No default browser inputs — every UI control lives in
src/components/ui/. - *Tagging
v..* triggers GitHub Actions Windows installer build + siteversion.jsonupload. Don't tag without user consent.
Operational
npm run typecheck— both sides (node + web).npx electron-vite build— builds Electron renderer + main.cd web && npm run build— rebuilds the site (web/dist/, served by Apache).bash scripts/bump-version.sh minor|patch|major— bumps VERSION + both package.jsons.bash scripts/build-icons.sh— regeneratesbuild/icon.{png,ico,svg}fromsrc/assets/xosc-mark.webp.gh run list --limit 5— check GitHub Actions after a tag push.- The home-page download pill version comes from
/var/www/xosc/downloads/version.json, written by the deploy job after a successful Windows installer build. The/changelog/page readsCHANGELOG.mddirectly so it updates the moment the site rebuilds.
Sanity checks before claiming a v0.5.x patch done
- [ ]
npm run typecheckclean (both projects). - [ ]
npx electron-vite buildclean. - [ ]
cd web && npm run buildregisters any new docs pages. - [ ] CHANGELOG
## v0.5.xblock written;bump-version.sh patchrun. - [ ] Commit message describes the why; tag
v0.5.x; push commit + tag. - [ ]
https://xosc.gamingworld.uk/changelog/shows the new section after the deploy workflow finishes.
If the user is asleep
1. Don't take destructive actions (force-push, drop branches, delete uncommitted work).
2. Don't push to main without confirmation except when wrapping a clean release the user previously asked for.
3. Don't tag v.. without explicit go — that triggers a Windows installer build.
4. Default to "draft a plan + the smallest reversible commit" if blocked.
5. Update this file at end of session so the next agent has fresh context.
Session log
2026-04-29 — v0.6.0 DMX milestone landed (parallel-track session)
Two agents worked the same repo concurrently against sealed handoffs in docs/ideas/dmx/:
Track A — hardware + graph nodes (other agent).
electron/dmx/{engine,transmitter,universeBus,diag,artnetIn}.tspluselectron/dmx/transmitters/{artNet,sacn,openDmx,enttecPro,slowBreak}.ts— five hardware backends behind a single transmitter interface; main owns the universe buffer; renderer pushes composed frames viadmx:set-frame.src/features/dmx/fixtures/{builtins,types,apply,index}.ts— eight fixture profiles +applyFixtureprojector.src/features/dmx/{nodeOutputBus,useDmxAutoCompose,useDmxTransmitters}.ts— per-node side-channel + auto-compose hook + transmitter reconciler.DmxOutNode/DmxInNode+ inspectors, store discriminantsdmx-out/dmx-in, topbar buttons,useDmxInputsfor inbound Art-Net.
Track B — sequences + pads + mixer (this agent).
src/features/dmx/sequence{Types,Engine,Fade,Ops,Presets,LayerBus}.ts— rAF-driven engine + start/stop/blackout API + 8 starter sequences.src/features/dmx/{mixer,mixerInspector}.ts— replaces Track A's last-write-wins placeholder with slot exclusivity + replace/max/add mix modes; graph writes overlay on top of pad composition.src/features/dmx/universeBusClient.ts— bridge between Track B's mixer and Track A's IPC.setAutoComposelets graph-only writes flush even with no active layers.src/features/sequences/{PadView,Pad,PadEditorModal,EffectGenerator,ClockBar,padHotkeys,padTypes,usePads,clocks}.tsx/.ts— top-level Pads view, full editor modal, tap tempo + named clocks, pad↔input-bus integration.src/store/dmxTypes.ts+ extensions tosrc/store/store.ts(settings.dmx.{pads, clocks, padGridColumns, presetsLoaded}, ui.activeView, addPad/updatePad/removePad/seedDmxPresets/setClockBpm/setActiveView).- Topbar: Graph/Pads view toggle + ClockBar slot. App routes
<PadView />vs<GraphCanvas />based onui.activeView.
Cross-track contract:
universeBus.write / writeMany / snapshot. Track A's main-process bus + Track B's renderer client share the same shape; Track B's mixer composes intents before they cross the IPC boundary.
Verified clean: npm run typecheck, npx electron-vite build, cd web && npm run build. Branch feat/dmx-track-b is the merge target. CHANGELOG ## v0.6.0 block written. VERSION not bumped, branch not tagged — user runs bash scripts/bump-version.sh minor then tags v0.6.0 after smoke-testing on Windows.
2026-04-29 — v0.5.4 shipped (prior session)
- After v0.5.3 went green, the user asked to push to feature-complete except DMX. Audit identified three loose ends from
competitor-gap.md: serial-out node, wire-breathes-on-fire, serial parser UI controls. All three landed in v0.5.4. - New files:
electron/serial-bridge.ts(addedserial:writeIPC),src/features/inputs/useSerialOutputs.ts,src/features/mapping/SerialOutNode.tsx,src/features/mapping/edgeFireBus.ts,src/features/mapping/color-mix-edge.css,src/features/inspector/SerialOutInspector.tsx,docs/inputs/serial-output.md. - Touched:
src/store/store.ts(newSerialOutNodeData),electron/preload.ts+src/types/api.ts(serial.write),src/features/mapping/{useMappingRouter,ColorMixEdge,GraphCanvas},src/components/Topbar.tsx,src/features/inspector/{Inspector,SerialBindUI}.tsx,src/App.tsx,docs/inputs/serial.md,docs/ideas/{competitor-gap,v0.5-backlog}.md, CHANGELOG, VERSION → 0.5.4. - typecheck + electron-vite build clean. Tagged
v0.5.4, pushed; both prior tags' workflows already green.
2026-04-29 — v0.5.3 shipped (earlier this session)
- Backlog #13 (custom theme editor) and #14 (mDNS auto-discovery for hosts).
- New files:
src/theme/buildCustomTheme.ts,src/features/settings/CustomThemeEditor.tsx(+ css),electron/peer-discovery.ts,src/features/discovery/peerBus.ts,src/components/ui/HostPicker.tsx(+ css). - Touched:
src/theme/themes.ts(addedseeds?to Theme),src/features/settings/SettingsPanel.tsx,electron/main.ts+electron/preload.ts+electron/shared/types.ts,src/types/api.ts, all three host-bearing inspectors (OutputInspector,ResolumeOutInspector,ResolumeInInspector),docs/themes/customization.md,docs/ideas/{network-discovery,v0.5-backlog}.md, CHANGELOG, VERSION → 0.5.3. - New dep:
bonjour-service ^1.3.0. - typecheck + electron-vite build clean. Not yet tagged or pushed — waiting on user. To ship:
git push && git tag v0.5.3 && git push --tags(this triggers the Windows installer build + site deploy + version.json upload).
2026-04-29 — v0.5.2 shipped (prior session)
- Backlog #7 (cycle warning UI), #8 (per-sink event-rate gauge), #11 (Resolume swagger discovery), #12 (global hotkeys).
- New files:
src/store/cycleDetect.ts,src/features/mapping/sinkRateBus.ts,src/features/mapping/SinkRateGauge.tsx,src/features/inputs/useGlobalHotkeys.ts,electron/global-hotkeys.ts. - Touched: ColorMixEdge, GraphCanvas, OutputNode, ResolumeOutNode, useMappingRouter, useResolumeComposition, store, App, InputInspector, preload, types/api, main, CHANGELOG, VERSION → 0.5.2.
- typecheck + electron-vite build + Astro build all clean. Tagged
v0.5.2, pushed; all three GitHub Actions workflows succeeded; installer + site + version.json published. - Storage cleanup also done this session: all 9 stale Actions caches deleted (~820 MB freed) and all old release installer .exe assets deleted (~427 MB freed) — only
latest.ymlfiles retained on old releases. Account was at 100% of the 2 GB GitHub Actions storage cap; now well under. Caches will regenerate on next workflow run.
2026-04-29 — v0.5.0 + v0.5.1 (prior sessions)
- v0.5.0 (commit
5f6057d): backlog #1–5. Loop protection, Resolume echo guard, OSC inbound, transform nodes, dispatch modes. - v0.5.1 (commit
f37632d): backlog #6 + #9 + #10 + #15. Per-edge throttle override, toggle-state cleanup on rebind, save debug log to file, OSC monitor node.