Docs: "reusable by default" in the library guides, and Deep Research by its name

- library/components/WARNING.md, library/dashboards/README.md and
  documentation/contributing/README.md taught "graduate into the library at the second
  consumer". They now teach the plugin-designer skill's rule (section 3): a component that
  could serve more than one map is built once in library/, driven by props, and landed through
  the build owner; one-off or experimental pieces stay in a map's folder; a new pattern is a
  pilot, then approved, then rolled out. The contributing guide's "full reasoning" link pointed
  to an index without it; it now points to the skill.
- The app shell doxa-maps-spa is labelled Deep Research. Ten docs still called it the Research
  Gallery (15 mentions): DEPLOY.md, the desktop how-to, embeddability, develop-a-plugin,
  reference-universal-props, reference.md, the shell's EMBED.md, and the plugin-designer skill
  with two of its references. Identifiers (doxa-maps-spa, branch and route names) are unchanged;
  README.md is not touched.
This commit is contained in:
Doxa Maps 2026-10-01 08:27:04 +00:00
parent 353bf078c8
commit 70ea389db3
13 changed files with 30 additions and 28 deletions

View file

@ -106,8 +106,8 @@ Details, per host (Cloudflare Pages, jsDelivr, any web server) and per embed sty
Copy the built `dx-maps/` folder into your site's `public/` (renaming it is fine) and your site becomes Copy the built `dx-maps/` folder into your site's `public/` (renaming it is fine) and your site becomes
the host: no second server and no cross-site setup, because the maps and the page share one origin. the host: no second server and no cross-site setup, because the maps and the page share one origin.
**Live example:** DX-life's drop-in branch copies the folder into its `public/` and fills `/research` with the **Live example:** DX-life's drop-in branch copies the folder into its `public/` and fills `/research` with
Research Gallery. Deep Research.
**The two routes, on your laptop:** DX-life has one branch per route: `research-gallery-drop-in` (this **The two routes, on your laptop:** DX-life has one branch per route: `research-gallery-drop-in` (this
section, no Docker) and `research-gallery-subdomain` (the dx-maps container on its own address, with section, no Docker) and `research-gallery-subdomain` (the dx-maps container on its own address, with

View file

@ -12,7 +12,7 @@ desktop path has never been run by anyone, and the steps say so where that is th
| Piece | State | | Piece | State |
|---|---| |---|---|
| The web tree (`bun run build` → `public/dx-maps/`) | Proven. Every landing builds it. | | The web tree (`bun run build` → `public/dx-maps/`) | Proven. Every landing builds it. |
| The generic Tauri shell (`deployment/src-tauri/`) | Exists. It opens the Research Gallery, builds only a Linux `.deb`, and has never been packaged on macOS or Windows. | | The generic Tauri shell (`deployment/src-tauri/`) | Exists. It opens Deep Research, builds only a Linux `.deb`, and has never been packaged on macOS or Windows. |
| The engine on Linux (`bun run dev`, laptop mic) | Proven. | | The engine on Linux (`bun run dev`, laptop mic) | Proven. |
| The engine taking the **webview's** microphone (`RUDDER_AUDIO=browser`) | Proven on Linux: audio streamed over `/api/rudder/stream` came back as the full test sentence. | | The engine taking the **webview's** microphone (`RUDDER_AUDIO=browser`) | Proven on Linux: audio streamed over `/api/rudder/stream` came back as the full test sentence. |
| The engine **compiling** for macOS (arm64, x86_64) and Windows (x86_64) | Proven by cross-compiling. It has **never been run** on either. | | The engine **compiling** for macOS (arm64, x86_64) and Windows (x86_64) | Proven by cross-compiling. It has **never been run** on either. |
@ -124,6 +124,6 @@ macOS and Windows the app must do the same on first run:
- `desktop:build` has run on Linux only; the app it produced has not been launched yet. Nothing has run on macOS or Windows. - `desktop:build` has run on Linux only; the app it produced has not been launched yet. Nothing has run on macOS or Windows.
- The macOS/Windows runtime archive layout, and whether the engine's derived path matches it. - The macOS/Windows runtime archive layout, and whether the engine's derived path matches it.
- The first-run download, including resuming an interrupted 742 MB download. - The first-run download, including resuming an interrupted 742 MB download.
- Mapbox basemaps in the Research Gallery need the token host to allow the Tauri origin - Mapbox basemaps in Deep Research need the token host to allow the Tauri origin
(`deployment/src-tauri/README.md`). Rudder itself needs no token. (`deployment/src-tauri/README.md`). Rudder itself needs no token.
- If the engine crashes, its speech server may keep running (and holding ~1 GB) until the app quits. - If the engine crashes, its speech server may keep running (and holding ~1 GB) until the app quits.

View file

@ -65,7 +65,7 @@ The built tree has TWO kinds of page, and they need DIFFERENT framing policy:
| **Direct embed** (the product) | `<bundle>/<profile>/index.html`, `<bundle>/<profile>/<instance>/index.html` — the bare map pages the manifest's `iframeUrl` points at | **`*`** — framable on any partner site (that's the whole point) | | **Direct embed** (the product) | `<bundle>/<profile>/index.html`, `<bundle>/<profile>/<instance>/index.html` — the bare map pages the manifest's `iframeUrl` points at | **`*`** — framable on any partner site (that's the whole point) |
| **Staging** (internal preview) | tree root `index.html`, per-bundle `<bundle>/index.html` | **`'self'`** — preview only | | **Staging** (internal preview) | tree root `index.html`, per-bundle `<bundle>/index.html` | **`'self'`** — preview only |
The Research Gallery (`doxa-maps-spa/doxa-maps-spa/index.html`) is a direct-embed page like any Deep Research (`doxa-maps-spa/doxa-maps-spa/index.html`) is a direct-embed page like any
other leaf. Its full embed contract (script and iframe) is in other leaf. Its full embed contract (script and iframe) is in
`plugin-designs/doxa-maps-spa/EMBED.md`. `plugin-designs/doxa-maps-spa/EMBED.md`.

View file

@ -7,14 +7,15 @@ being duplicated.
## The one rule that keeps everything clean ## The one rule that keeps everything clean
> **Parameterized + genuinely reused by more than one map → the shared library** > **Could serve more than one map → build it in the shared library**, parameterized
> (`library/`, alias `@map`). > (`library/`, alias `@map`).
> **Not reused → it lives in your app-profile folder** (`plugin-designs/<your-map>/`). > **One-off or experimental → it lives in your app-profile folder** (`plugin-designs/<your-map>/`).
"**Local by default, promote deliberately.**" When in doubt, keep your change local to your **Reusable by default.** If a component could serve more than one map, build it once in the shared
own map; something graduates into the shared library only once a *second* map genuinely library, driven by props, and land it through the build owner, so other maps adopt it instead of
needs it. Full reasoning — the promotion / orphan-audit rule — is in copying it. A one-off or experimental piece stays in your map's folder. A new pattern starts as a pilot
[`../documentation/explanation.md`](../explanation.md). on one plugin, is approved, and is then rolled out by each owner. The rule in full:
[`skills/plugin-designer`](../../skills/plugin-designer/SKILL.md), section 3.
## Start here ## Start here

View file

@ -104,7 +104,7 @@ rsync -a --delete public/dx-maps/ <your-host-checkout>/dx-maps/
it can see and whether it finds itself. To check that what you published is *current*, run it can see and whether it finds itself. To check that what you published is *current*, run
`bun run check:catalog-currency`. `bun run check:catalog-currency`.
**Your plugin surfaces itself.** The Research Gallery (`doxa-maps-spa`) reads the built `manifest.json` **Your plugin surfaces itself.** Deep Research (`doxa-maps-spa`) reads the built `manifest.json`
and puts every bundle it doesn't already curate into its folded **Experimental** shelf, with the label and and puts every bundle it doesn't already curate into its folded **Experimental** shelf, with the label and
description from the manifest — no code edit. Promoting it to a curated tab is an optional edit to description from the manifest — no code edit. Promoting it to a curated tab is an optional edit to
`TAB_DEFS` and `NAV_MODEL` in `plugin-designs/doxa-maps-spa/`. One row per bundle: a bundle's extra `TAB_DEFS` and `NAV_MODEL` in `plugin-designs/doxa-maps-spa/`. One row per bundle: a bundle's extra

View file

@ -120,7 +120,7 @@ A URL that returns a public token as JSON (`{token|tk|accessToken}`) or as a bar
found; a token baked at build time wins. found; a token baked at build time wins.
- **The studio:** the reader's URL (localStorage). The studio fetches it itself, and the fetched - **The studio:** the reader's URL (localStorage). The studio fetches it itself, and the fetched
token then travels as `tk` / `window.MAP_TOKEN`. token then travels as `tk` / `window.MAP_TOKEN`.
- **Plugins:** none reads `profile-config.tokenUrl` today. The studio and the Research Gallery - **Plugins:** none reads `profile-config.tokenUrl` today. The studio and Deep Research
forward it, but no element consumes it (see the open points below). forward it, but no element consumes it (see the open points below).
## 5. People-group list: `'doxa'` | `'unreached'` | `'frontier'` | `'frontier-500k'` | `'unengaged'` ## 5. People-group list: `'doxa'` | `'unreached'` | `'frontier'` | `'frontier-500k'` | `'unengaged'`

View file

@ -48,7 +48,7 @@ Smallest first. Which bundle is the current example of each: **[reference-capabi
| 2 | **preset-element** (prop-table) | `parameterized` | one profile + a table of named presets; each row is a ready-made map with its own embed page | the bundle ships `<name>.instances.json` — `{ "instances": [{ "id", "title", "props" }] }`; one page per row at `<bundle>/<profile>/<id>/index.html` | | 2 | **preset-element** (prop-table) | `parameterized` | one profile + a table of named presets; each row is a ready-made map with its own embed page | the bundle ships `<name>.instances.json` — `{ "instances": [{ "id", "title", "props" }] }`; one page per row at `<bundle>/<profile>/<id>/index.html` |
| 3 | **multi-view-element** (monolithic) | `monolithic` | one profile holding several screens (tabs or steps), one shown at a time | declared: `<profile>.meta.json` → `{ "type": "monolithic" }` | | 3 | **multi-view-element** (monolithic) | `monolithic` | one profile holding several screens (tabs or steps), one shown at a time | declared: `<profile>.meta.json` → `{ "type": "monolithic" }` |
| 4 | **composite-element** (nested elements) | `nested-elements-mfe` | a profile that places other plugins together and ships the set as one new element | declared: `<profile>.meta.json` → `{ "type": "nested-elements-mfe", "composes": [ … ] }` | | 4 | **composite-element** (nested elements) | `nested-elements-mfe` | a profile that places other plugins together and ships the set as one new element | declared: `<profile>.meta.json` → `{ "type": "nested-elements-mfe", "composes": [ … ] }` |
| 5 | **app-shell** (nested SPAs) | `nested-mfe-spa` | the outer frame — rail, header, navigation — that mounts any of the others; e.g. the Research Gallery (`doxa-maps-spa`) and the Plugin Studio | declared the same way; a compiled Vue custom element like every other rung | | 5 | **app-shell** (nested SPAs) | `nested-mfe-spa` | the outer frame — rail, header, navigation — that mounts any of the others; e.g. Deep Research (`doxa-maps-spa`) and the Plugin Studio | declared the same way; a compiled Vue custom element like every other rung |
**Tiers 3–5 are always declared, never guessed** (either spelling; see `TYPE_ALIASES` in **Tiers 3–5 are always declared, never guessed** (either spelling; see `TYPE_ALIASES` in
`internal/plugin-gallery/discover.mjs`). A folder's apparent simplicity does not decide its tier: an `internal/plugin-gallery/discover.mjs`). A folder's apparent simplicity does not decide its tier: an

View file

@ -7,9 +7,9 @@ A change here does not affect one map. It ripples to **all** of them. So:
- **Prototyping or experimenting? Don't edit these.** Put your custom, one-off, not-yet-proven component - **Prototyping or experimenting? Don't edit these.** Put your custom, one-off, not-yet-proven component
inside your own map's folder (`plugin-designs/<your-map>/`), where it can be as messy as you need and inside your own map's folder (`plugin-designs/<your-map>/`), where it can be as messy as you need and
breaks nothing else. breaks nothing else.
- **Only proven, parameterized, reusable components graduate into this library.** If a component has hard-coded - **Reusable by default.** If a component could serve more than one map, build it here once, driven by props,
values, assumptions about one specific map, or hasn't been used by more than one map yet — it is **not** and land it through the build owner, so other maps adopt it instead of copying it. If it has hard-coded
ready to live here. values or assumptions about one specific map, it is **not** ready to live here: parameterize it first.
- **A reusable component takes props, not assumptions.** Before you move something in, make it parameterized: - **A reusable component takes props, not assumptions.** Before you move something in, make it parameterized:
the map, the legend, the controls, the search bar all take configuration — yours should too. the map, the legend, the controls, the search bar all take configuration — yours should too.
- **If you must change a shared component, check every map that uses it.** Run `bun run dev` and click through - **If you must change a shared component, check every map that uses it.** Run `bun run dev` and click through

View file

@ -5,9 +5,10 @@ without importing each other. This file is the contract they share. It is **docu
exists today**, verified against `development`, not a proposal. exists today**, verified against `development`, not a proposal.
The components still live in `plugin-designs/research-dashboard/src/components/dashboards/`. They The components still live in `plugin-designs/research-dashboard/src/components/dashboards/`. They
are deliberately **not** in `library/` yet: a library component with a single consumer is a are not in `library/` yet. Under the repo's "reusable by default" rule
relocation, not a promotion, and the repo has already had to move such modules back. They graduate ([`skills/plugin-designer`](../../skills/plugin-designer/SKILL.md), section 3), a component another
when a second dashboard consumes them. dashboard could use belongs in `library/`, driven by props; moving these is a library change for the
build owner.
## The shape ## The shape

View file

@ -1,8 +1,8 @@
# Embedding the Research Gallery # Embedding Deep Research
![The Research Gallery embedded by script on a partner page: the tab rail on the left, Big Picture open](../../documentation/readme-images/deploy3-js-embed-dxlife.png) ![Deep Research embedded by script on a partner page: the tab rail on the left, Big Picture open](../../documentation/readme-images/deploy3-js-embed-dxlife.png)
`<doxa-maps-spa>` (labeled **Research Gallery**) is the capability-5 app shell: one element whose `<doxa-maps-spa>` (labeled **Deep Research**) is the capability-5 app shell: one element whose
tab rail nests every production map. It is built to drop onto somebody else's page. Below, tab rail nests every production map. It is built to drop onto somebody else's page. Below,
`TREE` means the URL of the built `doxa-maps/` folder wherever it is served, for example `TREE` means the URL of the built `doxa-maps/` folder wherever it is served, for example
`https://YOUR-HOST/doxa-maps`. `https://YOUR-HOST/doxa-maps`.

View file

@ -191,7 +191,7 @@ levels 1 and 2 are detected on their own.
| 2 | preset element (`parameterized`) | one screen, several named presets from `<screen>.instances.json` | `plugin-designs/doxa-simple-map/` | | 2 | preset element (`parameterized`) | one screen, several named presets from `<screen>.instances.json` | `plugin-designs/doxa-simple-map/` |
| 3 | multi-view element (`monolithic`) | many views fused inside one element | `plugin-designs/doxa-research-map/` | | 3 | multi-view element (`monolithic`) | many views fused inside one element | `plugin-designs/doxa-research-map/` |
| 4 | composite element (`nested-elements-mfe`) | nests other elements, each still embeddable alone, wired by props and events | `plugin-designs/dashboard-plus-grid/` (the Big Picture tab) | | 4 | composite element (`nested-elements-mfe`) | nests other elements, each still embeddable alone, wired by props and events | `plugin-designs/dashboard-plus-grid/` (the Big Picture tab) |
| 5 | app shell (`nested-mfe-spa`) | nests whole apps | `plugin-designs/doxa-maps-spa/` (the Research Gallery) | | 5 | app shell (`nested-mfe-spa`) | nests whole apps | `plugin-designs/doxa-maps-spa/` (Deep Research) |
**Your declaration is checked, not merely trusted.** `internal/check-capability-derived.mjs` **Your declaration is checked, not merely trusted.** `internal/check-capability-derived.mjs`
derives the level from what your plugin actually `composes` and fails when an explicit `"type"` derives the level from what your plugin actually `composes` and fails when an explicit `"type"`
@ -221,7 +221,7 @@ line number, because lines move.
| Interop: one element reacts to another's events | Simple Research: `plugin-designs/research-map-plus-grid/research-map-plus-grid.vue` | the map listening for the grid's `groups-filtered` event | | Interop: one element reacts to another's events | Simple Research: `plugin-designs/research-map-plus-grid/research-map-plus-grid.vue` | the map listening for the grid's `groups-filtered` event |
| Composite element (capability 4) | Big Picture: `plugin-designs/dashboard-plus-grid/dashboard-plus-grid.meta.json` | `"composes"`: two elements, each still embeddable alone | | Composite element (capability 4) | Big Picture: `plugin-designs/dashboard-plus-grid/dashboard-plus-grid.meta.json` | `"composes"`: two elements, each still embeddable alone |
| One bundle, several embeddable screens | UPG-100: `plugin-designs/my-upg-100-list/` | `upg-100-mfe`, `upg-100-list` and `poster-wall` from one bundle | | One bundle, several embeddable screens | UPG-100: `plugin-designs/my-upg-100-list/` | `upg-100-mfe`, `upg-100-list` and `poster-wall` from one bundle |
| App shell (capability 5) | Research Gallery: `plugin-designs/doxa-maps-spa/doxa-maps-spa.vue` | every plugin as a tab, filled from the manifest | | App shell (capability 5) | Deep Research: `plugin-designs/doxa-maps-spa/doxa-maps-spa.vue` | every plugin as a tab, filled from the manifest |
`emissivePaint` and `labelFont` (section 5) have no production-plugin exemplar, because the `emissivePaint` and `labelFont` (section 5) have no production-plugin exemplar, because the
production plugins get them through the library's own layers. The starter uses both directly: production plugins get them through the library's own layers. The starter uses both directly:
@ -258,7 +258,7 @@ tag from any host), the **mobile app** (the same folder wrapped by Capacitor, `b
the **desktop app** (the same folder in Tauri, `bun run tauri:build`, a proof of concept). the **desktop app** (the same folder in Tauri, `bun run tauri:build`, a proof of concept).
**Your plugin runs everywhere unless you say otherwise.** A `<profile>.meta.json` `"targets"` list **Your plugin runs everywhere unless you say otherwise.** A `<profile>.meta.json` `"targets"` list
narrows that, and it is read, not decorative: the Research Gallery skips a bundle that does not narrows that, and it is read, not decorative: Deep Research skips a bundle that does not
list `web` (`plugin-designs/doxa-maps-spa/doxa-maps-spa.vue:605`). Declare it only for a plugin list `web` (`plugin-designs/doxa-maps-spa/doxa-maps-spa.vue:605`). Declare it only for a plugin
that genuinely cannot work in a plain web embed — Rudder needs a local speech engine. Details: that genuinely cannot work in a plain web embed — Rudder needs a local speech engine. Details:
`references/targets.md`. `references/targets.md`.

View file

@ -17,7 +17,7 @@ A plugin that needs one names it in its `<profile>.meta.json` `"backend"` key, a
`plugin-designs/rudder-voice/rudder-voice.meta.json` does with `"backend": "backend/rudder-engine"`. `plugin-designs/rudder-voice/rudder-voice.meta.json` does with `"backend": "backend/rudder-engine"`.
A plugin that needs a local backend almost certainly also needs `"targets"` without `web`, which A plugin that needs a local backend almost certainly also needs `"targets"` without `web`, which
keeps it out of the Research Gallery — see `targets.md`. Rudder declares both. keeps it out of Deep Research — see `targets.md`. Rudder declares both.
## The database rule: schema is a commit, never a click ## The database rule: schema is a commit, never a click

View file

@ -72,7 +72,7 @@ a widget target without a corner (or a corner without the target), and a `backen
- `internal/profile-meta.mjs:28` reads the field (string entries only) and `:36` carries it into - `internal/profile-meta.mjs:28` reads the field (string entries only) and `:36` carries it into
the manifest, so every consumer of the manifest sees it. the manifest, so every consumer of the manifest sees it.
- **The Research Gallery skips a bundle whose every profile is a widget or lacks the `web` - **Deep Research skips a bundle whose every profile is a widget or lacks the `web`
target** — `plugin-designs/doxa-maps-spa/doxa-maps-spa.vue:605`: target** — `plugin-designs/doxa-maps-spa/doxa-maps-spa.vue:605`:
```js ```js