From 682f2121fee4b695bf3cf24a6d9edae36c78a4d1 Mon Sep 17 00:00:00 2001 From: "K. O. A." Date: Sun, 5 Jul 2026 21:31:35 -0400 Subject: [PATCH 1/2] best-practices: expand id/naming rule with collision + namespacing gotchas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rule 2 was a short "treat the id as forever". Expand it, ground-truthed against the plugin loader, into the real id/naming pitfalls: - How far the id reaches: routes module (plugin__routes), screen container (plugin-), viz global (window.feedBackViz_), diagnostics path, and the localStorage prefix — so a collision or rename ripples everywhere. - Folder name MUST equal id exactly incl. case (the #1 "won't load" cause), and the charset matters because id becomes a Python module name + DOM/JS identifier — uppercase/dots/spaces break discovery or module loading; the validator rejects them. - Collision resolution: a bundled id ALWAYS wins (a user plugin reusing it is silently ignored, kept only as a fallback); between two non-bundled plugins the first discovered wins. Check your id isn't a bundled one. - Reserved ids: capability_inspector and app_tour_* are always-enabled — don't collide with them. - Namespace shared-space names (localStorage, window globals, routes, CSS) by id, since all plugins share one window and document. Cross-link spec §4.2 / §5.2 for the normative rules. Add matching checklist items. Numbering unchanged (enriched in place). Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: K. O. A. --- CHANGELOG.md | 10 +++++++++ spec/best-practices.md | 51 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43250d7..e3011d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,16 @@ for how the document, manifest, and per-plugin versions relate. stage an in-memory fallback before the quota-fallible `setItem` and 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 the `id` reaches (routes module + `plugin__routes`, screen container `plugin-`, viz global `window.feedBackViz_`, + diagnostics path, `localStorage` prefix), the exact-case folder rule and why the charset matters + (it becomes a Python module + DOM/JS identifiers), the **collision resolution** (a bundled `id` + always 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`, `window` globals, routes, CSS) by `id`. Added + matching checklist items. Docs only. + ## [0.1.0] - 2026-07-05 Initial draft of the feedBack plugin specification. diff --git a/spec/best-practices.md b/spec/best-practices.md index dade43b..31831f0 100644 --- a/spec/best-practices.md +++ b/spec/best-practices.md @@ -24,12 +24,47 @@ Get *that* discovered first (folder name equal to `id`, dropped into a plugins d add one surface at a time — a screen, then settings, then routes. Adding surfaces incrementally means when something stops loading you know exactly which change caused it. -### 2. Treat the `id` as forever +### 2. Choose the `id` carefully — it's a permanent, far-reaching identifier -The `id` keys your settings store, your routes namespace, and your capability declarations. -Renaming it silently orphans every user's saved settings. Pick a lowercase, `-`/`_`-separated, -descriptive `id` once (`drum_highway_3d`, not `dh3` or `DrumHighway`) and never change it. The -folder name must match it exactly. +Your `id` is not just a label; the Host derives a surprising amount of machinery from it. It keys +the settings store, the routes namespace, and the capability participant, and it is interpolated +into names all over the app: the server routes module (`plugin__routes`), the screen container +element (`plugin-`), a visualization's factory global (`window.feedBackViz_`), the +diagnostics path (`plugins//`), and the conventional `localStorage` prefix. A collision or a +rename therefore ripples through all of them at once. + +So: + +- **Pick it once and never change it.** Renaming an `id` silently orphans every user's saved + settings and breaks every derived name above. Changing the `id` is a *new* plugin, not a new + version (see [spec §4.2](plugin-spec-v1.md#42-id)). +- **Make it specific and unique.** Prefer a descriptive `id` (`drum_highway_3d`, not `dh3` or a + generic `player`/`viz`). A generic `id` is the easiest way to collide with someone else's plugin. +- **The folder name MUST equal the `id`, exactly, including case.** A folder named `Tuner` or + `tuner-plugin` holding `{"id": "tuner"}` is simply not discovered — the most common "why won't my + plugin load?" (see [spec §5.2](plugin-spec-v1.md#52-the-directory-name-rule)). +- **Stick to `^[a-z0-9][a-z0-9_-]*$` (snake_case is the house style).** The charset isn't + cosmetic: the `id` is spliced into a Python module name and DOM/JS identifiers. Uppercase breaks + the exact-match discovery rule, and dots/spaces/other punctuation break module or element naming. + The reference validator rejects anything outside this set — run it (`python tools/validate.py`). + +**Collisions with an existing plugin.** When two plugins share an `id`, only one loads, and the +rule is not "last one wins": + +- A **bundled** (first-party) plugin **always wins** — a plugin you install that reuses a bundled + `id` is silently ignored (the Host keeps it only as a fallback if the bundled copy fails). Before + naming a plugin, make sure the `id` isn't already a bundled one. +- Between two non-bundled plugins, the first the Host discovers wins and the other is dropped — so a + duplicate `id` means one of them silently doesn't load. + +**Reserved ids.** Do not name a plugin `capability_inspector`, or use an `app_tour_` prefix, unless +you intend to replace those core surfaces — the Host treats them as always-enabled (they cannot be +disabled), so a collision there is especially sticky. + +**Namespace what the `id` doesn't namespace for you.** Because every plugin shares one `window` and +one document, prefix anything you put in a shared space with your `id`: `localStorage` keys, any +`window` globals you must expose, your routes (`/api/plugin//…`, rule 7), and your CSS (rule 10). +Two plugins writing `window.state` or `localStorage["theme"]` clobber each other silently. ### 3. Keep the manifest declarative @@ -323,7 +358,11 @@ note changes per version. It costs little and saves every future reader — incl ## Checklist before you publish -- [ ] Folder name equals `id`, matching `^[a-z0-9][a-z0-9_-]*$`. +- [ ] Folder name equals `id` exactly (incl. case), matching `^[a-z0-9][a-z0-9_-]*$`. +- [ ] `id` is specific and doesn't collide with a bundled plugin (a bundled `id` wins; yours would be + silently ignored) or a reserved one (`capability_inspector`, `app_tour_*`). +- [ ] Anything in shared space is namespaced by `id`: `localStorage` keys, `window` globals, routes, + and CSS. - [ ] `plugin.json` is valid against [`schemas/plugin.schema.json`](../schemas/plugin.schema.json) (`python tools/validate.py path/to/my-plugin`). - [ ] Every manifest file reference resolves to a shipped file. From 675d076a07ad6d8243742d982160cbc1b5b9fc51 Mon Sep 17 00:00:00 2001 From: "K. O. A." Date: Mon, 6 Jul 2026 00:26:43 -0400 Subject: [PATCH 2/2] best-practices: fix id charset wording + dedup checklist item - Rule 2: drop 'snake_case is the house style' (examples use kebab-case; both '-' and '_' are valid and used). Say lowercase with -/_ separators. - Checklist: limit the new namespacing item to localStorage/window globals; routes and CSS are already covered by the later item. Signed-off-by: K. O. A. --- spec/best-practices.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/best-practices.md b/spec/best-practices.md index 31831f0..4a642e9 100644 --- a/spec/best-practices.md +++ b/spec/best-practices.md @@ -43,7 +43,8 @@ So: - **The folder name MUST equal the `id`, exactly, including case.** A folder named `Tuner` or `tuner-plugin` holding `{"id": "tuner"}` is simply not discovered — the most common "why won't my plugin load?" (see [spec §5.2](plugin-spec-v1.md#52-the-directory-name-rule)). -- **Stick to `^[a-z0-9][a-z0-9_-]*$` (snake_case is the house style).** The charset isn't +- **Stick to `^[a-z0-9][a-z0-9_-]*$` (lowercase, with `-` or `_` separators — both are fine and + both are used in practice).** The charset isn't cosmetic: the `id` is spliced into a Python module name and DOM/JS identifiers. Uppercase breaks the exact-match discovery rule, and dots/spaces/other punctuation break module or element naming. The reference validator rejects anything outside this set — run it (`python tools/validate.py`). @@ -361,8 +362,8 @@ note changes per version. It costs little and saves every future reader — incl - [ ] Folder name equals `id` exactly (incl. case), matching `^[a-z0-9][a-z0-9_-]*$`. - [ ] `id` is specific and doesn't collide with a bundled plugin (a bundled `id` wins; yours would be silently ignored) or a reserved one (`capability_inspector`, `app_tour_*`). -- [ ] Anything in shared space is namespaced by `id`: `localStorage` keys, `window` globals, routes, - and CSS. +- [ ] Global namespaces are prefixed by `id`: `localStorage` keys and any `window` globals. (Routes + and CSS are covered separately below.) - [ ] `plugin.json` is valid against [`schemas/plugin.schema.json`](../schemas/plugin.schema.json) (`python tools/validate.py path/to/my-plugin`). - [ ] Every manifest file reference resolves to a shipped file.