@m0saic/knowledge 0.2.0 → 0.3.0
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/docs/runtime/cli-usage.md +109 -34
- package/docs/templates/recursion-nested-rendering.md +18 -2
- package/docs/templates/reference/prop-bindings.md +121 -0
- package/docs/templates/reference/template-flags.md +19 -0
- package/docs/templates/standalone-pack-authoring.md +67 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ Root `COMMANDS.md` (monorepo document, not published) documents the inline-DSL s
|
|
|
17
17
|
|
|
18
18
|
## Entry routing — inline-DSL mode is first-class
|
|
19
19
|
|
|
20
|
-
Before commander ever runs, `index.ts
|
|
20
|
+
Before commander ever runs, `index.ts:~7279-7285` inspects `process.argv[2]`:
|
|
21
21
|
`isInlineM0()` (`inlineDsl.ts:36-41`) treats ANY first arg that is not a flag and
|
|
22
22
|
not in `KNOWN_SUBCOMMANDS` as an inline m0 string and dispatches to
|
|
23
23
|
`handleInlineDsl` instead of `program.parseAsync`.
|
|
@@ -26,7 +26,7 @@ not in `KNOWN_SUBCOMMANDS` as an inline m0 string and dispatches to
|
|
|
26
26
|
m0saic "2(1,1)" # static wireframe → output.png (default mode "wire")
|
|
27
27
|
m0saic "2(1,1)" --anim # animated wireframe → output.mp4
|
|
28
28
|
m0saic "2(1,1)" --wire # explicit static (same as default)
|
|
29
|
-
m0saic layout.m0 # .m0 / .m0c paths also accepted (index.ts
|
|
29
|
+
m0saic layout.m0 # .m0 / .m0c paths also accepted (index.ts:~7184-7270)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
- Args after the string parse via `parseInlineArgs` (`inlineDsl.ts:123`): `-w/-h`
|
|
@@ -34,39 +34,48 @@ m0saic layout.m0 # .m0 / .m0c paths also accepted (index.ts:5787-5805)
|
|
|
34
34
|
`--save-m0`, `--prefer-pretty-m0`/`--prefer-canonical-m0`, `--disable-ui`,
|
|
35
35
|
`--format`, `--alpha`, `--validate-only`, `--report`, `--toolchain`, plus the
|
|
36
36
|
dev-gated dump flags (below).
|
|
37
|
-
- The string is validated with `validateM0String` before render (index.ts
|
|
37
|
+
- The string is validated with `validateM0String` before render (index.ts:~7222);
|
|
38
38
|
whitespace is stripped. Always shell-quote the DSL (parens/brackets).
|
|
39
39
|
- Output extension is coerced to match mode (`resolveInlineOutput`,
|
|
40
40
|
`inlineDsl.ts:86` — `--anim -o out.png` → `out.mp4`).
|
|
41
41
|
- Mode maps onto the wireframe handlers: `wire` → `handleWireframe`, `anim` →
|
|
42
|
-
`handleAnimatedWireframe` (index.ts
|
|
42
|
+
`handleAnimatedWireframe` (index.ts:~7264-7266).
|
|
43
43
|
|
|
44
44
|
## Command table
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
`registerTelemetryCommand(program)` (index.ts
|
|
48
|
-
Two of the
|
|
46
|
+
18 `.command(` registrations in `index.ts` + `telemetry` registered by
|
|
47
|
+
`registerTelemetryCommand(program)` (index.ts:~7138 → `utils/telemetry.ts:~372`).
|
|
48
|
+
Two of the 18 sit inside `if (DEV_MODE) { … }` and are absent from the public
|
|
49
49
|
help — see the DEV-GATED rows below.
|
|
50
50
|
|
|
51
|
+
> **Line numbers are loose signposts, written `~NNNN`** (founder ruling
|
|
52
|
+
> 2026-09-25). `index.ts` is ~7,200 lines and every number in this doc had drifted
|
|
53
|
+
> within weeks. **The greppable symbol is the anchor; the number only says
|
|
54
|
+
> roughly where to look.** A number that has moved is not a defect and does not
|
|
55
|
+
> earn a fix pass — correct one when you are editing that row anyway, and
|
|
56
|
+
> otherwise leave it.
|
|
57
|
+
|
|
51
58
|
| Command (index.ts line) | What it does |
|
|
52
59
|
|---|---|
|
|
53
|
-
| `make <input>` (
|
|
60
|
+
| `make <input>` (~5095) | Primary render: template id / `.mosaic` / `.mosaicx` → video or image. Full flag surface below. |
|
|
54
61
|
| `hello-world` | The first render — a thin alias for `make @m0saic/hello-world/v1` (the brand card; output defaults to `hello-world.mp4`). With `--template-repo <path...>` / `--community-repo <path>` it renders the loaded repo's **front door** instead: the first repo (load order) whose `repo.helloWorld` names a template that registered, announced as `Front door: <id> — <repo>'s hello-world`; no repos, or none that name one → the core card with a one-line why. Takes `-o -w -h --fps --durationMs --props --quiet --verbose` (no engine surface). Picker: the CLI source (not published). |
|
|
55
|
-
| `make-wireframe` (
|
|
56
|
-
| `make-wireframe-animated` (
|
|
57
|
-
| `flatten <input>` (
|
|
58
|
-
| `resolve <input>` (
|
|
59
|
-
| `list-templates` (
|
|
60
|
-
| `doctor <repoDir>` | **Public since 0.2.0** (dev-gated at 0.1.0; founder ruling 2026-09-16, BURN-DOWN D5 → publish). Run the template conventions over a template repo folder — the same checks its build gate (`tools/check-registry.mjs`) runs, from outside (agents, reviewers, forks, CI on a fork): definition-time + render-time conventions (the 13 throw-posture ones incl. `latticeSmooth`) and, when the repo commits `layout-fingerprints/` or `.layout.m0` sidecars, the fingerprint diff. `--json` (one object: `ok, repo, mode, loadDiagnostics, rendered, skipped, errors, warnings, notes, fingerprints`), `--sweep` (also render on the standard 1080p canvases → `canvasEnvelope`), `--entry <path>`, `--src <dir>`. Loads through `loadTemplateRepoFromPath` under the external origin scope, so conventions RECORD rather than throw and one bad template never hides the rest. **Runs the repo's template module bodies in-process** — prints a trust warning to stderr (not under `--json`). Exit 1 on any error-severity finding or a fatal load diagnostic. It is the pack-publishing contract for community/starter authors ("the publish requirement for packs"). Impl: the CLI source (not published); locked public by `__tests__/cli.surface.test.js`. |
|
|
61
|
-
| `browse-templates` (
|
|
62
|
-
| `momo <message…>` (
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
62
|
+
| `make-wireframe` (~5462) | Static wireframe PNG from `--m0 <string>` or `--mfile <path>`. |
|
|
63
|
+
| `make-wireframe-animated` (~5568) | Animated wireframe MP4 from `--m0`/`--mfile`; extra `--disable-ui`. |
|
|
64
|
+
| `flatten <input>` (~5680) | Inline all `type:mosaic` children of a `.mosaic`/template into one flat JSON doc — no render. |
|
|
65
|
+
| `resolve <input>` (~5800) | `.mosaicx` recipe → resolved `.mosaic` provenance doc, no ffmpeg; `--flatten` also inlines children. |
|
|
66
|
+
| `list-templates` (~5946) | Print registered template ids (`--json`); annotates primitive/internal/deprecated. |
|
|
67
|
+
| `doctor <repoDir>` | **Public since 0.2.0** (dev-gated at 0.1.0; founder ruling 2026-09-16, BURN-DOWN D5 → publish). Run the template conventions over a template repo folder — the same checks its build gate (`tools/check-registry.mjs`) runs, from outside (agents, reviewers, forks, CI on a fork): definition-time + render-time conventions (the 13 throw-posture ones incl. `latticeSmooth`) and, when the repo commits `layout-fingerprints/` or `.layout.m0` sidecars, the fingerprint diff. `--json` (one object: `ok, repo, mode, loadDiagnostics, rendered, skipped, errors, warnings, notes, fingerprints, conventions`), `--sweep` (also render on the standard 1080p canvases → `canvasEnvelope`), `--entry <path>`, `--src <dir>`. Loads through `loadTemplateRepoFromPath` under the external origin scope, so conventions RECORD rather than throw and one bad template never hides the rest. **Runs the repo's template module bodies in-process** — prints a trust warning to stderr (not under `--json`). Exit 1 on any error-severity finding or a fatal load diagnostic. It is the pack-publishing contract for community/starter authors ("the publish requirement for packs"). Impl: the CLI source (not published); locked public by `__tests__/cli.surface.test.js`. |
|
|
68
|
+
| `browse-templates` (~6033) | **DEV-GATED** (whole command inside `if (DEV_MODE)`, 4792): interactive TTY picker (template → variant → confirm → delegates to `make`). It drives the E2E variant matrix and writes to `test-output/` — a development harness, not a user surface. |
|
|
69
|
+
| `momo <message…>` (~6282) | **DEV-GATED** (whole command inside `if (DEV_MODE)`, 5040): relay English geometry instruction to Momo in a running Mosaic Desktop over the loopback bridge; exit 0 applied / 2 not-applied / 1 couldn't-run. |
|
|
70
|
+
| `init <name>` (~6464) | Scaffold a template workspace — the compact starter twin, renamed to the user's handle. `--dir`, `--handle`, `--display-name`. Shares ONE scaffold with Desktop's Start pane (`scaffoldTemplateWorkspace` in `@m0saic/product`), so the two cannot drift. Deliberately does NOT write the trust store: loading an external repo is the user's consent, recorded by the app in `~/m0saic/template-trust.json`. Prints the install / build / fingerprint line and the Add-source hint. |
|
|
71
|
+
| `mcp` (~6538) | Run the CLI as an **MCP server** over stdio — the agent-facing surface. See below. |
|
|
72
|
+
| `open [file]` (~6608) | Open a file in Mosaic Desktop, or `--template` / `--make` to open the Make page via the bridge. See below. |
|
|
73
|
+
| `setup` (~6830) | Download + install the pinned ffmpeg toolchain (`--gpl` default / `--lgpl` / `--yes` / `--platform-key`). Idempotent: detects installed golden slot or a PATH ffmpeg with libx264. **The only path that downloads** — renders never do. TTY asks `Download the pinned GPL ffmpeg build now?`; non-TTY / `CI` need explicit approval — `--yes` (or the `--gpl` / `--lgpl` repair flags), otherwise `No terminal to ask for approval on.` + exit 1, nothing downloaded. Ends `✓ ffmpeg is ready. Now run your m0saic command again.` |
|
|
74
|
+
| `activate <key>` (~7000) | Validate + store a license key at `~/m0saic/license.json` (`M0SAIC_PRODUCT_KEY` env wins over the file). **A VERIFIED key — valid, in grace, expired or revoked — also sends ONE notice to m0saic.io from a detached worker (`utils/licenseActivation.ts` → `licenseActivationWorker.js`): the key's `keyId`, the outcome, `surface: "cli"`, the CLI's major.minor. Never the token, the email or the install id. Sent in EVERY telemetry mode — the one signal outside them (founder ruling 2026-09-26); honours `M0SAIC_TELEMETRY_ENDPOINT=off`, `CI` (unless the endpoint came from env) and dormancy (a dev checkout never sends); ignores the mode and `DO_NOT_TRACK`. The command prints "Told m0saic.io this key was used …" after the verdict, and `--help` says so. An unverified key sends nothing.** `TELEMETRY.md` §1.16. |
|
|
75
|
+
| `license` (~6991) | Show tier/holder/expiry; `--remove` returns to free tier. |
|
|
76
|
+
| `update` (~7050) | Check npm for a newer release; offers `npm i -g m0saic@latest`. |
|
|
77
|
+
| `versions` (~7116) | Print **every bundled `@m0saic/*` package**, grouped by tier (language / substrate / product — the CLI bundles no community package) + ffmpeg baseline/runtime + resolved toolchain. `--json`: `packages` holds ALL bundled packages (a superset of the old `templates`/`types` pair, so scripts keep working). Classification comes from `@m0saic/platform` `describeMosaicPackage`, shared with Desktop's Tools → About this build (2026-09-26). |
|
|
78
|
+
| `telemetry` (via helper) | Subcommands `status` (default) · `set-mode <standard\|local-only\|ghost>` · `list` · `preview` · `flush` · `clear`. Since 0.2.0 PUBLISHED builds transmit (Standard mode): day summary + today-so-far rollups after renders via a detached worker, to `https://m0saic.io/api/telemetry`. Workspace builds are dormant. Gates for harnesses: `M0SAIC_TELEMETRY=ghost` (nothing recorded), `M0SAIC_TELEMETRY_ENDPOINT=off` (nothing sent), `CI` (no send unless the endpoint is set by env). **Modes govern usage telemetry only: `activate` is in `CLI_USAGE_SKIPPED_COMMANDS`** — its notice is identity-less, and the rollup deliberately carries no `cli.command.activate` counter beside it (the pairing would link an install id to a key id). `status` / `preview` cannot list the notice: it is never queued. Full reference: `TELEMETRY.md` at the repo root (§1.16 for the notice). |
|
|
70
79
|
|
|
71
80
|
> **`decode-watermark` is GONE** (removed 2026-07-28). Recovering a forensic
|
|
72
81
|
> watermark is now an ordinary template — `@m0saic/forensic/watermark/verify/v1`,
|
|
@@ -74,18 +83,18 @@ help — see the DEV-GATED rows below.
|
|
|
74
83
|
> family. The old `--ffmpeg <path>` override it carried is now the template's
|
|
75
84
|
> `ffmpegPath` prop.
|
|
76
85
|
|
|
77
|
-
## `make` input routing (index.ts
|
|
86
|
+
## `make` input routing (index.ts:~5340-5372)
|
|
78
87
|
|
|
79
88
|
1. Extension `.mosaic` → `handleMosaicFile` — parse JSON renderable, resolve
|
|
80
89
|
relative media against the file's dir, plan + render.
|
|
81
|
-
2. Extension `.mosaicx` → `handleMosaicxFile` (
|
|
90
|
+
2. Extension `.mosaicx` → `handleMosaicxFile` (~4214) — the **resolve-then-render
|
|
82
91
|
branch**: absolutize asset paths → apply the doc's `runner` block as option
|
|
83
92
|
defaults (explicit flags win) → sibling `.m0v` auto-discovery → route
|
|
84
93
|
`--inputs`/`--input-dir` into the single `template_invocation`'s
|
|
85
94
|
`props.sourceIds` (ambiguous multi-invocation → error; zero inputs on an
|
|
86
95
|
input-requiring template → renders a usage-card and exits non-zero) →
|
|
87
96
|
`resolveMosaicx` → write resolved doc to a tmp `.mosaic` → delegate to
|
|
88
|
-
`handleMosaicFile` (
|
|
97
|
+
`handleMosaicFile` (~4460, defined ~4475) so the post-resolve render path is shared.
|
|
89
98
|
**Two roots accepted** (2026-08-14): `mosaicx_document` and
|
|
90
99
|
`mosaicx_pipeline` (a root-level template chain — see
|
|
91
100
|
`templates/data-pipeline.md`). The header echoes which one it read
|
|
@@ -157,7 +166,7 @@ the JSON:
|
|
|
157
166
|
## `make` flags
|
|
158
167
|
|
|
159
168
|
**Core** — `-w/--width` and `-h/--height` are `requiredOption`s. Then:
|
|
160
|
-
`-o/--output` (defaults `out.mp4` / `out.png`, index.ts
|
|
169
|
+
`-o/--output` (defaults `out.mp4` / `out.png`, index.ts:~172-173), `--fps` (1–120),
|
|
161
170
|
`--durationMs` (>0), `--format`/`--output-kind` (`video|image`),
|
|
162
171
|
`--alpha`/`--no-alpha`, `--props <jsonOrPath>` (inline JSON **or** `@path/to/json`
|
|
163
172
|
— `utils/readJsonArg.ts:18` strips the `@` and reads the file),
|
|
@@ -171,7 +180,7 @@ mosaic; prints `Validation DEGRADED`), `--report` (`.output.json` /
|
|
|
171
180
|
`--template-repo-entry <file>` (external template repos), `--community-repo <path>`
|
|
172
181
|
(ONE checkout of the official community repo — see the community gate below).
|
|
173
182
|
|
|
174
|
-
**Encode surface** (index.ts
|
|
183
|
+
**Encode surface** (index.ts:~4231-4338, mirrors the Make page's Advanced panel;
|
|
175
184
|
precedence: flags > `.m0v` preset > doc-on-disk > engine default):
|
|
176
185
|
`--target <preset>` (`web-mp4|web-webm|alpha-mov|image-png|image-jpeg|animated-gif|audio-mp3|audio-wav`),
|
|
177
186
|
`--container`, `--video-codec`, `--audio-codec`, `--pixel-format`, `--bitrate`
|
|
@@ -189,10 +198,10 @@ over `--input-dir`), `--input-dir <path>` (+ `--recursive` for depth-first walk)
|
|
|
189
198
|
**`.m0v`**: `--m0v <path>` loads a Mosaic Vocabulary file two ways — named
|
|
190
199
|
outputs onto `ctx.userIntent.outputs` (template-consultative) AND a post-render
|
|
191
200
|
merge onto the renderable **by index** when entry counts match
|
|
192
|
-
(index.ts
|
|
201
|
+
(index.ts:~4218-4221; `BaseRenderOptions` docstring ~541-557).
|
|
193
202
|
|
|
194
203
|
**Toolchain**: `--toolchain <name>` is a **global program option**
|
|
195
|
-
(index.ts
|
|
204
|
+
(index.ts:~526), not make-specific — a named entry from `m0saic.local.json`, with
|
|
196
205
|
implicit `gpl`/`lgpl` golden slots merged in. Resolution precedence: `--toolchain`
|
|
197
206
|
flag > `M0SAIC_TOOLCHAIN` env > config `defaultToolchain` > PATH/golden fallback
|
|
198
207
|
(`utils/toolchainConfig.ts:129`). There is **no** `--ffmpeg` anywhere on the CLI
|
|
@@ -212,7 +221,7 @@ Spell every `setup` hint with `cliSetupCommand()` (`utils/invocation.ts`):
|
|
|
212
221
|
timing rolled up by mosaic node (names the bottleneck panel/template).
|
|
213
222
|
|
|
214
223
|
**DEV-GATED** (each wrapped in `.hideHelp(!DEV_MODE)`; `DEV_MODE =
|
|
215
|
-
process.env.M0SAIC_DEV === "1"` at index.ts
|
|
224
|
+
process.env.M0SAIC_DEV === "1"` at index.ts:~71, compile-stripped to `false` in
|
|
216
225
|
the published artifact): `--dev` (commands + timings), `--print-commands`,
|
|
217
226
|
`--save-plan [path]`, `--save-commands [path]`. Same gating inside inline-DSL
|
|
218
227
|
mode (`inlineDsl.ts:148-154`). Also dev-gated: the whole `momo` command, the
|
|
@@ -230,7 +239,7 @@ and neither is `doctor` (public since 0.2.0).
|
|
|
230
239
|
|
|
231
240
|
**Three reserved identities, exact-match, nothing else is checked:** template
|
|
232
241
|
namespaces `@m0saic/` and `@m0saic-dev/`, repo id `@m0saic-community`
|
|
233
|
-
(`index.ts
|
|
242
|
+
(`index.ts:~907-909`; registry compare is case-insensitive, `templateRegistry.ts:~112`).
|
|
234
243
|
Every other namespace / repo id is open. `--template-repo` never grants anything.
|
|
235
244
|
|
|
236
245
|
**The grant.** `--community-repo <path>` says "this IS the official repo"; the
|
|
@@ -338,7 +347,7 @@ ordinary repos); the community checkout loads only this way".
|
|
|
338
347
|
`make-wireframe` / `make-wireframe-animated` take the layout as `--m0 <string>`
|
|
339
348
|
(inline DSL — the flag is `--m0`, not `--m0saic`) or `--mfile <path>`, plus the
|
|
340
349
|
same core/save/report/`--m0v`/`--output-pattern` surface as `make` (no encode
|
|
341
|
-
surface, no `--inputs`). Animated adds `--disable-ui` (index.ts
|
|
350
|
+
surface, no `--inputs`). Animated adds `--disable-ui` (index.ts:~4504).
|
|
342
351
|
|
|
343
352
|
## `m0saic open` — pointer only
|
|
344
353
|
|
|
@@ -353,6 +362,72 @@ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/d
|
|
|
353
362
|
<file.mosaicx>` opens an authored `.mosaicx` in Make and adopts it for the
|
|
354
363
|
agent loop.
|
|
355
364
|
|
|
365
|
+
## `m0saic mcp` — the agent surface (2026-09-25)
|
|
366
|
+
|
|
367
|
+
MCP has three primitives; this server implements **tools** only (model-invoked).
|
|
368
|
+
Transport is newline-delimited JSON-RPC 2.0 over stdio, hand-rolled in
|
|
369
|
+
`src/mcp/server.ts` (~230 lines, **zero runtime deps**: the official SDK carries
|
|
370
|
+
17, including express, hono, cors, jose and ajv, and the CLI vendors its whole
|
|
371
|
+
closure into the published tarball). `initialize` echoes the client's
|
|
372
|
+
`protocolVersion`; a tool that throws returns `isError: true` rather than a
|
|
373
|
+
JSON-RPC error, so the model sees the failure instead of the transport eating it.
|
|
374
|
+
|
|
375
|
+
The 14 tools (`src/mcp/tools.ts`), in three groups:
|
|
376
|
+
|
|
377
|
+
- **Knowledge** — `knowledge_list`, `knowledge_read`, `knowledge_search`. The
|
|
378
|
+
`@m0saic/knowledge` docs, so an agent reads the handbook instead of guessing.
|
|
379
|
+
- **Local, no app needed** — `list_templates`, `template_props`, `validate_m0`,
|
|
380
|
+
`workspace_scaffold`, `render_still`, `doctor`.
|
|
381
|
+
- **Through the loopback bridge into a running Desktop** — `desktop_status`,
|
|
382
|
+
`desktop_open_in_make`, `desktop_reload_sources`, `desktop_read_verdict`,
|
|
383
|
+
`desktop_layout`.
|
|
384
|
+
|
|
385
|
+
Two rules the implementation enforces:
|
|
386
|
+
|
|
387
|
+
- **`render_still` pins `M0SAIC_CLI=/usr/bin/false`.** `@m0saic/benchmark/run/v1`
|
|
388
|
+
re-invokes `argv[1]`; without the pin a render from inside the CLI fork-bombs.
|
|
389
|
+
- **Failures return a HINT, never raw stderr** (`findFailureHint`). Engine text is
|
|
390
|
+
moat: the argv and the filtergraph never cross to a model.
|
|
391
|
+
|
|
392
|
+
`src/mcp/bridgeClient.ts` is the transport for the desktop tools. A 404 means
|
|
393
|
+
`route-missing` — "this Desktop is too old for that route" — not "the app is
|
|
394
|
+
down"; the compat warning prints once per process. The full route table is
|
|
395
|
+
`docs/compat-bridges.md`.
|
|
396
|
+
|
|
397
|
+
**`--agent-note` / `--agent-question` are PUBLIC** as of this line (the DEV_MODE
|
|
398
|
+
strip is gone and `__tests__/cli.surface.test.js` asserts they appear in the
|
|
399
|
+
published help — the lock was inverted, not removed). With `--template` they seed
|
|
400
|
+
a note / question into Make's File tab for the reviewer to answer.
|
|
401
|
+
|
|
402
|
+
## `m0saic doctor` answers a VERSIONED question (2026-09-25)
|
|
403
|
+
|
|
404
|
+
Conventions change, so "does this repo pass?" is the wrong question — the right one is **which line
|
|
405
|
+
does it pass at.** `report.conventions` carries it:
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
conventions: meets 0.2.0 (shipped at 0.2.0).
|
|
409
|
+
behind 0.3.0: bindingsDeclared, canvasFill
|
|
410
|
+
9 of 96 template(s) behind — a vN+1 on each clears it.
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
- **`meets`** — the newest m0saic line every template in the repo satisfies (error severity only; a
|
|
414
|
+
`record` warning is advice, not a failure to meet a line).
|
|
415
|
+
- **`behind`** — the lines it does not meet, with the rules that fail, oldest first.
|
|
416
|
+
- **`shippedAt`** — the repo's own `frozen.manifest.json` `release`.
|
|
417
|
+
- **`lagOnly`** — true when EVERY failure comes from a line newer than `shippedAt`. The footer then
|
|
418
|
+
says so explicitly: *"Not a defect: these templates met the conventions of their day. Fix at the
|
|
419
|
+
next vN."*
|
|
420
|
+
|
|
421
|
+
**A template is allowed to lag.** It met the conventions of its day; the rules moved; the fix is its
|
|
422
|
+
next `vN`. What the rules guarantee is that **NEW** templates meet the CURRENT line — which is why the
|
|
423
|
+
postures throw (founder: "we need to be greedy about adding conventions because agents, especially
|
|
424
|
+
non-frontier ones, won't"). The exit code is still 1 on any error, because the status is informational
|
|
425
|
+
and the message carries the meaning.
|
|
426
|
+
|
|
427
|
+
`TEMPLATE_CONVENTION_SINCE` in `@m0saic/template-utils` is the map of rule → line. Everything
|
|
428
|
+
pre-0.3.0 reads `0.2.0`: at that freeze the whole shipped fleet passed every rule then in force, so it
|
|
429
|
+
is a true lower bound rather than a guessed date.
|
|
430
|
+
|
|
356
431
|
## Failure modes
|
|
357
432
|
|
|
358
433
|
- `spawnSync m0saic EACCES` — the CLI isn't built/linked on this machine:
|
|
@@ -365,7 +440,7 @@ agent loop.
|
|
|
365
440
|
change, breaking probe-based assertions. Check `npm run tier:status` first;
|
|
366
441
|
full rule in the maintainers' agent contract §7.7 (not published).
|
|
367
442
|
- Non-zero ffmpeg exits print a core-computed failure classification after the
|
|
368
|
-
exit-code line (`printFfmpegFailure`, index.ts
|
|
443
|
+
exit-code line (`printFfmpegFailure`, index.ts:~113) — read it before rerunning
|
|
369
444
|
with `--verbose`.
|
|
370
445
|
- Invalid m0 strings fail fast at validation (`validateM0String`), not inside
|
|
371
446
|
ffmpeg; `SPLIT_EXCEEDS_AXIS` means the split count exceeds the pixel axis —
|
|
@@ -71,8 +71,24 @@ doc.children["<child-key>"] = {
|
|
|
71
71
|
};
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
`color: "black@0"` is the idiomatic transparent base
|
|
75
|
-
|
|
74
|
+
`color: "black@0"` is the idiomatic transparent base **for the child's own
|
|
75
|
+
framebuffer.** Whether the child's INTERMEDIATE keeps that transparency on the hop
|
|
76
|
+
back to the parent depends on the ROOT's deliverable, not on a default:
|
|
77
|
+
|
|
78
|
+
- under an image / `.mov` / alpha root it does;
|
|
79
|
+
- under an **mp4 root** (the single-flat path) only a child that paints an image,
|
|
80
|
+
text, or alpha media (`qtrle` / `png` / `.mov`) is encoded as an alpha carrier
|
|
81
|
+
(`qtrle` / `argb`). A child made only of lavfi tiles and masks comes back
|
|
82
|
+
**opaque** — 0.3.0 plan item **R4** (the Rainier pack works around it with one
|
|
83
|
+
image tile);
|
|
84
|
+
- a **stitched** child (a node past the chunk budget) composites its final stitch
|
|
85
|
+
over an opaque background regardless (`buildChunkCommands.ts` `isFinalOpaqueVideo`)
|
|
86
|
+
— plan item **R23**.
|
|
87
|
+
|
|
88
|
+
So a `black@0` base does not by itself make the composite clean. Until the engine
|
|
89
|
+
carries alpha for those cases: put one image or text tile in a transparent child,
|
|
90
|
+
and keep it under the chunk budget. (Corrected 2026-09-28 from the Rainier build;
|
|
91
|
+
was "intermediates carry alpha by default".)
|
|
76
92
|
|
|
77
93
|
### Cost, and when to skip it
|
|
78
94
|
|
|
@@ -59,6 +59,127 @@ Rules:
|
|
|
59
59
|
Order the entries the way a double-click should read them: `fields[0]` is the
|
|
60
60
|
primary action (a plain double-click); the badge row offers every kind.
|
|
61
61
|
|
|
62
|
+
## The roll call — `bindingsDeclared` (THROW, 2026-09-25)
|
|
63
|
+
|
|
64
|
+
**Every prop that CAN carry a canvas handle is either bound, or declared.** A new template that does
|
|
65
|
+
neither fails the build. Founder ruling: "if it makes sense to bind, we do — or warn if it's not."
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
bindings: { unbound: { fps: "timing", gap: "geometry", seed: "determinism" } }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
One honest word is the point — the reason is for a reviewer. Keys are dotted exactly as `propsSchema`
|
|
72
|
+
nests them (`titles.title`).
|
|
73
|
+
|
|
74
|
+
### The complete surface — one ruling per control
|
|
75
|
+
|
|
76
|
+
`MUST ACCOUNT` = bind it, or name it in `bindings.unbound`. `NEVER` = not a canvas thing; the
|
|
77
|
+
right-hand panel is its surface, and the rule stays silent.
|
|
78
|
+
|
|
79
|
+
| Prop / flavour | Handle | Convention |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `string`, free text | `string` | **MUST ACCOUNT** |
|
|
82
|
+
| `string` + `isColor` / `colorPicker` | `color` | **MUST ACCOUNT** |
|
|
83
|
+
| `number` | `number` | **MUST ACCOUNT** |
|
|
84
|
+
| `media` | `media` (also a drop target) | **MUST ACCOUNT** |
|
|
85
|
+
| `string[]` · `number[]` · `media[]` | per element, by index | **MUST ACCOUNT** (bound anywhere on the prop counts) |
|
|
86
|
+
| `json` · `list` · `array` | per leaf, `path` + `kind` | **MUST ACCOUNT** (any leaf counts) |
|
|
87
|
+
| `json` + `picker: "regions"` | `rect` | **MUST ACCOUNT** |
|
|
88
|
+
| `string` / `number` + closed set (`oneOf`, `options`, `optionsFrom…`) | — | **NEVER** — a picker is a mode, not a value on canvas |
|
|
89
|
+
| `boolean` | — | **NEVER** — a one-way door: `false` renders no rect to click |
|
|
90
|
+
| `group` | — | **NEVER** — a container; its fields are walked individually |
|
|
91
|
+
| `m0` · `m0c` · `m0p` | — | **NEVER** — the value IS the composition of every rect, not one of them |
|
|
92
|
+
| `code` | — | **NEVER** — read-only by contract; a handle implies editing |
|
|
93
|
+
| anything `ui.hidden` or `consumer: "human"` | — | **NEVER** |
|
|
94
|
+
|
|
95
|
+
Why the `m0` family is excluded, since it is the tempting one: a layout prop has no clickable element.
|
|
96
|
+
All 10 shipped layout props (wireframe, dsl-tutorial, screencap-grid v1/v2, watermark, qr-animate,
|
|
97
|
+
camera-debug) drive the ROOT composition — you would have to select the whole grid, which the canvas
|
|
98
|
+
cannot express. If a template ever nests a layout prop into ONE cell, that cell is a real handle and a
|
|
99
|
+
sixth kind (`"layout"`) becomes worth having. None does today.
|
|
100
|
+
|
|
101
|
+
**Bindable ≠ has a rect.** The predicate says a `number` is always inline-editable; whether any rect
|
|
102
|
+
*shows* it is the template's business. `durationMs`, `fps`, `gap`, `seed`, `padding` are all
|
|
103
|
+
accountable and correctly unbound — which is exactly why the rule takes a declaration instead of
|
|
104
|
+
guessing from the pixels.
|
|
105
|
+
|
|
106
|
+
**A colour that IS `document.backgroundColor` needs nothing** — no binding, no declaration. It has no
|
|
107
|
+
source and no rect, so no handle can exist, and it is the way a canvas SHOULD be filled: a full-frame
|
|
108
|
+
base rect becomes a click target that shadows everything behind it whenever the pointer is not on a
|
|
109
|
+
smaller tile (founder direction 2026-09-25). Matched case-insensitively, root or child.
|
|
110
|
+
|
|
111
|
+
**A binding in a nested CHILD counts.** A template that composes its content into a child document
|
|
112
|
+
(the hello-world card puts everything in a `card` child) reaches its props perfectly well — Make
|
|
113
|
+
resolves a binding through children. **But the audit renders at `defaultProps`,** so a rect the
|
|
114
|
+
template only creates when a prop is non-empty reads as unbound. That is the "bind even when the value
|
|
115
|
+
is empty" rule biting: bind it unconditionally, or declare it.
|
|
116
|
+
|
|
117
|
+
**A stale declaration is a violation too:** naming a prop that cannot carry a handle, naming one that
|
|
118
|
+
is in fact bound, or leaving the reason empty. An entry claims a reviewer looked at that prop.
|
|
119
|
+
|
|
120
|
+
**Exempt:** any template hashed in the repo's `frozen.manifest.json`. It shipped before the
|
|
121
|
+
convention, it is never edited in place, and the fix would be a vN+1 nobody will write. A repo with no
|
|
122
|
+
manifest has shipped nothing and holds every template to the rule.
|
|
123
|
+
|
|
124
|
+
## The in-context line — `bindingHints` (THROW, 2026-09-27)
|
|
125
|
+
|
|
126
|
+
**Every bound rect shows one line in context.** Make puts it under the inline editor when a person
|
|
127
|
+
double-clicks, on the tile card's row, and in the handle's hover tooltip — what changing the value
|
|
128
|
+
does and what it looks like, so nobody has to open the settings pane or a manual to understand a knob.
|
|
129
|
+
Founder ruling: a production-grade template adheres to all the surfaces of the ecosystem, and the
|
|
130
|
+
canvas is now the primary one; "it's better to take this tax now."
|
|
131
|
+
|
|
132
|
+
The line comes from either of two places, and the gate accepts either:
|
|
133
|
+
|
|
134
|
+
| Where | How | Reaches |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| the prop | `propsSchema.<key>.description = "…"` | every rect bound to that prop |
|
|
137
|
+
| the binding | `bindProp(src, key, i, { hint: "…" })` · a `bindProps` entry's `hint` · `withBindingHint(src, "…")` (composes with every binder) | that rect only — the wording for THIS place |
|
|
138
|
+
|
|
139
|
+
One plain sentence, never markdown, never the value itself ("The month this calendar shows; the grid
|
|
140
|
+
re-flows to its weeks", not "October"). Make trims it and caps it near 200 characters. The tax is
|
|
141
|
+
therefore one honest sentence per bound prop, paid once in the schema; a per-rect `hint` is for the
|
|
142
|
+
cases where the same prop means something different in different places.
|
|
143
|
+
|
|
144
|
+
**Needs none:** a `companion` leaf (filled only by a media drop, never shown in a form), and any prop
|
|
145
|
+
that is not bound (an `unbound` declaration already carries its reason). A binding on an unknown prop
|
|
146
|
+
is `bindingsSound`'s finding, not this one.
|
|
147
|
+
|
|
148
|
+
**Exempt:** any template hashed in the repo's `frozen.manifest.json`, like the roll call — it reports
|
|
149
|
+
in `lagging`, never `findings`. The CLI ignores the field entirely; it is extra data there.
|
|
150
|
+
|
|
151
|
+
## The `bindingsCover` convention — what the gate checks
|
|
152
|
+
|
|
153
|
+
The roll call above is the gate. `bindingsCover` is the WEAKER SECOND SIGNAL kept beside it
|
|
154
|
+
(posture `record` → a warning): it reads the drawn text, so it is the only rule that can catch a prop
|
|
155
|
+
bound to the WRONG rect, and it still fires on frozen templates where the roll call cannot. Both
|
|
156
|
+
`check-registry.mjs` copies and `m0saic doctor <repo>` run both.
|
|
157
|
+
|
|
158
|
+
- A **free-text `string`** prop — not `meta.ui.hidden`, not `consumer: "human"`, not a closed /
|
|
159
|
+
picked set — whose default is ≥2 characters and appears verbatim in drawn text must be bound.
|
|
160
|
+
- A **`number`** prop, same exclusions, whose default appears in drawn text in one of its honest
|
|
161
|
+
spellings: `String(v)`, `toLocaleString("en-US")`, and `toFixed(1|2)` **only when the value already
|
|
162
|
+
has decimals** (nothing draws an integer count as `"1.0"`, but chrome draws versions that read that
|
|
163
|
+
way). A one-character spelling never counts.
|
|
164
|
+
- Numbers match on a **digit boundary**: `12` is not read out of `2012` or `3.12`, and a drawn
|
|
165
|
+
`1,200` is not read as the prop `200`.
|
|
166
|
+
- Coverage is per PROP, not per rect — a prop bound anywhere on the root document is covered.
|
|
167
|
+
|
|
168
|
+
### One rect, two props — a composite line
|
|
169
|
+
|
|
170
|
+
`"@qsbuilds · 2026 on GitHub"` is one rect drawing two props, and a single `editor.binding` names one
|
|
171
|
+
of them. A bare `bindProp` there would REPLACE the existing handle, so the rule says so and names the
|
|
172
|
+
fix that keeps both:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
bindProps(src, [{ propKey: "handle" }, { propKey: "year", kind: "number" }])
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
— several handles on one rect — or split the line so each prop gets its own rect. (`year-card/v1` is
|
|
179
|
+
the live example; it is frozen, so its fix is v2 and it warns until then.)
|
|
180
|
+
|
|
181
|
+
Colour coverage is not checked: a colour is never drawn as text, so it needs a different detector.
|
|
182
|
+
|
|
62
183
|
## Authoring helpers (`@m0saic/template-utils`)
|
|
63
184
|
|
|
64
185
|
- `bindProp(src, propKey, index?)` — the basic case.
|
|
@@ -127,6 +127,25 @@ seam's `outputHintsResolve` convention (THROW posture) checks it returns an
|
|
|
127
127
|
object, is deterministic at `defaultProps`, and agrees with the static hints
|
|
128
128
|
for every field it returns there. Precedence: [`../output-resolution-tree.md`](../output-resolution-tree.md) §size.
|
|
129
129
|
|
|
130
|
+
**Image or video is part of the same answer (2026-09-26).** `format` rides the same
|
|
131
|
+
resolver. A template whose kind depends on its inputs — a `media` prop that accepts
|
|
132
|
+
both kinds — declares it per props, `resolveOutputHints: (props) => ({ format: … })`,
|
|
133
|
+
and its static `outputHints.format` is that function's value at `defaultProps`, which
|
|
134
|
+
for an EMPTY media slot is **image** even when the template is "for" video (the
|
|
135
|
+
`outputHintsResolve` throw holds the two equal). The resolver is pure and prop-only:
|
|
136
|
+
it runs before any probe, so a path's extension is the evidence available. Exactly
|
|
137
|
+
three answers exist — the static hint at defaults, the resolver at the current
|
|
138
|
+
props, and the user's override — and nothing else authors one. Every host asks the
|
|
139
|
+
author through `resolveTemplateOutputHints(tmpl, props).format` and passes it into
|
|
140
|
+
`createDesignContext` / `createEngineContext` as `format`, so the `format` the
|
|
141
|
+
`defineMosaicTemplate` wrapper stamps on the rendered document is a RECORD of the
|
|
142
|
+
decision, never a source of it. `inferOutputKind` (`@m0saic/platform`) is a last
|
|
143
|
+
resort for a bare `.mosaic` opened cold with no template behind it, and since
|
|
144
|
+
2026-09-26 it counts a `type: "media"` video source as motion (`videoMedia`) — the
|
|
145
|
+
omission that made footage-through-a-mask read as a still. The defect class and the
|
|
146
|
+
seven-surface audit that produced this rule:
|
|
147
|
+
(internal design history).
|
|
148
|
+
|
|
130
149
|
## The lattice declarations — `lattice` (2026-09-16)
|
|
131
150
|
|
|
132
151
|
**Convention `latticeSmooth` (throw):** every split count above 12 in the rendered
|
|
@@ -151,6 +151,14 @@ Two authoring facts, both learned on the starters:
|
|
|
151
151
|
- **Aspect-awareness.** Stress vertical / square / horizontal. A wide card may need a
|
|
152
152
|
different layout (timeline's `orientation:"auto"`); clamp edge content so it can't
|
|
153
153
|
spill the card.
|
|
154
|
+
- **Image or video → the same resolver (2026-09-26).** A template with a media slot
|
|
155
|
+
that accepts both kinds answers per props — `resolveOutputHints: (props) => ({ format })`
|
|
156
|
+
from a pure check on the media path's extension — and its static `outputHints.format`
|
|
157
|
+
is that value at `defaultProps`. An empty slot renders a still, so the honest static
|
|
158
|
+
answer is usually **image** (the seam's `outputHintsResolve` throw keeps the two
|
|
159
|
+
equal). Never let a host guess from the document: `inferOutputKind` is a last resort
|
|
160
|
+
for a bare `.mosaic`, not a decision. Reference: `reference/template-flags.md`
|
|
161
|
+
§ "A canvas that is a knob".
|
|
154
162
|
- **A canvas that is a knob → `resolveOutputHints(props)`.** When a prop picks the
|
|
155
163
|
output size (a `platform` knob: YouTube 1920×1080 vs TikTok 1080×1920), declare
|
|
156
164
|
the resolver on the template so hosts SEED the right target before rendering
|
|
@@ -158,6 +166,57 @@ Two authoring facts, both learned on the starters:
|
|
|
158
166
|
`render` still reads `ctx.target` and lays out at any size. At `defaultProps` it
|
|
159
167
|
must agree with the static `outputHints` (`outputHintsResolve`, throw). Rules:
|
|
160
168
|
[`philosophy-and-contract.md`](philosophy-and-contract.md) §"Output contract".
|
|
169
|
+
- ⛔ **Filling the canvas: `document.backgroundColor`, never a full-frame rect.** This is
|
|
170
|
+
the **`canvasFill` convention, and it THROWS** (2026-09-25) — the build and
|
|
171
|
+
`m0saic doctor` both refuse a root document carrying a static, opaque, full-canvas colour
|
|
172
|
+
source. A base rect is a click target that shadows everything behind it, selected any time
|
|
173
|
+
the pointer is not on a smaller tile, and it is redundant: `doc.backgroundColor` fills the
|
|
174
|
+
canvas with no rect at all. Only a STATIC, opaque, unshaped fill counts — a curtain wipe
|
|
175
|
+
(`overlay.enable` / `window`), a scrim (`overlay.alpha`), a masked shape, a rounded card
|
|
176
|
+
(`effects`) and an inset fill (`placement`) are all legitimate and never flagged.
|
|
177
|
+
Templates already frozen are exempt. `bindingsDeclared` also accepts a colour prop that IS the document background
|
|
178
|
+
with neither a binding nor a declaration, because such a prop has no rect to bind.
|
|
179
|
+
- ⚠️ **The one case that needs a rect: a template COMPOSED INTO another.** The engine
|
|
180
|
+
lifts `backgroundColor` from the PRIMARY OUTPUT document only, so a child document's
|
|
181
|
+
own background is silently ignored (tracked at
|
|
182
|
+
(internal design history) — founder
|
|
183
|
+
owns the engine fix).
|
|
184
|
+
- **The declared escape** — an opt-in dev prop, NOT a rect in the default shape.
|
|
185
|
+
Defaulting it to `true` is ALSO how a template tells `canvasFill` that its rect is
|
|
186
|
+
deliberate; declaring it `false` is no escape:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
useNestedBackgroundColor: {
|
|
190
|
+
type: "boolean", required: false,
|
|
191
|
+
description: "Compose-time: also paint the background as a full-frame rect, for when this template is nested inside another (a child document's own backgroundColor is ignored by the engine).",
|
|
192
|
+
meta: { ui: { hidden: true, label: "Nested background rect" } },
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`defaultProps` leaves it **false / absent**, so a standalone render never grows the
|
|
197
|
+
rect. A template that composes this one passes `useNestedBackgroundColor: true` in the
|
|
198
|
+
child's props. Keep `backgroundColor` set as well — it costs nothing and keeps the
|
|
199
|
+
standalone path identical.
|
|
200
|
+
- **The m0 move is ONE wrap.** `F{…}` prepends exactly one full-canvas frame and the
|
|
201
|
+
wrapped layout follows in order (`F{2[1,1]}` → `1280x720@0,0`, `1280x360@0,0`,
|
|
202
|
+
`1280x360@0,360` at 720p), so frame 0 is the base and the content overlays it:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
if (props.useNestedBackgroundColor) {
|
|
206
|
+
return { ...doc, m0: `F{${doc.m0}}`, sources: [colorSource(bg), ...doc.sources] };
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`F` is pretty syntax: it **canonicalizes to `1{…}`**, and the file formats never emit
|
|
211
|
+
pretty form, so on disk the wrapper reads `1{…}`. Every source index shifts by one, so
|
|
212
|
+
anything keyed by `sourceIndex` (never a binding — those key by prop) moves with it.
|
|
213
|
+
- `meta.ui.hidden` is deliberate: this is a compose-time flag a parent sets
|
|
214
|
+
programmatically, not a knob a human picks (and hidden props are outside
|
|
215
|
+
`bindingsDeclared`'s roll call — a boolean is anyway).
|
|
216
|
+
- **When the engine fix lands, this prop goes away**: delete it, delete the rect, keep
|
|
217
|
+
`backgroundColor`. That is the whole point of it being opt-in and hidden rather than
|
|
218
|
+
baked into every template's shape.
|
|
219
|
+
|
|
161
220
|
- **Prop bindings — the rect that SHOWS a prop is its handle in Make.**
|
|
162
221
|
`bindProp(src, key)` / `bindProps(src, entries)` / `bindPropPath(src, key, path,
|
|
163
222
|
kind)` / `bindPropRange(...)` (`@m0saic/template-utils`) stamp `editor.binding` on
|
|
@@ -166,7 +225,14 @@ Two authoring facts, both learned on the starters:
|
|
|
166
225
|
are never bindable) and the preview shows one glyph per thing the tile can do (T
|
|
167
226
|
text · 123 number · swatch colour · move rect · picture media), collapsing to ONE
|
|
168
227
|
dot on a small tile. Bind even when the value is empty (the rect is a handle to
|
|
169
|
-
ADD).
|
|
228
|
+
ADD). ⛔ **`bindingsDeclared` (0.3.0) THROWS** when a prop that could carry a handle
|
|
229
|
+
is neither bound nor named in `template.bindings.unbound` with its reason
|
|
230
|
+
(`{ fps: "timing" }`) — free-text and colour strings, numbers, media, one element of a
|
|
231
|
+
basic list, one leaf of json/list/array, and a regions-picker rect must all be
|
|
232
|
+
accounted for; booleans, closed sets, groups, the m0 family, code and hidden/human
|
|
233
|
+
props never are. Templates in `frozen.manifest.json` are exempt. On a rect that
|
|
234
|
+
already binds another prop the fix is `bindProps`, never a second `bindProp` — it
|
|
235
|
+
would replace the first. The full contract — kinds, `onClear`, `seedDraft`, `kind: "media"` (a rect
|
|
170
236
|
that takes a dropped file), `companion`, starter media — is
|
|
171
237
|
[`reference/prop-bindings.md`](reference/prop-bindings.md).
|
|
172
238
|
**`kind: "rect"` (2026-09-15) — a rendered cell edited IN PLACE:** a `json` prop
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m0saic/knowledge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The m0saic knowledge base for agents and authors: the m0 handbook, the engine mental models, and the template-authoring contract, as plain Markdown. Point your coding agent at README.md before it writes a template.",
|