Changelog¶
All notable changes to the feedBack plugin specification are documented here.
This repository is licensed under AGPL-3.0-only. This file follows Keep a Changelog, and the specification is versioned per Semantic Versioning — see spec §9 for how the document, manifest, and per-plugin versions relate.
Unreleased¶
Fixed¶
- Route-prefix examples now match the Host. Every illustrative route prefix said
/api/plugin/<id>/…(singular) in §7.3, twice inbest-practices.md, and throughoutexamples/full-plugin(routes.py,screen.js,settings.html), while the feedBack Host serves plugin routes under/api/plugins/…(plural) — and the rest ofbest-practices.mdalready used the plural form. All examples and prose now use/api/plugins/<id>/…. The normative rule (§7.4: derive every route path from the pluginid) is unchanged — the singular form only ever appeared in examples, never as a requirement. Closes #20.
Added¶
- Module
scriptentries. §3 (Anatomy), §4.1/§4.3, new §6.8 (Splitting client code), andschemas/plugin.schema.jsondocument the optionalscriptTypemanifest key:"module"loads a plugin'sscriptas an ES module (<script type="module">), letting the entry use top-levelimport/exportandimport.metaand split its client code across a servedsrc/module tree; absent or"classic"is the unchanged classic-script behaviour. The optionalminHostkey (minimum Host version) is documented alongside it. Purely additive — every existing manifest still validates, and a Host that ignoresscriptTypekeeps loadingscriptas a classic script.
Changed¶
- Best-practices "Organizing client code across files" (rules 27–29) rewritten around the new
paradigm: an ES-module entry (
scriptType: "module"+ asrc/tree with realimport/export) is now the recommended way to split client code, with the classic single-screen.jsbundle /window-shared runtime split kept as the fallback for older Hosts. Corrects the prior text, which statedscreen.jsis always a classic script and that onlyassets/is servable — both no longer true oncescriptType: "module"is set.
1.0.0 - 2026-07-06¶
First stable release of the feedBack plugin specification. Marks the normative spec and its companion best-practices guide (54 rules, ground-truthed against the app) as stable, consolidating everything accumulated since the 0.1.0 draft. The version string moves 0.1.0 → 1.0.0 and the document status changes from Draft to Stable.
Changed¶
- §8 (Capabilities) and
schemas/plugin.schema.jsonnow document the real capability-declaration vocabulary, ground-truthed against every bundled plugin: the domain keys and fields are an open set defined by the declared standard (e.g.capability-pipelines.v1), and a declaration may carryoperations/requests(alongsidecommands),emits/observes(alongsideevents), pluskindanddescription. Thecompatibilityfield is no longer a hard schema enum (its values are an open set); the observed values are documented instead. Purely additive and clarifying — every existing manifest still validates. - §6 (Client surface) rewritten to document the client screen contract, ground-truthed against the
Host. Adds the mount lifecycle (§6.1 — Host-created container,
screen-sourced markup, self-executingscriptwith no Host-invoked entry point, and a normative idempotent re-hydration MUST), screen activation/visibility (§6.2), and a description of the Host-provided, Host-versioned runtime surface (§6.3 — event bus, contribution registries, and the forward-stable capability control plane; raw window globals documented as supported-but-legacy). New §6.4 "Performance and the shared main thread" makes the hot-path rules normative (SHOULD NOT do per-frame DOM/layout/IO; don't observe/mutate the shell — use contribution registries; suspend work when hidden; keep state per-instance). Replaces the previous "client runtime API is out of scope" placeholder. Settings/Styles/Static-assets renumbered to §6.5–6.7. - Best-practices guide: added a "Client screen & the shared main thread" section grounded in
real feedBack performance regressions — no DOM/layout work on a per-frame path, don't
DOM-observe or mutate the app shell (use registration APIs instead of injecting into song/library
cards), no synchronous storage/network on hot or gameplay-event paths, idempotent re-hydration
(
plugin-runtime-idempotent.v1), stop work when hidden, stay per-instance, and talk to other plugins through the capabilityclaim/dispatch/releaseflow rather than their globals. Regrouped the guide (Getting started / Server routes / Client screen / Shipping) and expanded the pre-publish checklist with a client-performance block. Docs only. -
Best-practices guide: added a "Visualizations" section for
type: "visualization"plugins, ground-truthed against the Host's renderer contract and recent splitscreen/settings fixes. Covers the factory pattern (window.feedBackViz_<id>returns a fresh renderer per call — required for splitscreen's N simultaneous panels), the renderer interface (draw/init/resize/destroy/contextType), per-instance resource ownership anddestroy()cleanup, treating the per-frame bundle as read-only, self-detecting canvas size drift, and communicating settings viaapplySetting(key, value)per instance (declaresettingson thevisualizationcapability; the Host applies each change to the specific per-panel instance) — including the concrete failure modes recent fixes addressed (apply-live-not-reload, no cross-setting leakage, deliberate per-panel vs global key scoping, fan-out to all panels, settings panel loads before the renderer), and persistence guidance (Host owns persistence — don't hand-rolllocalStorage; if self-managed, stage an in-memory fallback before the quota-falliblesetItemand keep it off the per-frame path), plus the fail-safe auto-revert. Expanded the checklist with a Visualizations block. Docs only. -
Best-practices guide: expanded the
id/naming rule (rule 2) with the collision and namespacing gotchas, ground-truthed against the loader. Explains how far theidreaches (routes moduleplugin_<id>_routes, screen containerplugin-<id>, viz globalwindow.feedBackViz_<id>, diagnostics path,localStorageprefix), the exact-case folder rule and why the charset matters (it becomes a Python module + DOM/JS identifiers), the collision resolution (a bundledidalways wins — a user plugin reusing it is silently ignored; between two non-bundled plugins the first discovered wins), reserved ids (capability_inspector,app_tour_*are always-enabled), and namespacing shared-space names (localStorage,windowglobals, routes, CSS) byid. Added matching checklist items. Docs only. - Best-practices guide: added a "Minigames" section for plugins that register into the bundled
minigameshost, ground-truthed against the host + SDK. Covers late-binding registration (the host loads after your plugin — queue viawindow.__feedBackMinigamesPendingand/or thefeedBack-minigames-readyevent), theminigamemanifest block withspec.id=== pluginid, thestart/stoplifecycle where the game must release everything it opens (rAF loops,AudioContext,getUserMedia, timers, listeners — the host only cancels its own), supersede-safestart(double-tap / navigate-away races), standalone-by-default vsusesPlayer, and host-owned scoring/persistence (report via the SDK; single active session). Added a Minigames checklist block. Docs only. - Best-practices guide: added an "Organizing client code across files" section for splitting a
plugin's client JS instead of shipping one monolithic
screen.js, ground-truthed against how the Host loads and serves plugin JS. Covers bundling to onescreen.jsas the simplest path; the constraint thatscreen.jsis a classic script (noimport/export/import.meta— split files share state viawindow); serving extra files fromassets/(the plugin root isn't servable) referenced by absolute/api/plugins/<id>/…URLs (relative resolves against the document, not the script); and idempotent runtime loading so re-hydration doesn't double-load. Added a matching checklist block. Docs only. - Best-practices guide: added an "Integrating with the app" section, ground-truthed against the
window.feedBackruntime surface. Adds the event-bus catalog (screen:changed,song:*,library:changed,viz:*,highway:*with theirevent.detailpayloads), the rule to drive the app through thefeedBackAPI (navigate/getNavParams/playSong/seek/setLoop/currentSong/playQueue) rather than its DOM controls, wrapper discipline for hooking Host globals (call andawaitthe original, install once, clean up, no load-order assumptions), and the v2/v3 player-chrome mount contract. Added a matching checklist block. Docs only. - Best-practices guide: added a "Server-side robustness" section for
routes-shipping plugins, ground-truthed against the loader. Covers declaring Python deps inrequirements.txt(no manifest field; installs are hash-keyed, sequential, and delay later plugins — keep them minimal and pinned; a failed install is non-fatal so guard heavy/optional imports), not blocking the event loop (a fastsetup()killed at a ~60s timeout; a blockingasync defhandler freezes the server — use non-blocking I/O or a plaindefthat runs in the threadpool), splitting server code viacontext["load_sibling"]rather than bare imports that collide across plugins insys.modules, and logging throughcontext["log"](neverprint()) plus route namespacing. Added a matching checklist block. Docs only. - Best-practices guide: added a "Styling" section. A plugin can't rely on the app's compiled
stylesheet (it only contains classes the bundled code uses), so a runtime-installed plugin renders
unstyled unless it ships its own compiled stylesheet via
styles. Covers building it to coexist — base/preflight reset off, selectors scoped, no Tailwind Play CDN / runtime CSS engine (slow, offline-hostile), and bumpversionto cache-bust the sheet. Added a matching checklist block. Docs only. -
Best-practices guide: added a "Diagnostics" section. Covers contributing plugin state to the user-exportable diagnostics bundle — client via
window.feedBack.diagnostics.contribute(id, payload)(idempotent; last snapshot before export wins) and server viadiagnostics.server_files/diagnostics.callable("module:function"returning dict/list/bytes/str; the Host catches exceptions so a broken function never crashes the export) — and the rule that a bundle is effectively public, so it MUST NOT contain secrets, credentials, absolute paths, usernames, or raw user content and should stay small with aschemafield. Added a matching checklist block. Docs only. -
Best-practices guide: added an "Extending the app's shared surfaces" section — the registration APIs rule 10 points authors to but never named. Covers song/library card actions (
window.feedBack.libraryCardActions.register(spec)withapplies/enabled/run), nav entries (the manifestnavkey), keyboard shortcuts (window.registerShortcutwith scoped lifetimes, cleaned up on teardown), and audio faders (window.feedBack.audio.registerFader, gated onfeedBack:audio:ready, plugin owns persistence). Added a matching checklist block. Docs only. -
Best-practices guide: added "Onboarding tours" and "Library providers" sections, the last of the extension points a completeness sweep found. Tours: any plugin can ship a guided tour as a declarative
tour.json(atourmanifest key; steps are centeredbubbles or elementspotlights withselector/waitFor), with the clientwindow.feedBackTour.register(...)API reserved for dynamic steps and offering the tour once. Library providers: add song sources viacontext["register_library_provider"](unregister on teardown; the Host enforces owner attribution and the built-inlocalprovider can't be removed; declare thelibrarycapability and page large sources). Added matching checklist items. Docs only. -
Best-practices guide: added a "Highway overlays and note-state providers" section for plugins that participate in the note-highway without replacing its renderer. Covers overlays (a layer on top of whatever renderer is active — own your rAF + canvas, re-read state each frame, respect lefty/invert, gate the built-in
project/fretXgeometry helpers onhighway.isDefaultRenderer(), track thehighway:visibilityevent for sibling DOM, clean up on toggle-off; nottype: "visualization", not in the picker), and the note-state provider (highway.setNoteStateProvider(fn)— a scorer lights the note gem itself on a correct hit; a single global surface, cleared on stop). Added matching checklist items. Docs only.
0.1.0 - 2026-07-05¶
Initial draft of the feedBack plugin specification.
Added¶
- Normative specification
spec/plugin-spec-v1.md: conformance (RFC 2119 / RFC 8174), plugin anatomy, theplugin.jsonmanifest reference, discovery and loading (the directory-name ==idrule, bundled vs user-installed precedence, partial load, enable/disable), the client surface (screen, settings, styles, static assets), the server surface (routes.pysetup(app, context)), capabilities and standards, versioning, and security considerations. spec/best-practices.md: a non-normative best-practices guide with a pre-publish checklist.- Manifest JSON Schema
schemas/plugin.schema.json(Draft 2020-12). - Worked examples:
examples/minimal-plugin(manifest only) andexamples/full-plugin(screen, styles, settings, routes, capabilities). - Reference validator
tools/validate.py: schema-checks a plugin manifest, enforces the directory-name rule, and confirms referenced files exist; doubles as the CI gate. - Documentation site (MkDocs Material) assembled by
tools/gen_docs.pyand published to GitHub Pages, plus release-on-merge automation and a version-consistency guard (tools/check_versions.py). - Repository governance: README, CONTRIBUTING (DCO + enhancement-proposal process), GOVERNANCE, CODE_OF_CONDUCT, issue templates, and AGPL-3.0-only licensing.