total-recall-brain 3.40.7 → 3.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/total-recall-memory.mjs +0 -0
- package/bin/total-recall.mjs +9 -40
- package/docs/reference/cli-reference.md +65 -1
- package/frontend/dist/assets/{index-AGUes-VG.css → index-BG9NkVUy.css} +1 -1
- package/frontend/dist/assets/{index-Bt5iVPZj.js → index-BtuiZqwP.js} +20 -7
- package/frontend/dist/index.html +2 -2
- package/frontend/src/components/plugins/PluginPanel.tsx +31 -5
- package/frontend/src/components/plugins/card/CardEngine.tsx +15 -23
- package/frontend/src/components/plugins/card/CardScenes.tsx +52 -23
- package/frontend/src/components/plugins/card/card.css +2 -5
- package/frontend/src/components/plugins/card/contract.ts +13 -19
- package/frontend/src/components/plugins/card/format.ts +8 -1
- package/frontend/src/components/plugins/card/widgets.tsx +4 -35
- package/frontend/src/components/plugins/plugin-frame.ts +30 -3
- package/frontend/src/pages/PluginsPage.tsx +3 -2
- package/package.json +1 -1
- package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
- package/scaffold/.agent/skills/plugin/SKILL.md +110 -0
- package/scaffold/.agent/skills/plugin/evals/README.md +53 -0
- package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +42 -0
- package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +39 -0
- package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +32 -0
- package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +46 -0
- package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +55 -0
- package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +92 -0
- package/scaffold/.agent/skills/plugin/evals/run.mjs +118 -0
- package/scaffold/.agent/skills/plugin/prompts/01-function.md +29 -0
- package/scaffold/.agent/skills/plugin/prompts/02-surfaces.md +43 -0
- package/scaffold/.agent/skills/plugin/prompts/03-scaffold.md +31 -0
- package/scaffold/.agent/skills/plugin/prompts/04-cli.md +49 -0
- package/scaffold/.agent/skills/plugin/prompts/05-credentials.md +41 -0
- package/scaffold/.agent/skills/plugin/prompts/06-card.md +46 -0
- package/scaffold/.agent/skills/plugin/prompts/07-scaffold.md +52 -0
- package/scaffold/.agent/skills/plugin/prompts/08-verify.md +43 -0
- package/scaffold/.agent/skills/plugin/references/anti-patterns.md +123 -0
- package/scaffold/.agent/skills/plugin/references/card.md +90 -0
- package/scaffold/.agent/skills/plugin/references/scaffold.md +120 -0
- package/scaffold/.agent/skills/plugin/references/surfaces.md +206 -0
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +360 -0
- package/scaffold/.agent/skills/plugin/subagents/card-designer.md +60 -0
- package/scaffold/.agent/skills/plugin/subagents/plugin-reviewer.md +55 -0
- package/scaffold/.agent/skills/plugin/subagents/scaffold-extractor.md +71 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +71 -1441
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +7 -5
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +3 -44
- package/scaffold/.agent/skills/total-recall/plugins/auth/scripts/agent-login-pty.py +185 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +20 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/host-secrets-store.mjs +15 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +6 -4
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -0
- package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -24
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +0 -21
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +0 -32
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +11 -0
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +5 -2
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +1 -28
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +95 -13
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +553 -340
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +0 -36
- package/scaffold/.agent/skills/total-recall/plugins/mesh/ui/card.js +383 -400
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +0 -1
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +0 -28
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +4 -4
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +0 -32
- package/scaffold/.agent/skills/total-recall/references/auth.md +3 -1
- package/scaffold/.agent/skills/total-recall/references/memory.md +4 -0
- package/scaffold/.agent/skills/total-recall/references/openwiki.md +2 -0
- package/scaffold/.agent/skills/total-recall/references/operations.md +12 -0
- package/src/cli/brain.mjs +72 -2
- package/src/cli/connect.mjs +10 -39
- package/src/cli/context-stats.mjs +104 -0
- package/src/cli/context.mjs +8 -0
- package/src/cli/daemon.mjs +10 -1
- package/src/cli/export.mjs +3 -2
- package/src/cli/ingest-okf.mjs +12 -15
- package/src/cli/init.mjs +0 -85
- package/src/cli/lint.mjs +3 -2
- package/src/cli/plugin/copies.mjs +68 -0
- package/src/cli/plugin/create.mjs +22 -24
- package/src/cli/plugin/index.mjs +49 -3
- package/src/cli/plugin/search.mjs +19 -2
- package/src/cli/rebuild.mjs +2 -0
- package/src/cli/remember.mjs +23 -0
- package/src/cli/status.mjs +10 -3
- package/src/cli/update.mjs +3 -2
- package/src/core/action-predictor.mjs +161 -0
- package/src/core/app-deploy/runtime-cli/node.mjs.tmpl +20 -3
- package/src/core/app-deploy/source.mjs +16 -9
- package/src/core/automatic-brain-updates.mjs +6 -3
- package/src/core/brain-registry.mjs +19 -2
- package/src/core/brain-state.mjs +23 -0
- package/src/core/cli-commands.mjs +1 -0
- package/src/core/cli-maintenance.mjs +1 -1
- package/src/core/cli-semantic-index.mjs +1 -1
- package/src/core/command-surface.mjs +3 -3
- package/src/core/composed-command-resolution.mjs +21 -0
- package/src/core/config.mjs +84 -0
- package/src/core/confined-memory.mjs +93 -0
- package/src/core/context-telemetry.mjs +214 -0
- package/src/core/daemon-control.mjs +14 -43
- package/src/core/fast-recall.mjs +1 -1
- package/src/core/memory-brain.mjs +2 -0
- package/src/core/plugin-activation.mjs +38 -6
- package/src/core/plugin-bundle.mjs +54 -0
- package/src/core/plugin-catalog.mjs +1 -1
- package/src/core/plugin-context.mjs +1 -1
- package/src/core/plugin-contracts/card.mjs +69 -37
- package/src/core/plugin-loader.mjs +5 -6
- package/src/core/plugin-runner.mjs +13 -6
- package/src/core/plugin-runtime.mjs +5 -4
- package/src/core/plugin-setup.mjs +5 -3
- package/src/core/plugin-state-store.mjs +12 -1
- package/src/core/plugin-state.mjs +46 -5
- package/src/core/plugin-store.mjs +78 -3
- package/src/core/plugin-tasks.mjs +3 -1
- package/src/core/plugin-updates.mjs +117 -0
- package/src/core/plugin-vendor.mjs +10 -4
- package/src/core/project-brain.mjs +14 -41
- package/src/core/project-id.mjs +4 -1
- package/src/core/required-plugins.mjs +151 -13
- package/src/core/skills-registry.mjs +27 -3
- package/src/core/ssss-kernel-bridge.mjs +3 -1
- package/src/core/startup-health.mjs +66 -16
- package/src/core/surface.mjs +53 -46
- package/src/core/testing/plugin-fixture.mjs +69 -0
- package/src/core/vault-files.mjs +13 -0
- package/src/core/vault.mjs +4 -30
- package/src/server/index.mjs +13 -1
- package/src/server/invoke-decision-child.mjs +49 -0
- package/src/server/invoke-execution.mjs +87 -0
- package/src/server/invoke-policy.mjs +64 -0
- package/src/server/invoke-read-child.mjs +26 -0
- package/src/server/invoke-targets.mjs +49 -0
- package/src/server/invoke-write-child.mjs +19 -0
- package/src/server/routes/invoke.mjs +314 -0
- package/src/server/routes/plugins.mjs +1 -1
- package/scaffold/.agent/skills/plugins/SKILL.md +0 -57
- package/scaffold/.agent/skills/plugins/references/card-design.md +0 -55
- package/scaffold/.agent/skills/plugins/references/distribution.md +0 -23
- package/scaffold/.agent/skills/plugins/references/manifest-contract.md +0 -90
- package/scaffold/.agent/skills/plugins/references/optimized/index.md +0 -9
- package/scaffold/.agent/skills/plugins/references/optimized/manifest.json +0 -35
- package/scaffold/.agent/skills/plugins/references/optimized/original.md +0 -58
- package/scaffold/.agent/skills/plugins/references/optimized/section-01.md +0 -5
- package/scaffold/.agent/skills/plugins/references/optimized/section-02.md +0 -25
- package/scaffold/.agent/skills/plugins/references/optimized/section-03.md +0 -12
- package/scaffold/.agent/skills/plugins/references/optimized/section-04.md +0 -7
- package/scaffold/.agent/skills/plugins/references/optimized/section-05.md +0 -5
- package/scaffold/.agent/skills/total-recall/openwiki/README.md +0 -10
- package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +0 -5
- package/scaffold/.agent/skills/total-recall/openwiki/ide.md +0 -10
- package/scaffold/.agent/skills/total-recall/openwiki/memory.md +0 -11
- package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +0 -7
- package/scaffold/.agent/skills/total-recall/openwiki/skills.md +0 -7
- package/scaffold/openwiki/README.md +0 -10
- package/scaffold/openwiki/architecture.md +0 -5
- package/scaffold/openwiki/ide.md +0 -10
- package/scaffold/openwiki/memory.md +0 -11
- package/scaffold/openwiki/secrets.md +0 -7
- package/scaffold/openwiki/skills.md +0 -7
- package/src/core/okf-adapter.mjs +0 -559
- package/templates/openwiki/README.md +0 -10
- package/templates/openwiki/architecture.md +0 -5
- package/templates/openwiki/ide.md +0 -10
- package/templates/openwiki/memory.md +0 -11
- package/templates/openwiki/secrets.md +0 -7
- package/templates/openwiki/skills.md +0 -7
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Prompt 3 — Scaffold, then stop generating
|
|
2
|
+
|
|
3
|
+
## The prompt
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
total-recall plugin create <id> --name "<plain descriptive name>" --description "<one concrete sentence>"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
You receive a manifest, a CLI handler and a test.
|
|
10
|
+
|
|
11
|
+
**No UI is generated, deliberately.** A card face is a design act: it requires knowing the users, their tasks and the domain's vocabulary, which is Prompt 1's work. A generated stub is precisely what produced an ecosystem of identical cards, so the scaffold refuses to write one.
|
|
12
|
+
|
|
13
|
+
**The scaffold is not yet a valid plugin.** It has no card, and `plugin info` will say so. Do not treat a successful scaffold as finished work, and do not declare a card you have not designed.
|
|
14
|
+
|
|
15
|
+
## Correct means
|
|
16
|
+
|
|
17
|
+
- The repository exists at `tr-plugin-<id>` with a manifest carrying identity only.
|
|
18
|
+
- Its tests run.
|
|
19
|
+
- You know the face is outstanding, and that Prompts 4–6 are how it gets written.
|
|
20
|
+
|
|
21
|
+
## Then
|
|
22
|
+
|
|
23
|
+
Set the surfaces you chose in Prompt 2: add `cli.subcommands`, `secrets`, `configuration`, `status`, `branding`, and finally `ui.elements` when the face exists.
|
|
24
|
+
|
|
25
|
+
## Check
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
node <skill>/scripts/record.mjs . --manifest
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Reports the surfaces present and the surfaces outstanding. A plugin with no `ui.elements` fails — correctly — until Prompt 6 is done.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Prompt 4 — Write the CLI as an agent interface
|
|
2
|
+
|
|
3
|
+
The CLI is used by two audiences: an operator at a terminal, and **an agent** that discovers the plugin, calls it, and parses the result. Write for both.
|
|
4
|
+
|
|
5
|
+
## The prompt
|
|
6
|
+
|
|
7
|
+
In `plugin.json`, for every operation:
|
|
8
|
+
|
|
9
|
+
- a **name** in lowercase kebab-case, unique
|
|
10
|
+
- a **description** of what it does
|
|
11
|
+
- an **effect**: `read`, `write`, `destructive` or `send`
|
|
12
|
+
- **typed arguments**, each with a label — `string`, `number`, `boolean`, `choice` with options. Mark positionals, mark requireds.
|
|
13
|
+
- a **secret argument** is marked `secret = true`, and its value travels over stdin, never `argv`. One per command, a string, no flag.
|
|
14
|
+
- `output: "json"` unless the result is genuinely prose
|
|
15
|
+
- `timeout_ms` for anything slow
|
|
16
|
+
|
|
17
|
+
In `cli.mjs`:
|
|
18
|
+
|
|
19
|
+
- `--json` emits structured output on **every** subcommand, including those that default to text
|
|
20
|
+
- a failure prints the real error to **stderr** and exits **non-zero**
|
|
21
|
+
- `--help` answers **without resolving credentials**, so it works before setup
|
|
22
|
+
- no secret value reaches `argv`, stdout, stderr or a log
|
|
23
|
+
|
|
24
|
+
## Correct means
|
|
25
|
+
|
|
26
|
+
An agent can:
|
|
27
|
+
|
|
28
|
+
1. run `total-recall plugin library --json` and see this plugin's operations, their arguments and their effects;
|
|
29
|
+
2. choose an operation without reading prose;
|
|
30
|
+
3. call `total-recall <id> <op> <args> --json`;
|
|
31
|
+
4. parse the JSON;
|
|
32
|
+
5. tell success from failure from the exit code.
|
|
33
|
+
|
|
34
|
+
## Common wrong answers
|
|
35
|
+
|
|
36
|
+
- `--json` that only some subcommands honour.
|
|
37
|
+
- Resolving a credential at the top of `run()` so `--help` fails without a key.
|
|
38
|
+
- Printing a success message on a failed HTTP call.
|
|
39
|
+
- Taking an API key as `--key <value>`, which puts it in `argv`, in shell history and in the process list.
|
|
40
|
+
- Declaring an effect of `read` for an operation that mutates.
|
|
41
|
+
|
|
42
|
+
## Check
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
node <skill>/scripts/record.mjs . --manifest
|
|
46
|
+
node <skill>/scripts/record.mjs . --secrets
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then run `--help` and one `--json` call by hand and read the real output.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Prompt 5 — Declare credentials and settings
|
|
2
|
+
|
|
3
|
+
There is exactly **one** credential path in the whole system, and it belongs to the auth plugin.
|
|
4
|
+
|
|
5
|
+
## The prompt
|
|
6
|
+
|
|
7
|
+
**Credentials.** Every key the plugin reads goes in `secrets[]`:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
"secrets": [ { "key": "SERVICE_API_KEY", "description": "Reads and creates charges", "required": true } ]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Resolve it only through `total-recall auth secret`. The plugin has no store of its own, reads no ambient environment variable as a fallback, and accepts no key on `argv`.
|
|
14
|
+
|
|
15
|
+
**Settings.** Every setting goes in `configuration.fields` with its type and default. Field keys are camelCase and may not contain `password`, `secret`, `apiKey` or `token` — those belong in `secrets`.
|
|
16
|
+
|
|
17
|
+
**The card** reports a credential as *present* or *missing*. It never renders a value, and never renders an input that accepts one. To set a key, the card hands over the command:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
total-recall auth secret set SERVICE_API_KEY --stdin
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Correct means
|
|
24
|
+
|
|
25
|
+
- Exactly one credential path exists, and it is auth.
|
|
26
|
+
- The card has no code path that can receive a key value, so it cannot leak one.
|
|
27
|
+
- A setting a user must change is declared, so it is visible rather than buried in code.
|
|
28
|
+
|
|
29
|
+
## Why the card must not collect keys
|
|
30
|
+
|
|
31
|
+
The rule is not bureaucracy: one store means one rotation path and one audit trail. A per-plugin key screen violates it even when it writes to the same store, because it makes the plugin the place an operator thinks of when a key must change. It also creates a receive path for the secret inside plugin code, which is a leak surface that would otherwise not exist.
|
|
32
|
+
|
|
33
|
+
Evidence: a scaffolded panel rendered `<input type="password">` and dispatched the raw value to the host; a real card built three such screens.
|
|
34
|
+
|
|
35
|
+
## Check
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
node <skill>/scripts/record.mjs . --secrets
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Fails if a secret is reachable in `argv`, in stdout, in a log, or in the rendered card.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Prompt 6 — Design the card face by hand
|
|
2
|
+
|
|
3
|
+
Every plugin has a card face. It is bespoke owner code, written for this plugin and no other.
|
|
4
|
+
|
|
5
|
+
Full specification: [card](../references/card.md).
|
|
6
|
+
|
|
7
|
+
## The prompt
|
|
8
|
+
|
|
9
|
+
Work in this order. Write the reasoning into `ui/DESIGN.md` **before** writing component code.
|
|
10
|
+
|
|
11
|
+
1. Who uses it, and what are they in the middle of doing? Name the real tasks.
|
|
12
|
+
2. What must be visible without a single click? That decides the hierarchy.
|
|
13
|
+
3. What is the one primary action, and what is destructive?
|
|
14
|
+
4. What is the domain's own vocabulary? Use it. Never render a domain as "a list of commands".
|
|
15
|
+
5. Then choose the arrangement, and justify it in the plugin's own terms.
|
|
16
|
+
|
|
17
|
+
Then author `ui/card.js`:
|
|
18
|
+
|
|
19
|
+
- **One bespoke Web Component**, declared as the single element of kind `card` in `ui.elements`. No `card_layout`, no template name, no region or widget vocabulary.
|
|
20
|
+
- **Screens the plugin defines**, ordered by the plugin's own tasks, most-used first. Never one screen per subcommand.
|
|
21
|
+
- **Wide, not a column.**
|
|
22
|
+
- **Real data only.** Every value from `host.run(subcommand, args)`. Anything not returned renders an em dash with its reason.
|
|
23
|
+
- **Failures are states**, each rendered explicitly with the time of the last attempt.
|
|
24
|
+
- **Name a value's driver** when severity combines several measures, or a red tile beside a green figure reads as a contradiction.
|
|
25
|
+
- **The host's tokens.** The dashboard is dark: use the injected `--text-*`, `--bg-*`, `--border`, `--accent` and your brand tokens. Declare state colours once in a `:host` block and keep them legible on dark.
|
|
26
|
+
- **Accessible**: 44 px controls, keyboard reachable, visible focus, contrast-checked states, `prefers-reduced-motion` respected.
|
|
27
|
+
- **Severity never by colour alone** — pair every colour with a word.
|
|
28
|
+
- **The card consumes credentials, never collects them.**
|
|
29
|
+
|
|
30
|
+
## Correct means
|
|
31
|
+
|
|
32
|
+
1. Render at **360, 768 and 1280 px** — no horizontal overflow anywhere.
|
|
33
|
+
2. Render on the **real dashboard tokens** (dark), not on a white page.
|
|
34
|
+
3. Exercise every state: credential present, missing, provider unreachable, call rejected, empty, stale.
|
|
35
|
+
4. Put it beside another plugin's face. If you cannot tell which is which from the layout alone, it is not finished.
|
|
36
|
+
5. No secret value can reach the DOM in any state.
|
|
37
|
+
|
|
38
|
+
**Then look at it.** Every defect in the anti-patterns catalogue that begins "only looking reveals" was invisible to a schema check.
|
|
39
|
+
|
|
40
|
+
## Check
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
node <skill>/scripts/record.mjs . --card
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then render and inspect it yourself. A passing check is not a design.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Prompt 7 — Extract the scaffold, if another application wants the capability
|
|
2
|
+
|
|
3
|
+
Only when Prompt 1 answered yes. Full specification: [scaffold](../references/scaffold.md).
|
|
4
|
+
|
|
5
|
+
## The prompt
|
|
6
|
+
|
|
7
|
+
A **scaffold** is the capability extracted so a builder can put it into their own application. It is **not a copy of the plugin**. The plugin is the reference implementation.
|
|
8
|
+
|
|
9
|
+
Structure the repository as one source with two artifacts:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
tr-plugin-<id>/
|
|
13
|
+
capability/ ← the scaffold payload: exported functions, no host
|
|
14
|
+
index.mjs
|
|
15
|
+
cli.mjs ← the Total Recall entry: parse argv, resolve credentials, call capability/
|
|
16
|
+
ui/ ← the face; Total Recall only
|
|
17
|
+
test/
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Declare it:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
"capability": { "entry": "capability/index.mjs", "exports": ["…"] }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Move the domain logic — request construction, signature verification, payload building, response mapping — out of the CLI handler and into `capability/`. `cli.mjs` becomes a thin adapter.
|
|
27
|
+
|
|
28
|
+
The capability must be:
|
|
29
|
+
|
|
30
|
+
- **explicitly parameterised** — no `argv`, no `process.env`, no module-level singleton
|
|
31
|
+
- **free of environment defaults** — a missing `successUrl` is the caller's error, never a silent `example.com`
|
|
32
|
+
- **transport-injected**, so the app uses its own HTTP client and tests run offline
|
|
33
|
+
- **returning data**, not printing text
|
|
34
|
+
- **throwing errors**, not setting `process.exitCode`
|
|
35
|
+
- **free of Total Recall** — no `total-recall`, no `TOTAL_RECALL_BIN`, no auth store
|
|
36
|
+
- **free of UI** — no card, no `DESIGN.md`, no logo
|
|
37
|
+
|
|
38
|
+
Write `BRICK.md`: what to import, what to pass, where credentials come from, what comes back.
|
|
39
|
+
|
|
40
|
+
## Correct means
|
|
41
|
+
|
|
42
|
+
A builder can lay the capability into their application and use it **without rewriting it**, with no Total Recall present, every environment value supplied by them.
|
|
43
|
+
|
|
44
|
+
The test is not "does it run". It is **"can a builder use this as it stands?"** Evidence: 19 of 34 plugins currently export only `run`, so a builder must extract the logic by hand — exactly the work a scaffold exists to remove.
|
|
45
|
+
|
|
46
|
+
## Check
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
node <skill>/scripts/record.mjs . --scaffold
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Asserts the capability loads with `TOTAL_RECALL_BIN` unset and no auth store, that no Total Recall string is reachable from it, that no placeholder value ships, and that its tests fail if it spawns a process.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Prompt 8 — Verify, then report
|
|
2
|
+
|
|
3
|
+
Run every check and report the **real** result. A passing health probe is not a test result.
|
|
4
|
+
|
|
5
|
+
## The prompt
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
node <skill>/scripts/record.mjs . --json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Then work the list, and record the actual outcome of each. Do not tick a box you did not test.
|
|
12
|
+
|
|
13
|
+
- [ ] `total-recall plugin info <id>` reports no errors and no warnings.
|
|
14
|
+
- [ ] Every surface the function needs is declared, and nothing unused.
|
|
15
|
+
- [ ] Every operation has typed arguments and an effect.
|
|
16
|
+
- [ ] `--json` works on every subcommand; failures exit non-zero; `--help` works without credentials.
|
|
17
|
+
- [ ] No secret value appears in `argv`, stdout, stderr, a log or the card state.
|
|
18
|
+
- [ ] The card declares exactly one element of kind `card`, and no `card_layout`.
|
|
19
|
+
- [ ] Every value on the card came from a command; failures render as failures; unknown values render an em dash.
|
|
20
|
+
- [ ] The card renders on the real dark dashboard at 360, 768 and 1280 px with no horizontal overflow.
|
|
21
|
+
- [ ] Every state renders: credential present, missing, provider unreachable, rejected call, empty, stale.
|
|
22
|
+
- [ ] The suite passes — per-subcommand unit tests including failure paths, a manifest test, a secrets test.
|
|
23
|
+
- [ ] No seeded value, placeholder or example default survives anywhere in the plugin.
|
|
24
|
+
- [ ] If a scaffold exists: it runs with `TOTAL_RECALL_BIN` unset, and its tests fail if it spawns a process.
|
|
25
|
+
|
|
26
|
+
## Then, honestly
|
|
27
|
+
|
|
28
|
+
Bring `README.md` to describe the plugin **as it actually is**:
|
|
29
|
+
|
|
30
|
+
- what it does, in its own words
|
|
31
|
+
- the real commands
|
|
32
|
+
- where credentials come from
|
|
33
|
+
- **anything still outstanding**, stated plainly
|
|
34
|
+
|
|
35
|
+
No claim of a capability the plugin does not have. A stale warning about a fixed bug is itself a defect: one plugin's README described two reporting bugs as unfixed long after both were repaired and verified.
|
|
36
|
+
|
|
37
|
+
## Correct means
|
|
38
|
+
|
|
39
|
+
Every claim in the report is backed by a command you ran, and anything you could not verify is named as unverified rather than omitted.
|
|
40
|
+
|
|
41
|
+
## Check
|
|
42
|
+
|
|
43
|
+
A second reader can reproduce every claim from the report alone.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Anti-patterns
|
|
2
|
+
|
|
3
|
+
Every entry below was observed in a real plugin in this ecosystem, with the evidence. They are the failure modes this recipe exists to prevent. Each is a rejection, not a preference.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fabrication
|
|
8
|
+
|
|
9
|
+
**Seeded readings.** A card constructor held a literal hostname, uptime, load average and `usedPercent: 99.1`, with the comment `// Retain fallback telemetry`. A host at 99.1% memory is in distress; this was a false alarm, and one machine's hostname shipped to every node.
|
|
10
|
+
|
|
11
|
+
**Invented API results.** A card's fallback branch fabricated `cs_live_…` session ids, `pi_…` intent ids, a payment URL and a latency of "42ms" when `host.run` was unavailable. That branch was not a degraded mode; it was a mock payment terminal.
|
|
12
|
+
|
|
13
|
+
**Hard-coded claims.** A header rendered `Stripe API (Live Secret)` and `HMAC-SHA256 Verified` with no command behind either. A merchant with no key and no webhook secret saw an identical card.
|
|
14
|
+
|
|
15
|
+
**Fixed text where a command belongs.** `card_face.metrics` held literal strings such as `ONLINE`. The validator warns on it; the card must never carry it.
|
|
16
|
+
|
|
17
|
+
> **Rule.** A value appears only if a command or the host returned it. If a source fails, show the failure and when it was last attempted.
|
|
18
|
+
|
|
19
|
+
## Reassuring defaults
|
|
20
|
+
|
|
21
|
+
The single most damaging pattern found.
|
|
22
|
+
|
|
23
|
+
- A card read `data.worktree.unstaged`, `data.synced` and `data.upstream.status` — **none of which the command returns**. Every value fell through to its default.
|
|
24
|
+
- The defaults were `'Up to date'` and `'Synced'`.
|
|
25
|
+
- Therefore a repository with **three unpushed commits rendered as "Up to date."**
|
|
26
|
+
|
|
27
|
+
Related: a missing upstream comparison becoming `unpushedCommits: 0` instead of unknown.
|
|
28
|
+
|
|
29
|
+
> **Rule.** Unknown renders an em dash with its reason. `0` and `unknown` are different facts, and a reassurance the plugin has not verified is a lie.
|
|
30
|
+
|
|
31
|
+
## Credential violations
|
|
32
|
+
|
|
33
|
+
- A scaffolded panel rendered `<input type="password">` for an API key and dispatched `save-token` with the raw value — a second credential store, and a path the key could leak from.
|
|
34
|
+
- A card built three separate key-entry screens.
|
|
35
|
+
- A plugin's error text told the *application's* users to run a Total Recall command.
|
|
36
|
+
|
|
37
|
+
> **Rule.** One credential path exists in the whole plugin, and it is `total-recall auth secret`. The card reports present or missing, never renders a value, and never collects one.
|
|
38
|
+
|
|
39
|
+
## Templates
|
|
40
|
+
|
|
41
|
+
- Core enforced a closed allowlist of seven layouts, so a bespoke arrangement could not be declared even in principle.
|
|
42
|
+
- One engine rendered every card through a shared widget vocabulary. **24 of 34 plugins shared a byte-identical card signature**; Stripe, Domains and Tickets — three unrelated domains — were the same layout.
|
|
43
|
+
- 30 plugins shipped a real bespoke `card.js` that the card face never mounted.
|
|
44
|
+
- The scaffolder generated a stub panel and an empty `DESIGN.md`, which is the mechanism that produced the uniformity.
|
|
45
|
+
|
|
46
|
+
> **Rule.** No layout vocabulary anywhere. A face that could be dropped onto another plugin is a failed face.
|
|
47
|
+
|
|
48
|
+
## Invented brands
|
|
49
|
+
|
|
50
|
+
| Plugin | Invented wordmark | Real identity |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| Stripe wrapper | `PAYFORGE` | Stripe's own |
|
|
53
|
+
| git-sentinel | `[BLAME//GAME]` | plain descriptive name |
|
|
54
|
+
| system-monitor | `[PULSE//CHECK]` | plain descriptive name |
|
|
55
|
+
|
|
56
|
+
Two of the three also carried colour values contradicting their own manifest.
|
|
57
|
+
|
|
58
|
+
> **Rule.** A wrapper of someone else's API keeps that provider's name and brand. A first-party plugin uses a plain descriptive name and its own mark. Never an emoji, never a generic icon.
|
|
59
|
+
|
|
60
|
+
## Values that break the capability elsewhere
|
|
61
|
+
|
|
62
|
+
- `const successUrl = "https://example.com/checkout/success"` shipped inside a vendorable plugin. An app scaffolding it would send paying customers to a placeholder domain.
|
|
63
|
+
- Defaults of `2000`, `"General Admission Ticket"` and `"usd"` — the author's examples — travelling into every copy.
|
|
64
|
+
- A domain default of `clean[1] || 'example.com'`.
|
|
65
|
+
|
|
66
|
+
> **Rule.** Every environment value is the caller's to supply. No placeholder, no author default, no example value in anything a builder might take.
|
|
67
|
+
|
|
68
|
+
## Host leaks into a scaffold
|
|
69
|
+
|
|
70
|
+
- A vendored brick shelled out to the `total-recall` binary for every credential read: with `TOTAL_RECALL_BIN=/nonexistent` it failed with `Auth credential read failed`.
|
|
71
|
+
- It shipped `ui/card.js`, whose only data channel is `host.run` — a host that application does not have.
|
|
72
|
+
- **19 of 34 plugins** carry this runtime dependency, and every one of them is vendorable.
|
|
73
|
+
|
|
74
|
+
> **Rule.** A scaffold runs with Total Recall absent. Its tests must fail if it spawns a process.
|
|
75
|
+
|
|
76
|
+
## Presentation defects only looking reveals
|
|
77
|
+
|
|
78
|
+
- `.figure[data-tone]` CSS was defined but the tone was never set on the wrapper, so all four accent borders rendered grey. Dead CSS, invisible to a schema check.
|
|
79
|
+
- A fleet tile showed a **red border beside a green load figure**: severity combined load and memory, but the tile only displayed load, so a memory-driven critical read as a contradiction.
|
|
80
|
+
- A selection outline collided with the severity border, so a critical tile appeared to have a green edge.
|
|
81
|
+
- Bars collapsed to slivers after a layout change, because a three-column grid left no room for the track.
|
|
82
|
+
- **"USD" appeared twice** — as a suffix in the amount field and on the currency button immediately beside it.
|
|
83
|
+
- A long payment URL ran into its copy button at narrow widths.
|
|
84
|
+
|
|
85
|
+
> **Rule.** Render it, look at it, measure it. A passing schema check is not a design.
|
|
86
|
+
|
|
87
|
+
## Dark-surface blindness
|
|
88
|
+
|
|
89
|
+
The dashboard is **dark-only** (`--text-primary: #f1f5f9`, zero `prefers-color-scheme` rules), but cards were authored and verified against a white test page. Light-theme state colours were therefore unreadable in production, including the verdict line.
|
|
90
|
+
|
|
91
|
+
> **Rule.** Design and verify on the tokens the host actually injects.
|
|
92
|
+
|
|
93
|
+
## Colour-only severity
|
|
94
|
+
|
|
95
|
+
Severity conveyed by hue alone, with no word and no value. Indistinguishable under colour blindness and in a screenshot without a legend.
|
|
96
|
+
|
|
97
|
+
> **Rule.** Every colour is paired with a word.
|
|
98
|
+
|
|
99
|
+
## Host-derived screens
|
|
100
|
+
|
|
101
|
+
The host built one screen per CLI subcommand, so every plugin's screens were its command list. That is the template failure at a higher level.
|
|
102
|
+
|
|
103
|
+
> **Rule.** The plugin defines its screens, ordered by its own tasks, most-used first.
|
|
104
|
+
|
|
105
|
+
## Stale documentation
|
|
106
|
+
|
|
107
|
+
A plugin's README described two reporting bugs as unfixed and pointed at a correction tracker. **Both had already been fixed and were verified against hostile input** — a file named `" leading-space.txt"` parses correctly, and a missing upstream yields `null`, not `0`. The stale warning misrepresented a working plugin and sent readers to a dead end.
|
|
108
|
+
|
|
109
|
+
> **Rule.** Documentation describes the plugin as it is now. A warning that no longer applies is a defect.
|
|
110
|
+
|
|
111
|
+
## Environments that return confidently wrong data
|
|
112
|
+
|
|
113
|
+
`cat /proc/meminfo 2>/dev/null | head -4 || vm_stat` **never fell through on macOS**, because `cat missing | head` exits 0. The macOS memory branch was dead code, and the plugin reported a false `100%` where the truth was `58.3%`.
|
|
114
|
+
|
|
115
|
+
> **Rule.** Test the condition, not the exit status. Verify a computed figure against an independent calculation before trusting it.
|
|
116
|
+
|
|
117
|
+
## Two definitions of one thing
|
|
118
|
+
|
|
119
|
+
- **30 of 30 plugin repositories** carried a stale copied `auth` plugin declaring a `yaml` dependency with no `node_modules`, so every secrets and mesh call from a plugin repository crashed.
|
|
120
|
+
- The scaffold held real directory copies of the nine default plugins rather than links, so each drifted independently.
|
|
121
|
+
- Two skills, `plugin` and `plugins`, described the same thing.
|
|
122
|
+
|
|
123
|
+
> **Rule.** One definition, referenced everywhere. A second copy is a second source of truth, and it will diverge.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# The card face
|
|
2
|
+
|
|
3
|
+
Every plugin has a card face. It is the plugin's interface in the Total Recall dashboard, and it is **bespoke owner code**: written by hand, for that one plugin, from that plugin's own function and brand.
|
|
4
|
+
|
|
5
|
+
There is no template, no layout vocabulary, no widget set, no region list. Core rejects `card_layout` outright. A face that could be dropped onto a different plugin is a failed face.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What the host provides, and nothing more
|
|
10
|
+
|
|
11
|
+
- a **sandboxed frame** — opaque origin, `default-src 'none'`, `connect-src 'none'`, no dashboard DOM and no storage
|
|
12
|
+
- a frozen **`host`** object whose `run(subcommand, args, options)` is the only channel out
|
|
13
|
+
- the **design tokens** it injects into the frame
|
|
14
|
+
- the **drawer**, which carries settings and credentials so the face stays focused
|
|
15
|
+
|
|
16
|
+
Everything else — structure, vocabulary, hierarchy, controls, visual language — is yours to design for this plugin and no other.
|
|
17
|
+
|
|
18
|
+
## The design process
|
|
19
|
+
|
|
20
|
+
Work in this order, and write the reasoning into `ui/DESIGN.md` **before** writing component code.
|
|
21
|
+
|
|
22
|
+
1. **Who uses it, and what are they in the middle of doing?** Name the two or three real tasks. A payments operator reconciling a payout is not the same as one hunting a suspicious charge.
|
|
23
|
+
2. **What must be visible without a single click?** That decides the hierarchy. A DNS plugin shows the zone and its records; a telephony plugin shows line state and the active call; a telemetry plugin shows which machine is struggling.
|
|
24
|
+
3. **What is the one primary action, and what is destructive?** A destructive action gets confirmation, and never the visual weight of the obvious next step.
|
|
25
|
+
4. **What is the domain's own vocabulary?** Use it. Amounts, dates and states in the arrangement an accountant expects; peers, routes and reachability for a mesh. Never render a domain as "a list of commands".
|
|
26
|
+
5. **Then choose the arrangement**, and justify it in the plugin's own terms.
|
|
27
|
+
|
|
28
|
+
`ui/DESIGN.md` carries a real `tokens:` block matching the manifest, and the reasoning above. See the surfaces reference for the token fields.
|
|
29
|
+
|
|
30
|
+
## Screens
|
|
31
|
+
|
|
32
|
+
A plugin with several jobs carries them across **screens the operator flips through**. The plugin defines them.
|
|
33
|
+
|
|
34
|
+
- Order them by the plugin's own tasks, **most-used first**.
|
|
35
|
+
- The first screen answers what the operator opened the card to do.
|
|
36
|
+
- **Never one screen per subcommand.** That is the host's command list wearing a costume, and it is what makes unrelated plugins look alike.
|
|
37
|
+
- Never a screen set the host derives or imposes.
|
|
38
|
+
|
|
39
|
+
## Rules that make a face real
|
|
40
|
+
|
|
41
|
+
- **Real data only.** A value appears only if a command or the host returned it. Never a seeded reading, a placeholder, an example default or an `ONLINE` badge. If a source fails, show the failure and when it was last attempted.
|
|
42
|
+
- **Unknown is an em dash with a reason**, never a zero and never a reassuring word. `0 unpushed commits` and `unknown, no upstream configured` are different facts.
|
|
43
|
+
- **Failures are states**, first-class and rendered like any other. Missing credential, unreachable provider, rejected call, stale reading.
|
|
44
|
+
- **A value's driver is named.** If severity combines several measures, say which one drove it, or a red tile beside a green figure reads as a contradiction.
|
|
45
|
+
- **Every declared operation is reachable**, and its effect is visible — a `read` must not look like a `send`.
|
|
46
|
+
- **Destructive actions state their target and require confirmation.**
|
|
47
|
+
- **The card consumes credentials, never collects them.** Report present or missing; never render a value; never render an input that accepts one. Hand over the auth command to run instead.
|
|
48
|
+
- **Tokens only.** No hard-coded hex outside the single `:host` token block. The dashboard is **dark**, so the injected `--text-*`, `--bg-*`, `--border` and `--accent` values are light-on-dark; tune your state colours for that surface and keep them legible.
|
|
49
|
+
- **Wide, not a column.** A narrow column forces vertical stacking, which is what makes different plugins look alike.
|
|
50
|
+
- **Severity never by colour alone** — pair every colour with a word.
|
|
51
|
+
- **Accessible.** Controls at least 44 px, keyboard reachable, visible focus, contrast-checked state colours, `prefers-reduced-motion` respected.
|
|
52
|
+
- **No decoration.** No ambient animation, no emoji as icons, no hardware-skin imitation, no fake equaliser on a plugin that has no audio. Motion only when it reports a state change.
|
|
53
|
+
- **Copy.** No filler words, and no sentence ends with a preposition.
|
|
54
|
+
|
|
55
|
+
## Shape of the component
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
export class ExampleCard extends HTMLElement {
|
|
59
|
+
get screens() { return [ { id: 'charge', label: 'Charge' }, /* … most-used first */ ] }
|
|
60
|
+
connectedCallback() { /* attach shadow, render frames with em dashes, then read */ }
|
|
61
|
+
disconnectedCallback() { /* clear timers; never leave one running */ }
|
|
62
|
+
async read() { /* host.run(...) → paint; a rejection clears figures and shows the error */ }
|
|
63
|
+
}
|
|
64
|
+
if (!customElements.get('tr-example-card')) customElements.define('tr-example-card', ExampleCard)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- Values are painted **after** a command returns. Before the first read, the frames show em dashes.
|
|
68
|
+
- A failed read **clears** every figure rather than retaining the previous one as though current.
|
|
69
|
+
- Timers are cleared on disconnect.
|
|
70
|
+
|
|
71
|
+
## Verifying a face
|
|
72
|
+
|
|
73
|
+
Run `scripts/record.mjs --card` for the mechanical checks, then look at it:
|
|
74
|
+
|
|
75
|
+
1. Render at **360, 768 and 1280 px**. No horizontal overflow at any width.
|
|
76
|
+
2. Render on the **real dashboard tokens** (dark), not on a white test page. A face that only reads on white is broken in production.
|
|
77
|
+
3. Exercise every state: credential present, credential missing, provider unreachable, call rejected, empty, stale.
|
|
78
|
+
4. Put it beside another plugin's face. If you cannot tell which is which from the layout alone, it is not finished.
|
|
79
|
+
5. Confirm no secret value can reach the DOM in any state.
|
|
80
|
+
|
|
81
|
+
## What a face must never do
|
|
82
|
+
|
|
83
|
+
- Never seed a value, a hostname, a reading or a status word.
|
|
84
|
+
- Never show a reassuring default in place of an unknown.
|
|
85
|
+
- Never render a secret, in any state, for any reason.
|
|
86
|
+
- Never collect a credential.
|
|
87
|
+
- Never present a command name as the primary structure.
|
|
88
|
+
- Never derive its screens from the command list.
|
|
89
|
+
- Never style a destructive action as the obvious next step.
|
|
90
|
+
- Never render as a narrow column when width is available.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# The scaffold
|
|
2
|
+
|
|
3
|
+
A plugin's capability may be wanted inside a **different application** — someone else's app, which owns the code and ships without Total Recall. The **scaffold** is that capability, extracted so a builder can put it into their application and use it.
|
|
4
|
+
|
|
5
|
+
The plugin is the reference implementation. The scaffold is what a builder takes so nobody starts from zero.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## A scaffold is not a copy of the plugin
|
|
10
|
+
|
|
11
|
+
Measured against the current ecosystem, vendoring a plugin copies its whole tree, and the result is unusable in an app:
|
|
12
|
+
|
|
13
|
+
| What a builder receives | Why it fails |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| the card face | its only data channel is `host.run`, a Total Recall host the app does not have |
|
|
16
|
+
| an auth client that shells out to `total-recall` | the app breaks the moment Total Recall is absent — `Auth credential read failed` |
|
|
17
|
+
| request logic inlined in `argv`-parsing handlers | there is nothing to import; 19 of 34 plugins export only `run` |
|
|
18
|
+
| `https://example.com/checkout/success`, a default amount of `2000` | a real app would send paying customers to a placeholder domain |
|
|
19
|
+
|
|
20
|
+
The test is not "does it run". It is **"can a builder lay this in without rewriting it?"**
|
|
21
|
+
|
|
22
|
+
## What a scaffold contains
|
|
23
|
+
|
|
24
|
+
**The capability, as code the application imports:**
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
// capability/index.mjs — exported, importable, host-free
|
|
28
|
+
export async function createCheckoutSession({
|
|
29
|
+
secretKey, // supplied by the caller; never read from a store
|
|
30
|
+
amount, currency,
|
|
31
|
+
successUrl, cancelUrl, // the caller's URLs; never a placeholder
|
|
32
|
+
productName,
|
|
33
|
+
fetchImpl = fetch, // injectable: the app uses its own client, tests run offline
|
|
34
|
+
}) { /* … returns data … */ }
|
|
35
|
+
|
|
36
|
+
export function verifyWebhookSignature({ payload, signatureHeader, secret }) { /* … */ }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The rules the shape enforces:
|
|
40
|
+
|
|
41
|
+
- **Explicit parameters.** No `argv`, no `process.env`, no module-level singleton, no ambient credential.
|
|
42
|
+
- **No defaults for environment values.** A missing `successUrl` is the caller's error to fix, never a silent `example.com`.
|
|
43
|
+
- **Injected transport.** The app supplies its own HTTP client; a test needs no network.
|
|
44
|
+
- **Returned data, not printed text.** Printing is the CLI's job.
|
|
45
|
+
- **Thrown errors, never `process.exitCode`.** The application decides how to fail.
|
|
46
|
+
- **No Total Recall.** The capability does not know it exists.
|
|
47
|
+
- **No card, no design tokens, no logo.** There is no dashboard to render them.
|
|
48
|
+
|
|
49
|
+
**Plus a builder entry point** — `BRICK.md`, short and concrete:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
# Checkout capability
|
|
53
|
+
|
|
54
|
+
Copy `capability/` into your app.
|
|
55
|
+
|
|
56
|
+
import { createCheckoutSession } from './capability/index.mjs'
|
|
57
|
+
|
|
58
|
+
await createCheckoutSession({
|
|
59
|
+
secretKey: process.env.STRIPE_SECRET_KEY, // your own configuration, never ours
|
|
60
|
+
amount: 2500, currency: 'usd',
|
|
61
|
+
successUrl: `${origin}/checkout/success`,
|
|
62
|
+
cancelUrl: `${origin}/checkout/cancel`,
|
|
63
|
+
productName: 'General admission',
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
It states what to import, what to pass, where credentials come from, and what comes back. Not a description of a CLI.
|
|
68
|
+
|
|
69
|
+
## What a scaffold never contains
|
|
70
|
+
|
|
71
|
+
- the card face, `ui/DESIGN.md`, a logo
|
|
72
|
+
- the plugin manifest — a manifest of Total Recall commands, secrets and a card means nothing in an app
|
|
73
|
+
- any call to `total-recall`, `TOTAL_RECALL_BIN`, or an auth store
|
|
74
|
+
- `argv` parsing or `console.log` as its output
|
|
75
|
+
- a placeholder URL, an example default, or the author's own values
|
|
76
|
+
- a message telling the app's users to install another product
|
|
77
|
+
|
|
78
|
+
## How the plugin presents it
|
|
79
|
+
|
|
80
|
+
One source, two artifacts, separated by directory and declared in the manifest:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
tr-plugin-<id>/
|
|
84
|
+
capability/ ← the scaffold payload
|
|
85
|
+
index.mjs
|
|
86
|
+
cli.mjs ← the Total Recall entry; parses argv, resolves credentials, calls capability/
|
|
87
|
+
ui/ ← the face; Total Recall only
|
|
88
|
+
test/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
"capability": { "entry": "capability/index.mjs", "exports": ["createCheckoutSession", "verifyWebhookSignature"] }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- `capability` present → the plugin is vendorable as a scaffold.
|
|
96
|
+
- `capability` absent → vendoring reports that this plugin has no separable capability, instead of copying a program into the app.
|
|
97
|
+
|
|
98
|
+
The CLI becomes a **thin adapter**: parse arguments, resolve credentials through auth, call the capability, format the output. The domain logic lives in `capability/` and is exercised by both.
|
|
99
|
+
|
|
100
|
+
## Verifying a scaffold
|
|
101
|
+
|
|
102
|
+
- [ ] It runs with `TOTAL_RECALL_BIN` unset and no auth store present.
|
|
103
|
+
- [ ] Every exported function is callable with caller-supplied arguments and an injected transport.
|
|
104
|
+
- [ ] No `total-recall` string is reachable from the payload.
|
|
105
|
+
- [ ] No `example.com`, no author default, no placeholder value.
|
|
106
|
+
- [ ] No `ui/`, no `card.js`, no manifest.
|
|
107
|
+
- [ ] The suite fails if the capability spawns a process — proving standalone operation rather than assuming it.
|
|
108
|
+
- [ ] Vendored into a clean app, its suite passes there.
|
|
109
|
+
|
|
110
|
+
## Distribution
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
"distribution": { "tier": "official", "license": "open" }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`tier`: `core`, `official`, `personal`, `community`. `license`: `open` (vendorable and shareable) or `closed` (an operated service behind a thin client).
|
|
117
|
+
|
|
118
|
+
Monetized plugins state their model in `monetization` — `model`, `price_cents` or `take_rate_basis_points`, `period`, `license_key_secret`, `checkout_url`. **Never rely on a local license check for protection**: a closed plugin's value lives on the server, not in the client.
|
|
119
|
+
|
|
120
|
+
A third party publishes with the same manifest and the same conformance checks. A store lists signed, immutable versions and never runs plugin code.
|