@m0saic/knowledge 0.2.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/index.d.ts +8 -0
  4. package/dist/index.js +7 -0
  5. package/docs/README.md +60 -0
  6. package/docs/file-formats/m0-iteration-protocol.md +120 -0
  7. package/docs/file-formats/m0p-and-custom-field.md +195 -0
  8. package/docs/handbook/README.md +27 -0
  9. package/docs/handbook/composition-arithmetic.md +278 -0
  10. package/docs/handbook/dsl-complexity.md +75 -0
  11. package/docs/handbook/dsl-rules.md +367 -0
  12. package/docs/handbook/feasibility-precision-quantization.md +591 -0
  13. package/docs/handbook/m0-construction-methods.md +201 -0
  14. package/docs/handbook/precision-tiers.md +84 -0
  15. package/docs/m0saic-thesis.md +95 -0
  16. package/docs/runtime/README.md +17 -0
  17. package/docs/runtime/cli-usage.md +372 -0
  18. package/docs/runtime/ffmpeg-expression-limits.md +117 -0
  19. package/docs/runtime/reduce-to-one.md +96 -0
  20. package/docs/skills/README.md +40 -0
  21. package/docs/skills/axis-and-geometry.md +103 -0
  22. package/docs/skills/dsl-stdlib-method-catalog.md +7 -0
  23. package/docs/skills/identity.md +123 -0
  24. package/docs/skills/labels-and-masks.md +170 -0
  25. package/docs/skills/m0saic-string-generation.md +251 -0
  26. package/docs/skills/more-atoms-not-bigger-atoms.md +77 -0
  27. package/docs/skills/overlay-semantics.md +194 -0
  28. package/docs/skills/parse-apis.md +79 -0
  29. package/docs/skills/passthrough-semantics.md +136 -0
  30. package/docs/skills/structural-construction.md +86 -0
  31. package/docs/skills/text-in-templates.md +126 -0
  32. package/docs/skills/zero-overlay-analysis.md +87 -0
  33. package/docs/templates/README.md +65 -0
  34. package/docs/templates/capability-templates.md +72 -0
  35. package/docs/templates/construction-strategy.md +329 -0
  36. package/docs/templates/data-pipeline.md +324 -0
  37. package/docs/templates/emission-patterns.md +130 -0
  38. package/docs/templates/geometry-recipes.md +248 -0
  39. package/docs/templates/layout-contract.md +168 -0
  40. package/docs/templates/output-resolution-tree.md +202 -0
  41. package/docs/templates/patterns/case-study-lessons.md +69 -0
  42. package/docs/templates/patterns/perf-authoring-rules.md +100 -0
  43. package/docs/templates/patterns/primitive-extraction-pattern.md +103 -0
  44. package/docs/templates/philosophy-and-contract.md +310 -0
  45. package/docs/templates/recursion-nested-rendering.md +138 -0
  46. package/docs/templates/reference/grid.md +104 -0
  47. package/docs/templates/reference/json-prop-type.md +169 -0
  48. package/docs/templates/reference/mosaic-color.md +81 -0
  49. package/docs/templates/reference/mosaic-placement-props.md +103 -0
  50. package/docs/templates/reference/prop-bindings.md +203 -0
  51. package/docs/templates/reference/template-flags.md +205 -0
  52. package/docs/templates/render-lifecycle.md +117 -0
  53. package/docs/templates/rendering-model-contract.md +392 -0
  54. package/docs/templates/standalone-pack-authoring.md +233 -0
  55. package/docs/templates/theming.md +81 -0
  56. package/docs/templates/ui-controls.md +150 -0
  57. package/package.json +37 -0
@@ -0,0 +1,367 @@
1
+ # m0 DSL — Grammar, Semantics, and Validation
2
+
3
+ The exact surface grammar, semantic rules, and validation invariants for the m0
4
+ DSL — the deterministic spatial-layout language that powers m0saic. Any system
5
+ that generates, transforms, or mutates m0 strings must follow this document and
6
+ must prove correctness with the canonical validator. Visual inspection is never
7
+ sufficient.
8
+
9
+ The DSL is strict, deterministic, count-exact, and validator-authoritative.
10
+ Posture (as of 2026-07-27): **frozen at v1.1.0**
11
+ ([https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/package.json](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/package.json); verify there).
12
+ Grammar changes are explicitly avoided; the 1.1.0 minor carried the 2026-06-03
13
+ overlay-body relaxation (`ZERO_SOURCE_OVERLAY` no longer raised) and the
14
+ public-API prune.
15
+
16
+ > **Source of truth:** [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src).
17
+ > Validator: [`validate/m0StringValidator.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/validate/m0StringValidator.ts);
18
+ > error specs: [`errors/errors.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/errors/errors.ts);
19
+ > warning specs: [`warnings/warnings.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/warnings/warnings.ts);
20
+ > parsers: [`parse/m0StringParser.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts);
21
+ > types: [`types.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/types.ts).
22
+
23
+ ## Canonicalization (always applied first)
24
+
25
+ Before validation, the reference validator canonicalizes:
26
+
27
+ 1. Remove all whitespace
28
+ 2. Replace every `F` with `1`
29
+ 3. Replace every `>` with `0`
30
+
31
+ All correctness rules apply **after canonicalization**. Generators should emit
32
+ canonical form directly. `toCanonicalM0String` / `toPrettyM0String` are exported
33
+ from `@m0saic/dsl`
34
+ ([`format/m0StringFormat.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/format/m0StringFormat.ts)).
35
+
36
+ ### Compact form (transport only — outside the grammar)
37
+
38
+ A third form, **compact**, exists for length-constrained transports: pretty form
39
+ with runs of `>` or `-` folded into `N>` / `N-` (`6[1,0,0,0,0,1]` → `6[F,4>F]`).
40
+ Compact is **outside the grammar** — a folded string fails `isValidM0String` by
41
+ construction, and nothing in the parser, validator, or file formats accepts it.
42
+ Always unfold before parse, validate, or persist; files stay canonical.
43
+ `toCompactM0String` / `fromCompactM0String` are exported from `@m0saic/dsl`
44
+ ([`format/m0StringCompact.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/format/m0StringCompact.ts)),
45
+ and the round-trip law is
46
+ `fromCompactM0String(toCompactM0String(x)) === toCanonicalM0String(x)`.
47
+
48
+ The fold is unambiguous because NUMBER may only be followed by `(` or `[`
49
+ (invariant 2 under "Grammar invariants", below), so a digit adjacent to `>` or
50
+ `-` is free namespace no valid m0 can occupy. That
51
+ argument is also why the fold is only expressible over *pretty* tokens: it turns
52
+ on `>` and `-` living outside the digit alphabet. `0` is itself a digit and
53
+ appears inside ordinary split counts (`10(`, `100(`), so a canonical marker `N0`
54
+ would be built from the same characters counts are, with nothing delimiting the
55
+ run count from the digits after it — no free namespace, and no left-to-right way
56
+ to find the marker's edges.
57
+
58
+ `fromCompactM0String` is meant to be fed by untrusted input (share URLs are
59
+ forgeable) and bounds expansion before allocating: it throws on a zero count, a
60
+ non-safe-integer count, or a total past `MAX_COMPACT_EXPANSION` (10M chars — the
61
+ same ceiling `perf/large-dsl-ceiling.break.test.ts` treats as supported).
62
+
63
+ Consumers today are the layout share URL
64
+ (the Mosaic Desktop / Web app source (not published): compact on build, expand on parse)
65
+ and the editor's compact token view, which renders the same codec so the string
66
+ on screen matches the one a link carries. Share links are self-contained by
67
+ construction — there is no server-side share store — because compact fits ~98.8%
68
+ of layouts under the URL limit and the remainder exports a `.m0` file.
69
+
70
+ Compact was evaluated and **rejected** as a storage format — gzip already beats
71
+ the fold on stored bytes, and the file-format layers canonicalize on read AND
72
+ write.
73
+
74
+ ## Allowed characters (after canonicalization)
75
+
76
+ Digits `0–9`, classifiers `( ) [ ]`, overlays `{ }`, separator `,`, null tile
77
+ `-`. No letters, no spaces, no other symbols.
78
+
79
+ ## Token types
80
+
81
+ The DSL is parsed as a strict token stream:
82
+
83
+ | Token | Form | Meaning |
84
+ |---|---|---|
85
+ | PRIMITIVE | `0` | zero-frame (donates space forward) |
86
+ | PRIMITIVE | `-` | null-render tile (hole) |
87
+ | PRIMITIVEONE | `1` | rendered tile |
88
+ | NUMBER | digits forming an integer (`2`, `10`, `154`) | split count; `0` is NOT a NUMBER token |
89
+ | CLASSIFIEROPEN | `(` / `[` | column split (horizontal) / row split (vertical) |
90
+ | CLASSIFIERCLOSE | `)` / `]` | closes a split |
91
+ | OBJECTOPEN / OBJECTCLOSE | `{` / `}` | overlay object |
92
+ | COMMA | `,` | slot separator |
93
+
94
+ ## Root form
95
+
96
+ A m0 string must resolve to exactly one root node. Valid root shapes:
97
+
98
+ - `1` · `1{...}`
99
+ - `N(...)` · `N[...]` · `N(...){...}` · `N[...]{...}` (N ≥ 2)
100
+
101
+ The root may carry an overlay. There is no implicit wrapping — the DSL
102
+ represents a single explicit tree. Invalid roots: `0`, `-` (both
103
+ `INVALID_EMPTY`), `{1}`, `1{1}{1}` (`OVERLAY_CHAIN`).
104
+
105
+ ## Node forms
106
+
107
+ ### Primitives
108
+
109
+ `1` (rendered tile), `0` (zero-frame), `-` (null tile). Each may carry one
110
+ overlay: `1{1}`, `0{1}`, `-{1}`. Overlays never appear standalone — they always
111
+ attach to a primitive or container.
112
+
113
+ ### Numeric containers (splits)
114
+
115
+ `N( ... )` — column split; `N[ ... ]` — row split.
116
+
117
+ - **N ≥ 2.** A 1-way split is rejected: `1(` / `1[` →
118
+ `ILLEGAL_ONE_SPLIT` ("Use a count >= 2 for splits",
119
+ [`m0StringValidator.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/validate/m0StringValidator.ts) ~L793).
120
+ - **Count-exact:** the container must contain **exactly N child slots**. There
121
+ is no compression *in the grammar*; all slots are explicit. (The compact
122
+ transport form folds runs — see "Compact form" under Canonicalization,
123
+ above — but it is not valid m0 and never reaches the validator.)
124
+ - Commas are separators only; slot order is semantically meaningful and
125
+ preserved; slots may be primitives, nested containers, and may carry overlays.
126
+
127
+ Valid: `2(1,1)` · `3[1,0,1]` · `4(0,0,0,1)` · `2(1{1},1)`
128
+ Invalid: `2(1)` · `3(1,1)` · `2(1,1,1)` (all `TOKEN_COUNT`) · `3()` (`INVALID_EMPTY`) · `1(1)` (`ILLEGAL_ONE_SPLIT`)
129
+
130
+ ## Overlay objects `{}`
131
+
132
+ An overlay may follow a primitive or a numeric container: `node{ <valid m0> }`.
133
+
134
+ - Overlay contents are validated recursively.
135
+ - The overlay subtree renders within the parent's rectangle: origin = parent
136
+ rect origin, size = parent rect size. Overlays never escape their parent rect.
137
+ - Overlays may themselves contain splits and further overlays: `2(1,1){3[1,0,1]}`,
138
+ `1{2[3(1,1,1),3(1,1,1)]}`.
139
+ - Chained overlays are illegal: `1{1}{1}` → `OVERLAY_CHAIN` (nest instead:
140
+ `1{1{1}}`).
141
+
142
+ ## Zero-frame (`0`) semantics
143
+
144
+ `0` consumes a split slot but produces no rendered output. Consecutive `0`s form
145
+ a **donation run**: each `0` grows the merged region; the next claimant (`1` or
146
+ `-`) absorbs all donated space. `3(0,0,1)` → the `1` spans three slots.
147
+
148
+ ### Overlays on `0`
149
+
150
+ > An overlay attached to `0` applies to the merged region accumulated **at the
151
+ > moment the overlay appears**. It does not retroactively resize when later
152
+ > tokens grow the region.
153
+
154
+ Worked example — `4(0,0{2(1,1)},0,1)`:
155
+
156
+ 1. First `0` — merged region = 1 slot
157
+ 2. `0{2(1,1)}` — merged region = 2 slots; overlay canvas = those 2 slots
158
+ 3. Third `0` — merged region = 3 slots
159
+ 4. `1` — absorbs all 4 slots; the overlay stays sized to the 2-slot region
160
+
161
+ Result: base tile spans full width; the overlay occupies the left half only.
162
+ With multiple zero overlays (`5(0{1},0{1},0,0,1)`) each overlay is independent
163
+ and anchored at its creation-time region size. In short: `0` = grow region;
164
+ `0{...}` = grow region AND paint an overlay over the current region; the
165
+ claimant finalizes the region but never resizes earlier overlays.
166
+
167
+ ### Overlay paint order (deferred painting)
168
+
169
+ When multiple overlays exist at the same level, paint order is **not**
170
+ left-to-right by source position. The engine defers zero-overlay painting until
171
+ all sibling overlays are collected, then paints sorted by:
172
+
173
+ 1. **Area, descending** — the largest overlay paints first (bottom of the
174
+ overlay stack).
175
+ 2. **Stable rootId, ascending** — deterministic tie-break at identical area.
176
+
177
+ Smaller overlays therefore paint on top of larger ones. Source:
178
+ [`parse/sortDeferredOverlays.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/sortDeferredOverlays.ts),
179
+ called from [`parse/m0StringParser.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts).
180
+ Generators must trust the engine's ordering — do not try to encode z-order via
181
+ source position.
182
+
183
+ ## Null tile (`-`) semantics
184
+
185
+ Consumes space, renders nothing, does **not** donate space forward, may carry
186
+ overlays. Bare `-` as the root is invalid (`INVALID_EMPTY`).
187
+
188
+ ## Grammar invariants (hard rules)
189
+
190
+ 1. First token must be NUMBER or `1`
191
+ 2. NUMBER must be immediately followed by `(` or `[`
192
+ 3. All enclosures `() [] {}` must be balanced and correctly nested
193
+ 4. Every numeric container must contain exactly N child slots (N ≥ 2)
194
+ 5. No empty splits (`N()` / `N[]`)
195
+ 6. Token transitions must obey the validator's state rules
196
+ 7. Every donation run must resolve to a claimant
197
+ 8. A valid string must produce at least one renderable frame at the root
198
+ 9. Strings that produce zero-size frames at execution resolution fail at parse
199
+ (`SPLIT_EXCEEDS_AXIS`, below)
200
+
201
+ ## Passthrough-to-nothing
202
+
203
+ The validator rejects any split whose **last child** begins with passthrough
204
+ `0` — a trailing passthrough has no next tile to donate to. An overlay on the
205
+ `0` does not change this: the donation still goes nowhere. The rule applies
206
+ recursively inside nested classifiers AND overlay bodies, and also covers the
207
+ all-donors case (`2(0,0)` — structurally balanced, but no claimant exists).
208
+
209
+ Invalid: `2(1,0)` · `2(1,0{1})` · `3(1,1,0{1})` · `2(0,0)`
210
+ Valid: `2(0,1)` · `2(0{1},1)` · `3(1,0,1)`
211
+
212
+ Error code: `PASSTHROUGH_TO_NOTHING`.
213
+
214
+ ## No-sources
215
+
216
+ The validator rejects any layout containing **no leaf `1` anywhere** — all-`-`
217
+ / all-`0` layouts produce no renderable output.
218
+
219
+ Invalid: `2(-,-)` · `3[-,-,-]` — Valid: `2(1,-)` · `2(-,-){1}` (a source in an
220
+ overlay counts).
221
+
222
+ Error code: `NO_SOURCES`.
223
+
224
+ ## Overlay body rules
225
+
226
+ An overlay body `{...}` must contribute **at least one node** to the graph, but
227
+ those nodes do **not** have to paint. This lets overlays act as logical-owner
228
+ anchors (carrying `stableKey` + label without contributing rendered tiles).
229
+
230
+ Rejected (`INVALID_EMPTY`):
231
+
232
+ - `1{}` — empty body, no nodes at all
233
+ - `1{0}` — bare passthrough at the overlay root with nothing to donate to
234
+ - `1{-{}}` — recursive: the inner `{}` fails the same rule (deepest offender reported)
235
+
236
+ Accepted — at least one node, even if nothing paints:
237
+
238
+ - `1{-}` — single null node; valid logical-owner anchor
239
+ - `1{2(-,-)}` — all-null split; structural nodes, no paint
240
+ - `1{2(0,-)}` — passthrough with a sibling to donate to
241
+ - `2(-{F},-{F})` · `1{-{2(0,-)}}` · `1{2[1,1]{1}}`
242
+
243
+ The whole-string `NO_SOURCES` check still enforces that the ROOT layout paints
244
+ at least one source tile — the relaxation is per-overlay, so nested bodies can
245
+ be paint-free but the root must paint something. The legacy `ZERO_SOURCE_OVERLAY`
246
+ code is **no longer raised** (see the error table below).
247
+
248
+ ## Runtime feasibility guard: `SPLIT_EXCEEDS_AXIS`
249
+
250
+ `SPLIT_EXCEEDS_AXIS` is NOT a grammar error and is **never emitted by the
251
+ validator** — `isValidM0String` / `validateM0String` operate purely on the
252
+ token stream and have no notion of pixel dimensions. A string that will fail at
253
+ runtime due to infeasibility still validates `ok: true`.
254
+
255
+ It is emitted only by `parseM0StringComplete(m0, w, h)` after concrete frame
256
+ geometry is computed: every produced frame is checked for
257
+ `width > 0 && height > 0`, and any zero-size frame (or an empty frame set)
258
+ returns `{ ok: false, error: SPLIT_EXCEEDS_AXIS }`
259
+ ([`m0StringParser.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts) ~L1840).
260
+ The convenience wrappers (`parseM0StringToLogicalFrames`,
261
+ `parseM0StringToFullGraph`) delegate to `parseM0StringComplete` and collapse any
262
+ failure to `[]` — they do not surface the error object.
263
+
264
+ To preflight infeasibility **before** parse, use `computeFeasibility` (next
265
+ section). This is the recommended generator-side check.
266
+
267
+ ## Feasibility, precision, quantization → the geometry doc
268
+
269
+ Everything about whether a valid string **renders** at given dims, **looks
270
+ right**, and stays **balanced** — `computeFeasibility`, the precision floor,
271
+ quantization spread, outside-in remainder distribution, GCD collapse — lives in
272
+ [`feasibility-precision-quantization.md`](feasibility-precision-quantization.md)
273
+ (canonical). Quick reference: `computeFeasibility(m0)` → `{ minWidthPx,
274
+ minHeightPx }` (`@m0saic/dsl`,
275
+ [`feasibility/computeFeasibility.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/feasibility/computeFeasibility.ts));
276
+ render at dims ≥ that floor or the parse fails with `SPLIT_EXCEEDS_AXIS`.
277
+
278
+ ## Validation API
279
+
280
+ A string is valid **only if it passes the canonical validator**. Exported from
281
+ `@m0saic/dsl`
282
+ ([`validate/m0StringValidator.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/validate/m0StringValidator.ts)):
283
+
284
+ - `isValidM0String(s): boolean` — boolean shortcut.
285
+ - `validateM0String(s): { ok: true } | { ok: false, error }` — structured
286
+ result. **`error` is singular** — one `M0ValidationError` (`code`, `kind`,
287
+ `message`, `span`, `position`, optional `details`); there is no `errors`
288
+ array.
289
+
290
+ The validator runs in O(n) using a structural index; performance is consistent
291
+ across layout depth. Public error types (`M0ValidationErrorKind`,
292
+ `M0ValidationErrorCode`, `M0ValidationError`, `M0ValidationResult`) are
293
+ exported; the `M0_VALIDATION_ERROR_SPECS` table and `makeValidationError` are
294
+ internal ([`errors/index.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/errors/index.ts)).
295
+
296
+ ## Error codes (complete, as of 2026-07-27)
297
+
298
+ Every code declares a `kind` in
299
+ [`errors/errors.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/errors/errors.ts) —
300
+ `SYNTAX` (the string is not well-formed m0), `ANTIPATTERN` (well-formed DSL the
301
+ engine rejects by policy), `SEMANTIC` (reserved). A consumer uses `kind` to
302
+ decide grammar-bug vs policy-reject. Re-verify with:
303
+ `grep -n "kind:" https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/errors/errors.ts.
304
+
305
+ | Code | Kind | Trigger |
306
+ |---|---|---|
307
+ | `OVERLAY_CHAIN` | SYNTAX | chained overlays (`1{1}{1}`) — nest instead |
308
+ | `INVALID_CHAR` | SYNTAX | character outside the allowed set |
309
+ | `UNBALANCED` | SYNTAX | unbalanced `()` / `[]` / `{}` |
310
+ | `TOKEN_RULE` | SYNTAX | illegal token transition |
311
+ | `TOKEN_COUNT` | SYNTAX | container child count ≠ N |
312
+ | `ILLEGAL_ONE_SPLIT` | SYNTAX | `1(` / `1[` — split count must be ≥ 2 |
313
+ | `INVALID_EMPTY` | SYNTAX | degenerate input: empty string, bare `0` / `-` root, `N()`, empty overlay body `{}`, bare-passthrough body `{0}` |
314
+ | `PASSTHROUGH_TO_NOTHING` | ANTIPATTERN | trailing `0` in any split (recursive; an overlay on the `0` doesn't save it) |
315
+ | `NO_SOURCES` | ANTIPATTERN | no leaf `1` anywhere in the string |
316
+ | `SPLIT_EXCEEDS_AXIS` | ANTIPATTERN | parser-only: a split produced a 0-size frame at the given w×h |
317
+ | `ZERO_SOURCE_OVERLAY` | SEMANTIC | **reserved, never emitted since 2026-06-03** — the overlay-body relaxation dropped it (validator comment at [`m0StringValidator.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/validate/m0StringValidator.ts) ~L485: "The ZERO_SOURCE_OVERLAY rule above was dropped"); paint-free bodies now validate, `INVALID_EMPTY` covers the degenerate cases |
318
+
319
+ ## Warnings
320
+
321
+ Warnings are a **parse-time** surface, separate from validation errors — they
322
+ flag legal-but-suspect layouts without failing them.
323
+
324
+ - `parseM0StringComplete(input, width, height, opts?)` returns `ParseM0Result`:
325
+ `{ ok: true, ir, precision, warnings }` or
326
+ `{ ok: false, error, precision, warnings }` — `precision` and `warnings` are
327
+ **always present** regardless of `ok`.
328
+ - One warning code exists: `PRECISION_EXCEEDS_NORM` — emitted when the string's
329
+ `maxSplitAny` exceeds `opts.precisionNorm` (**default 100**;
330
+ [`m0StringParser.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts) ~L1814).
331
+ Raise the norm deliberately for intentionally fine layouts.
332
+ - Specs live in `M0_WARNING_SPECS`
333
+ ([`warnings/warnings.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/warnings/warnings.ts));
334
+ the spec table and `makeWarning` are internal — the public surface is the
335
+ `M0Warning` / `M0WarningCode` types (exported from
336
+ [`types.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/types.ts)) and the `warnings` array on
337
+ parse results.
338
+
339
+ Note `computePrecisionFromString` is an **internal** dsl helper, not public API
340
+ (barrel comment in [`parse/index.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/index.ts));
341
+ for precision metrics use `getComplexityMetricsFast(m0).precision` — see
342
+ [`dsl-complexity.md`](dsl-complexity.md).
343
+
344
+ ## Rules for generators
345
+
346
+ 1. **Emit canonical form** (`1` not `F`, `0` not `>`, no whitespace).
347
+ 2. **Build token arrays first; count child slots mechanically; assert the count
348
+ equals N.** Never guess counts.
349
+ 3. **Validate every string** with `isValidM0String` / `validateM0String` before
350
+ use. If validation fails, the string is invalid — regardless of intent.
351
+ 4. **Preflight feasibility when render dims are known:** refuse to emit when
352
+ `targetWidth < f.minWidthPx || targetHeight < f.minHeightPx` for
353
+ `f = computeFeasibility(m0)`.
354
+ 5. **Watch the warning surface:** a `PRECISION_EXCEEDS_NORM` on parse means the
355
+ layout is highly granular — expensive, size-sensitive, hard to reason about.
356
+ 6. If correctness cannot be proven, do not emit.
357
+
358
+ ## Known limitations
359
+
360
+ **Unbounded nesting depth.** Neither the grammar nor the implementation caps
361
+ nesting depth. The parser uses an iterative explicit-stack strategy
362
+ (`parseInternal` in
363
+ [`parse/m0StringParser.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts)),
364
+ not native recursion, so depths beyond 2000 work in practice; inputs are bounded
365
+ only by heap. There is no `DEPTH_EXCEEDED` error code. Generators emitting
366
+ machine-produced layouts should self-impose a depth cap if memory
367
+ predictability matters.