doxa-maps/skills/plugin-designer/references/targets.md
Doxa Maps 53363ba80d
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
plugin-designer skill: fix stale gallery claims, split depth into references/
SKILL.md claimed new plugins land in a Research Gallery Experimental section.
That section was replaced by capability tiers (plugin-studio.vue:630); stage is
now a label prefix, not a place. Both stale lines corrected.

Adds what the skill never said:
- capability is DERIVED from composes and an explicit type is CHECKED against it
  (check-capability-derived.mjs), with its two traps;
- rail labels come from meta, and check-studio-rail.mjs:68 fails a bare id;
- presets merge OVER the profile's meta config, key by key -- rule 3 of
  documentation/PRESET-PLUGIN-STANDARD.md, asserted by check:preset-config-merge
  (generate-tree.mjs:1094, the comment there records the pinHint bug);
- targets is read (profile-meta.mjs:28) and consequential: the Research Gallery
  skips a bundle that does not list web (doxa-maps-spa.vue:605).

Depth moves to references/: glossary, targets, backend, workflow. backend.md
carries the PocketBase user tiers marked DESIGN INTENT, NOT BUILT -- quoted
rather than linked, because their source lives outside this repo.

Verified against this tree, rebased on main: 61 path citations resolve, 10 line
citations land on the code they claim.

README: one line pointing an agent at the skill.
2026-09-28 03:32:27 +00:00

5 KiB
Raw Blame History

Capabilities and targets

Two separate questions about a plugin: how composed is it (capability 1–5) and where can it run (targets). Only the first is enforced.

Capability 1–5

Pick the lowest level that does the job.

Level type What it is Example
1 single one screen, one element plugin-designs/prayer-walk-planner/
2 parameterized one screen, several named presets from <screen>.instances.json plugin-designs/doxa-simple-map/
3 monolithic many views fused inside one element plugin-designs/doxa-research-map/
4 nested-elements-mfe nests other elements, each still embeddable alone plugin-designs/dashboard-plus-grid/
5 nested-mfe-spa nests whole apps plugin-designs/doxa-maps-spa/

It is DERIVED, and your declaration is CHECKED against it

Levels 1 and 2 are inferred: a bundle with an *.instances.json is parameterized, otherwise single (internal/plugin-gallery/discover.mjs:44-52). Levels 3–5 you declare in <screen>.meta.json "type".

Declaring is not the end of it. internal/check-capability-derived.mjs derives the capability from what your plugin actually composes and fails when an explicit type disagrees. Its own header states the contract:

A profile that composes anything derives cap-4, or cap-5 if it composes a cap-4 — and an EXPLICIT type must AGREE with that. The declaration stays authoritative; it is not overridden, it is checked.

The defect it exists for: research-dashboard nested ten views with no meta.json and shipped as capability 1, because a missing file is indistinguishable from a deliberate choice. Every surface that shows a tier — the manifest, the gallery, the studio rail — reads that one field, so the understatement propagated everywhere.

Run bun run check:capability-derived. Two related gates: check:capability-citations and check:capability-exemplars.

Two traps the gate documents

Worth knowing because they will bite you the same way:

  • composes is per-profile at source but hoisted to the bundle in the manifest. Deriving from the bundle row applies a composite's edges to its plain sibling leaves — gospel-resources has one composite and four single leaves, and all four wrongly derived as cap-4.
  • Refs appear in three forms in the same field: 'bundle/profile', a bare 'bundle', and an element tag. Resolving only the first demoted plugin-studio from 5 to 4.

targets — where the plugin may run, and it has teeth

A <profile>.meta.json may carry "targets", naming where the plugin is meant to run:

{ "type": "single", "label": "Rudder", "targets": ["desktop"], "backend": "backend/rudder-engine" }

Omitting it means "everywhere", and that is the right default for almost every plugin. The whole point of the build is one folder that runs as a web embed, a mobile app and a desktop app.

Declaring it has a real consequence, so declare it only when you mean it:

  • 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 Research Gallery skips a bundle whose every profile is a widget or lacks the web target — plugin-designs/doxa-maps-spa/doxa-maps-spa.vue:605:

    if (profiles.every(p => p.widget || (Array.isArray(p.targets) && !p.targets.includes('web')))) continue
    

    So "targets": ["desktop"] removes your plugin from the gallery. That is the feature, not a bug: it keeps a plugin that cannot work on a static host out of a surface that is served from one. bun run check:gallery-embed covers the gallery's embeds.

The worked example is Rudder: its speech model runs in a local backend started by bun run dev and run by Tauri as a sidecar, so "a website has no local engine" (its _comment). It is correctly absent from the gallery and makes no network call on a static host.

  • Say why in _comment when you declare it — the next reader needs the reason, not just the list.
  • A backend key alongside it names the folder that must be running. See backend.md.
  • Do not add targets "for documentation". Adding it without web silently removes you from the gallery.

Where a plugin actually runs

The three outputs, all from the same built folder:

Output What it is How
Web folder public/dx-maps/, embeddable by iframe or script tag from any host copy the folder; snippets in manifest.json
Mobile app the same folder wrapped by Capacitor (capacitor.config.json webDir) bun run phone
Desktop app the same folder in Tauri (deployment/src-tauri/), a proof of concept bun run tauri:build

WordPress (deployment/wordpress/) embeds the web folder's snippets; it is experimental.

Embeddability is checked, unlike targets: bun run check:embed-targets runs internal/check-embed-targets-exist.mjs.

  • glossary.md — bundle, profile, preset, stage vs capability
  • backend.md — plugins that need a service, and the user tiers