mosaic-headless 1.17.2 → 1.18.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 (64) hide show
  1. package/README.ja.md +17 -10
  2. package/README.ko.md +17 -10
  3. package/README.md +16 -9
  4. package/README.zh-TW.md +15 -9
  5. package/SKILL.md +96 -40
  6. package/assets/templates/platforms/claude-ai.json +1 -1
  7. package/assets/templates/platforms/claude-code.json +1 -1
  8. package/assets/templates/platforms/codex-cli.json +1 -1
  9. package/assets/templates/platforms/copilot.json +1 -1
  10. package/assets/templates/platforms/gemini-cli.json +1 -1
  11. package/bin/check-release.mjs +1 -1
  12. package/data/accordion-verification.csv +1 -1
  13. package/data/browser-verification.csv +906 -906
  14. package/data/conversion-verification.csv +1 -1
  15. package/data/db-columns.csv +40 -36
  16. package/data/design-audit-acknowledged.csv +1 -0
  17. package/data/design-audit.csv +1 -1
  18. package/data/intro-verification.csv +7 -7
  19. package/data/loop-verification.csv +5 -5
  20. package/data/node-properties.csv +4 -3
  21. package/data/node-property-verification.csv +81 -80
  22. package/data/node-type-notes.csv +1 -1
  23. package/data/node-types.csv +9 -9
  24. package/data/node-verification.csv +100 -100
  25. package/data/rest-routes.csv +1 -1
  26. package/data/style-properties.csv +1 -1
  27. package/data/style-state-verification.csv +48 -48
  28. package/data/style-states.csv +32 -32
  29. package/data/style-verification.csv +1 -1
  30. package/data/theme-zip-verification.csv +10 -10
  31. package/data/variants.csv +153 -0
  32. package/evals/accordion-is-not-broken/graders/criteria.md +1 -1
  33. package/evals/grid-child-placement/graders/criteria.md +3 -3
  34. package/package.json +1 -1
  35. package/references/data-model.md +7 -6
  36. package/references/design-system.md +28 -16
  37. package/references/interactions.md +1 -1
  38. package/references/responsive.md +6 -6
  39. package/references/styling.md +23 -14
  40. package/references/upgrading.md +182 -0
  41. package/references/vs-elementor-gutenberg.md +1 -1
  42. package/references/write-protocol.md +1 -1
  43. package/sites/_moksa.py +67 -62
  44. package/sites/moksa.json +219 -219
  45. package/tools/bootstrap_probe_theme.php +1 -1
  46. package/tools/build_page.py +17 -11
  47. package/tools/build_site.py +1 -1
  48. package/tools/capture_live.py +42 -6
  49. package/tools/data_upgrade.py +74 -0
  50. package/tools/extract_node_types.py +1 -1
  51. package/tools/from_elementor.py +3 -3
  52. package/tools/mo.py +10 -9
  53. package/tools/probe.py +1 -1
  54. package/tools/sweep_components.py +1 -1
  55. package/tools/sweep_interactions.py +3 -3
  56. package/tools/sweep_node_properties.py +1 -1
  57. package/tools/sweep_node_types.py +20 -0
  58. package/tools/sweep_style_properties.py +6 -7
  59. package/tools/sweep_style_states.py +2 -2
  60. package/tools/theme_export.php +2 -2
  61. package/tools/theme_zip_compare.php +2 -2
  62. package/tools/verify_browser.py +6 -5
  63. package/tools/verify_rwd.py +17 -15
  64. package/data/element-classes.csv +0 -152
package/SKILL.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: "mosaic-headless"
3
3
  description: |
4
- Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface with `mo.py`, which joins every source table to the live sweeps so a lookup leads with the measured verdict rather than the declaration (122 node types, 181 properties, 98 style properties with 20 structured value shapes pinned down, 53 style states, 151 element classes, 74 dynamic variables, 12 interaction triggers, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, the design-token and element-class layers verified against compiled CSS, the @VAR() dynamic language verified against rendered output, nine designed pages built through the tables themselves, and the delivered pages re-read in Chromium at three viewports so a rule that is present, correct and still wrong cannot pass. Drives Mosaic's own theme export/import from outside the editor and holds the copy against the source tree for tree.
4
+ Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface with `mo.py`, which joins every source table to the live sweeps so a lookup leads with the measured verdict rather than the declaration (122 node types, 182 properties, 98 style properties with 20 structured value shapes pinned down, 53 style states, 152 variants, 74 dynamic variables, 12 interaction triggers, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, the design-token and element-class layers verified against compiled CSS, the @VAR() dynamic language verified against rendered output, nine designed pages built through the tables themselves, and the delivered pages re-read in Chromium at three viewports so a rule that is present, correct and still wrong cannot pass. Drives Mosaic's own theme export/import and its plugin data upgrade from outside the editor and holds the copy against the source tree for tree. Measured on Mosaic Pro 1.0.8, with the 1.0.7 -> 1.0.8 migration (variants, universal classes, `m-` class names, customDeclarations) run and re-verified.
5
5
  license: "MIT"
6
6
  author: "moksa (https://moksaweb.com)"
7
- version: "1.17.2"
7
+ version: "1.18.0"
8
8
  ---
9
9
 
10
10
  # Headless Mosaic
@@ -48,7 +48,7 @@ python tools/mo.py css border-radius # which Mosaic key drives this CSS
48
48
  python tools/mo.py states --grep hover # state IDs and their selector templates
49
49
  python tools/mo.py states --verified # only the ones measured to compile
50
50
  python tools/mo.py vars --namespace post # the @VAR() surface
51
- python tools/mo.py classes --grep Heading # element classes (theme-global)
51
+ python tools/mo.py classes --grep Heading # variants: the theme-global element classes
52
52
  python tools/mo.py routes --grep template
53
53
  python tools/mo.py tables wp_mosaic_nodes
54
54
  python tools/mo.py skeleton # a minimal valid page spec
@@ -58,7 +58,7 @@ python tools/mo.py skeleton # a minimal valid page spec
58
58
  measured verdict rather than the declaration, because on this platform the two
59
59
  disagree for 52 of the 122 types.
60
60
 
61
- Then check the page. Mosaic has four failure modes and **only one of them changes the
61
+ Then check the page. Mosaic has eight failure modes and **only three of them change the
62
62
  HTTP status code**:
63
63
 
64
64
  ```
@@ -78,6 +78,12 @@ CONTENT when Mosaic parses it. A `code` node's content is a
78
78
  template: `@media(` (as every minifier writes it) is
79
79
  read as a function call and kills the whole page.
80
80
  `@media (` renders. build_page refuses the former.
81
+ plugin upgrade renamed HTTP 200, page intact, rows migrated - and a selector
82
+ what the page emits you wrote against an emitted class matches nothing.
83
+ 1.0.8: `M_EL4 M_EL_Div` -> `m-div _e`, the state
84
+ classes with them, `customStyles` -> `customDeclarations`.
85
+ The worked example's tap-to-enlarge stopped opening
86
+ and only `verify_loop.py` OPENS said so.
81
87
  ```
82
88
 
83
89
  That last one also fooled every checker here for a day: WordPress answers a fatal
@@ -125,8 +131,11 @@ measured.
125
131
  ## What was verified, and how
126
132
 
127
133
  Everything ran against a live install: WordPress 7.1, WooCommerce 11.1,
128
- Mosaic Pro 1.0.7, **unlicensed** — the licence gates the theme library and updates,
129
- not the node factories, so the Pro types register and render regardless.
134
+ Mosaic Pro 1.0.8, **unlicensed** — the licence gates the theme library and updates,
135
+ not the node factories, so the Pro types register and render regardless. The
136
+ site was built on 1.0.7 and upgraded in place: the 1.0.8 data migration
137
+ (`tools/data_upgrade.py`) ran over the real theme, and every sweep below was
138
+ re-run on the result, so the tables describe 1.0.8 as delivered, not as declared.
130
139
 
131
140
  ```
132
141
  WRITE PATH verified end to end over REST
@@ -135,10 +144,15 @@ WRITE PATH verified end to end over REST
135
144
  NODE SWEEP 122 of 122 node types, ONE PER DOCUMENT, committed then rendered then
136
145
  deleted, asserting each type's attrID against the delivered HTML:
137
146
  RENDERED 70 id found; tag and classes recorded
138
- COMMITTED 30 row exists, nothing reached the page
147
+ COMMITTED 25 row exists, nothing reached the page
139
148
  COMMIT_5xx 15 PHP fatal on commit
140
- BROKE_PAGE 7 committed, then the whole page died
141
- Free 74: 43/22/6/3. Pro 48: 27/8/9/4. data/node-verification.csv
149
+ BROKE_PAGE 12 committed, then the whole page died
150
+ Free 74: 43/18/6/7. Pro 48: 27/7/9/5. data/node-verification.csv
151
+ Re-swept on 1.0.8: five types that used to commit and render
152
+ nothing (accordion-title, loop-pagination and its two buttons,
153
+ multi-steps-form-step) now kill the page instead - the render
154
+ guard on their required parent throws. Same advice, louder
155
+ failure: nest them. `button` now renders `<button>`, not `<span>`.
142
156
  Three of the non-rendering outcomes are artefacts of the sweep's own
143
157
  method - one type per document, under a plain div - and not of the
144
158
  type: `component-instance` (needs `component-instance/<id>`),
@@ -150,9 +164,10 @@ STYLE the states[state][breakpoint][property] shape confirmed by writing
150
164
  and reading back the compiled CSS; 20 of 22 structured value shapes
151
165
  pinned down the same way. data/style-value-shapes.csv
152
166
 
153
- DESIGN SYS element classes and collection variables both verified against compiled
154
- CSS: an elementClass on the Heading 2 meta emitted a site-wide
155
- h2,.M_EL_Text__Heading2{...} rule, and a collection variable emitted
167
+ DESIGN SYS variants and collection variables both verified against compiled
168
+ CSS: a variant on the Heading 2 catalog entry emitted a site-wide
169
+ h2,.m-heading-2{...} rule (1.0.7: h2,.M_EL_Text__Heading2), and a
170
+ collection variable emitted
156
171
  :root{--brand: rgb(9, 99, 199)} with background-color:var(--brand).
157
172
  references/design-system.md
158
173
 
@@ -167,9 +182,10 @@ PROPERTIES 170 probes over the declared property surface, each value asserted
167
182
 
168
183
  STATES 52 of the 53 style states written to a live page and matched against
169
184
  the selector `data/style-states.csv` promises: 36 COMPILED exactly,
170
- 1 BROKE_PAGE, 12 NO_HOST (their node type cannot be committed safely),
171
- 3 SKIPPED. All seven globally usable states verified - and the
172
- pseudo-classes are emitted UPPERCASE (`.M_EL9:HOVER`), so grepping a
185
+ 13 NO_HOST (their node type cannot be committed safely), 3 SKIPPED,
186
+ 0 BROKE_PAGE on 1.0.8 (the one host that broke the page on 1.0.7
187
+ now refuses to commit alone, so it is NO_HOST). All seven globally usable states verified - and the
188
+ pseudo-classes are emitted UPPERCASE (`._j:HOVER`), so grepping a
173
189
  stylesheet for `:hover` finds nothing. data/style-state-verification.csv
174
190
 
175
191
  COMPONENTS used on the example page: the service row is committed ONCE to the
@@ -310,10 +326,17 @@ EVAL the skill itself, put in front of the model with and without it loa
310
326
  don't have reliable knowledge of Mosaic Pro's spec format"), which is
311
327
  the right thing for a model with no data to do. evals/
312
328
 
313
- MEASURED 114 REST routes, 151 element classes, 59 condition subjects,
314
- 23 tables / 206 columns - read off the running site.
329
+ MEASURED 114 REST routes, 152 variants, 59 condition subjects,
330
+ 23 tables / 210 columns - read off the running site.
315
331
 
316
- FROM SOURCE 122 node types, 181 properties (61 with enums), 207 pluggable IDs,
332
+ UPGRADE 1.0.7 -> 1.0.8 driven over the plugin's own milestone route from
333
+ outside wp-admin (6 milestones, 17 calls): four class tables renamed,
334
+ every emitted class name changed (M_EL4 M_EL_Div -> m-div _e),
335
+ customStyles -> customDeclarations in every stored row - and the
336
+ one selector on the worked example that named an emitted class
337
+ stopped matching until it was rewritten. references/upgrading.md
338
+
339
+ FROM SOURCE 122 node types, 182 properties (61 with enums), 207 pluggable IDs,
317
340
  122 placement rules, 10 composite default structures, 98 style
318
341
  properties, 53 style states.
319
342
  ```
@@ -324,8 +347,8 @@ the difference is worth being exact about:
324
347
 
325
348
  ```
326
349
  node types 122 / 122 swept live, one per document
327
- node properties 181 / 181 re-probed with a value shaped by each property's
328
- own validator chain: 35 APPLIED, 42 NO_EFFECT,
350
+ node properties 182 / 182 re-probed with a value shaped by each property's
351
+ own validator chain: 35 APPLIED, 43 NO_EFFECT,
329
352
  2 EDITOR_ONLY, 55 NO_HOST (no rendering type
330
353
  declares them), 2 INSTRUMENT, 45 SKIPPED
331
354
  style properties 98 / 98 swept live; 58 COMPILED, 18 ABSENT, 1 NO_ELEMENT,
@@ -334,7 +357,7 @@ style properties 98 / 98 swept live; 58 COMPILED, 18 ABSENT, 1 NO_ELEMENT,
334
357
  ```
335
358
 
336
359
  **A same-value probe measures the probe, not the surface.** The first property run
337
- sent the string `MPROP0000X` to all 181 properties regardless of what each wanted,
360
+ sent the string `MPROP0000X` to all 181 properties (182 since 1.0.8) regardless of what each wanted,
338
361
  and reported 91 NO_EFFECT. The tell was that `tagName` was in that list while the
339
362
  entire demo site is built on it. Re-probed with a value derived from the declared
340
363
  validator chain - array for `ValidatorArray`, boolean for `ValidatorBoolean`, a legal
@@ -351,7 +374,8 @@ located by `attrID`. They are labelled **INSTRUMENT**, which is not a pass eithe
351
374
  and the label carries the file and row count that does cover them.
352
375
 
353
376
  **Some properties are gated by a companion.** `target` and `rel` did nothing until
354
- the node also carried a `url`: `button` and `menu-link` render a `<span>` without one
377
+ the node also carried a `url`: `menu-link` renders a `<span>` without one (and so did
378
+ `button` before 1.0.8; it is a `<button>` now)
355
379
  and an `<a href>` with it, so an anchor-only attribute has nothing to attach to. A
356
380
  NO_EFFECT is only meaningful once the property has been given the context it needs.
357
381
 
@@ -371,13 +395,13 @@ Zero exceptions in either direction. The 20 are the `borderStyle` per-side longh
371
395
  (12), `outlineStyle` (4) and `gridChildPosition` (4) — so `borderLeftWidth`,
372
396
  `outlineColor` and `gridColumnStart` are all instances of one rule rather than three
373
397
  oddities. Set the grouped shape instead (`border` takes `{width, style, color}`), or
374
- use `customStyles`. `data/style-verification.csv` carries the group beside the result
398
+ use `customDeclarations`. `data/style-verification.csv` carries the group beside the result
375
399
  so the pattern is in the data, not just in this paragraph.
376
400
 
377
401
  **Known gaps, stated rather than papered over.**
378
402
 
379
403
  - `backgroundStyle` is the one style property whose shape resisted every attempt — it
380
- accepts what you send and emits `background-image:none`. Use `customStyles` for
404
+ accepts what you send and emits `background-image:none`. Use `customDeclarations` for
381
405
  gradients, as the glass and darkglow pages do.
382
406
  - **`gridColumnStart` / `gridColumnEnd` / `gridRowStart` / `gridRowEnd` emit nothing.**
383
407
  All four are real entries in `data/style-properties.csv`, under `gridChildPosition`.
@@ -412,7 +436,7 @@ so the pattern is in the data, not just in this paragraph.
412
436
  6. `references/responsive.md` - the state/breakpoint/property axis, the two
413
437
  breakpoint rows, and the override that can change a property but never remove
414
438
  one. **Read before writing any `_t` or `_m` value.**
415
- 7. `references/design-system.md` — element classes and design tokens. **Read this
439
+ 7. `references/design-system.md` — variants (element classes) and design tokens. **Read this
416
440
  before styling anything beyond a one-off page**; per-node style is the wrong layer
417
441
  for a real site.
418
442
  8. `references/dynamic-content.md` — the `@VAR()` language. The syntax is not
@@ -422,6 +446,9 @@ so the pattern is in the data, not just in this paragraph.
422
446
  10. `references/interactions.md` — the JavaScript animation system, and how far it is
423
447
  verified.
424
448
  11. `references/vs-elementor-gutenberg.md` — which builder habits transfer.
449
+ 12. `references/upgrading.md` — what a plugin update does to the data and to the
450
+ delivered page, measured on 1.0.7 -> 1.0.8; how to drive the migration and what
451
+ to re-verify afterwards.
425
452
 
426
453
  ## The data files
427
454
 
@@ -429,14 +456,14 @@ so the pattern is in the data, not just in this paragraph.
429
456
  |---|---|---|
430
457
  | `data/node-verification.csv` | 122 | **swept live** — outcome, rendered tag and classes, page bytes, failure detail |
431
458
  | `data/node-types.csv` | 122 | source — slug, label, edition, aliases, data class |
432
- | `data/node-properties.csv` | 181 | source — property, validator chain, **accepted enum values**, `supportsInherit` |
459
+ | `data/node-properties.csv` | 182 | source — property, validator chain, **accepted enum values**, `supportsInherit` |
433
460
  | `data/placement-rules.csv` | 122 | source — which children each type accepts |
434
461
  | `data/default-children.csv` | 10 | source — what a composite type needs **inside** it |
435
462
  | `data/style-properties.csv` | 98 | source — every settable CSS property and its value shape |
436
463
  | `data/style-value-shapes.csv` | 22 | **probed live** — the exact JSON shape for each structured value, and what it compiled to |
437
464
  | `data/style-states.csv` | 53 | source — state IDs with their exact CSS selector templates |
438
465
  | `data/property-verification.csv` | 170 | **probed live** — per-property effect on markup vs CSS, with unprovable enums marked INCONCLUSIVE |
439
- | `data/node-property-verification.csv` | 181 | **swept live** — each property probed with a value shaped by its own validator chain, on a type that declares it |
466
+ | `data/node-property-verification.csv` | 182 | **swept live** — each property probed with a value shaped by its own validator chain, on a type that declares it |
440
467
  | `data/style-verification.csv` | 98 | **swept live** — every style property written to a page and checked against the compiled CSS, with its group beside the result |
441
468
  | `data/rwd-verification.csv` | 731 | **checked live** - every `_t`/`_m` declaration vs the served stylesheet, with status per row |
442
469
  | `data/browser-verification.csv` | 3988 | **computed in Chromium** - declared vs `getComputedStyle` at three viewports, `not-comparable` labelled per row |
@@ -454,7 +481,7 @@ so the pattern is in the data, not just in this paragraph.
454
481
  | `data/node-type-notes.csv` | 8 | where a sweep outcome is true but misleading on its own, why. Surfaced by `mo.py type` |
455
482
  | `data/interaction-verification.csv` | 7 | **probed live** - interaction animation shapes, with negative controls and the stored row beside the payload |
456
483
  | `data/intro-verification.csv` | 8 + 15 | **sampled live** - the entrance sequence over fifteen timestamps on a monotonic clock, plus the eight assertions about it |
457
- | `data/element-classes.csv` | 151 | **live** — the built-in class metas; their IDs are what an `elementClass` record must use |
484
+ | `data/variants.csv` | 152 | **live** — the variant catalog (Mosaic's built-in element classes); their IDs are what a `variant` record must use, and `class_name` is what the element emits |
458
485
  | `data/dynamic-variables.csv` | 74 | source — every `@VAR('ns/name')` expression, by namespace |
459
486
  | `data/evaluator-functions.csv` | 19 | source — the `@` functions with their arity |
460
487
  | `data/interaction-types.csv` | 12 | source — trigger types, `timed` vs `progress` |
@@ -463,7 +490,7 @@ so the pattern is in the data, not just in this paragraph.
463
490
  | `data/condition-comparators.csv` | 12 | **live** — comparators and their operator sets |
464
491
  | `data/pluggables.csv` | 207 | source — every `setID()` by registry |
465
492
  | `data/rest-routes.csv` | 114 | **live** — method, path, args |
466
- | `data/db-columns.csv` | 206 | **live** — every column of all 23 tables |
493
+ | `data/db-columns.csv` | 210 | **live** — every column of all 23 tables |
467
494
 
468
495
  ## Building a page
469
496
 
@@ -490,19 +517,19 @@ tokens and element-class typography rather than per-node values:
490
517
  ```jsonc
491
518
  "theme": {
492
519
  "variables": {"--brand": {"type": "color", "value": "rgb(13,108,102)"}},
493
- "elementClasses": {"Heading 1": {"&": {"_": {"color": {"token": "--brand"}}}}}
520
+ "variants": {"Heading 1": {"&": {"_": {"color": {"token": "--brand"}}}}}
494
521
  }
495
522
  ```
496
523
 
497
524
  `{"token": "--brand"}` anywhere in a style resolves to the `{"var": "<uuid>"}`
498
525
  reference the compiler wants.
499
526
 
500
- **Two brands in one theme need namespaced tokens and no element classes at all.**
501
- Collection variables and element classes are both theme-global: two specs that each
527
+ **Two brands in one theme need namespaced tokens and no variants at all.**
528
+ Collection variables and variants are both theme-global: two specs that each
502
529
  declare `--ink` produce one `:root` with duplicate declarations, and two specs that
503
530
  each style `Heading 1` produce one set of rules. Whichever committed last wins, for
504
531
  every page. `sites/_moksa.py` namespaces its tokens (`--mk-*`) and bakes the type
505
- system onto the nodes with `apply_type()` instead of using element classes, which is
532
+ system onto the nodes with `apply_type()` instead of using variants, which is
506
533
  what lets it share an install with a completely different design.
507
534
 
508
535
  Whole themes move between installs with `theme_export.php` / `theme_import.php`.
@@ -552,8 +579,10 @@ post — `build_all.py` resets first for that reason.
552
579
 
553
580
  ## Facts worth knowing before you look anything up
554
581
 
555
- - **The REST namespace contains the plugin version** (`/wp-json/mosaic/v1.0.7`). Read
556
- it from `mosaicOptions.rest_api_url`, never hardcode.
582
+ - **The REST namespace contains the plugin version** (`/wp-json/mosaic/v1.0.8`). Read
583
+ it from `mosaicOptions.rest_api_url`, never hardcode. After a plugin update the
584
+ editor namespace is GONE until the data upgrade has run; only
585
+ `mosaic/<dataVersion>/<version>/upgrade` answers. `tools/data_upgrade.py`.
557
586
  - **`ordering` is a fractional-index string**, not a number.
558
587
  - **The tree is a `parentID` column**, not nesting. There is no page-level JSON blob.
559
588
  - **Everything is scoped to a `themeID`**, breakpoints included.
@@ -564,8 +593,13 @@ post — `build_all.py` resets first for that reason.
564
593
  - **Commit has side effects.** `heal()` runs inside it and creates breakpoints,
565
594
  collections and child nodes you never sent. Take every revision in the response.
566
595
  - **`modified_gmt` is server-assigned.**
567
- - **Styling hangs on the generated `.M_EL<n>` class, not your `attrID`**, and that
568
- number is not stable. The sibling `M_EL_<Type>` class is.
596
+ - **Styling hangs on the generated `._<token>` class, not your `attrID`**, and the
597
+ token (`_a`, `_b`, ... `_ba`: a base-38 per-document counter) is not stable. The
598
+ sibling `m-<type>` class is (`m-div`, `m-text m-wysiwyg`, `m-menu-link`), and a
599
+ variant that renders its name adds it (`m-heading-2`). Before 1.0.8 the same
600
+ three were `M_EL4`, `M_EL_Div` and `M_EL_Text__Heading2`; state classes moved
601
+ with them (`m-accordion-item--opened`, `m-menu-link--current`, `m-tab--active`).
602
+ Any CSS you write against an emitted class is coupled to the plugin version.
569
603
  - **`body{opacity:0}`** — the theme reveals itself from JavaScript. Screenshot tooling
570
604
  must let scripts run or it captures a blank page.
571
605
  - **Two themes can carry the same name, and only the ACTIVE one is bound to
@@ -580,7 +614,7 @@ post — `build_all.py` resets first for that reason.
580
614
  STALE CACHE when they differ by more than a percent - believe it, then find
581
615
  every layer. `wp breeze purge --cache=all` was the one that mattered.
582
616
  - **A `code` node is a wrapper, not a splice.** `insertLocation:"inPlace"` puts your
583
- markup INSIDE `<div class="M_EL_Code">`, one level down - so a sibling combinator
617
+ markup INSIDE `<div class="m-code _x">`, one level down - so a sibling combinator
584
618
  from injected HTML to a Mosaic node never matches, and a control you inject has
585
619
  to be styled through its own ancestors or through `:has()`. The native accordion
586
620
  avoided the question entirely.
@@ -596,10 +630,31 @@ post — `build_all.py` resets first for that reason.
596
630
  - **The front page 301s.** When a post is `page_on_front`, its own permalink
597
631
  (`/moksa/`) redirects to `/`; a fetch that does not follow redirects reads 0
598
632
  bytes and looks like an outage.
633
+ - **Every `build_site.py` run leaves a master behind, and they add up.** After a
634
+ season of probing the test theme held 223 masters and 98,261 nodes with 25
635
+ masters actually bound; Mosaic's ZIP import then spent 230s on masters and died
636
+ in templates when nginx closed the upstream at five minutes. Pruned to the bound
637
+ set (masters not reached from a bound template, their templates, their nodes)
638
+ the same import took under two minutes and compared 22 of 22. Prune before you
639
+ export; `wp_mosaic_template_assigns.parentID` -> template -> `masterID` is the
640
+ bound set, plus any `assign="auto"` template.
641
+ - **A variant row cannot be deleted, only emptied.** `status:"delete"` on a
642
+ variant is accepted and ignored - the row is catalog-backed. Commit it back with
643
+ `{"states": {"&": {"_": {}}}}` and the rule disappears; measured on Heading 2.
644
+ - **User class names have a grammar.** Variant sub classes and universal classes
645
+ emit `[a-z0-9_-]` segments joined by `--` (`m-button--primary--sm`), each segment
646
+ at most 60 characters, never starting with `_`, a digit or the reserved `m-`;
647
+ names that do not sanitize fall back to `class`, and collisions get `-2`, `-3`.
648
+ The emitted name is UNIQUE per theme (an `emittedName` column with a unique
649
+ index on four tables), so two classes cannot share a spelling.
599
650
 
600
651
  ## Rendered-tag facts you would otherwise guess wrong
601
652
 
602
- - **`button` renders as `<span>`**, not `<button>`. So do `menu-link` and `wysiwyg-link`.
653
+ - **`button` renders three tags since 1.0.8, by two properties.** With a `url` it is
654
+ `<a href>`; without one it is `<button type="button">` (1.0.7 emitted `<span>`);
655
+ with `inactive:"1"` - the new integer-as-string property, the Badge variant's
656
+ default - it is a `<span>`, a label rather than a control. Measured all three.
657
+ `menu-link` and `wysiwyg-link` still render `<span>` without a `url`.
603
658
  - **`text` renders as `<div>` by default** — set `tagName` for `<h1>`, `<p>` and so on.
604
659
  - **Eight types emit custom elements**: `<mosaic-dropdown>`, `<mosaic-navbar>`,
605
660
  `<mosaic-tabs>`, `<mosaic-accordion>`, `<mosaic-vimeo>`, `<mosaic-youtube>` and
@@ -643,6 +698,7 @@ post — `build_all.py` resets first for that reason.
643
698
  | `theme_zip.py` | drive Mosaic's OWN export/import - the ZIP the editor makes, attachments included, over the milestone protocol; import lands in test mode unless told `--activate` |
644
699
  | `theme_zip_compare.php` | hold an imported copy against its source, tree for tree - every scoped table, the (parentType, type) shape, which ids survive, and orphans named rather than counted |
645
700
  | `theme_delete.php` | remove a theme completely through the plugin's own routine; refuses the live one |
701
+ | `data_upgrade.py` | after a plugin update, run Mosaic's data migration over its own milestone route - the step wp-admin does from a screen - and set the config's version when the editor API is back |
646
702
  | `copy_styles.py` | push one node's style onto others, by attrID or prefix |
647
703
  | `bootstrap_probe_theme.php` | a licence-free scratch theme |
648
704
  | `mint_session.php` | a matching cookie + `wp_rest` nonce from WP-CLI |
@@ -657,7 +713,7 @@ python tools/extract_default_children.py <plugin-root> data/
657
713
  python tools/extract_style_properties.py <plugin-root> data/
658
714
  python tools/extract_interactions.py <plugin-root> data/
659
715
  python tools/extract_dynamic_variables.py <plugin-root> data/
660
- python tools/capture_live.py data/ # from data/raw/*.json
716
+ python tools/capture_live.py data/ # from data/raw/*.json + db-columns.txt
661
717
  # (raw dumps are gitignored;
662
718
  # re-capture from a live site)
663
719
 
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.17.2"
23
+ "version": "1.18.0"
24
24
  },
25
25
  "loaderBehaviour": "Upload via Settings -> Skills -> Upload. Claude.ai parses SKILL.md frontmatter and surfaces the skill in your library. The extraction tool (extract-block-schema.php) needs a live WP-CLI connection and won't run in the sandbox; use it from a local terminal against your own site instead.",
26
26
  "uploadSteps": [
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.17.2"
23
+ "version": "1.18.0"
24
24
  },
25
25
  "loaderBehaviour": "Auto-loads on session start when SKILL.md frontmatter parses successfully.",
26
26
  "verified": true,
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.17.2"
23
+ "version": "1.18.0"
24
24
  },
25
25
  "loaderBehaviour": "Confirmed (2026-07-11): Codex CLI natively supports the SKILL.md spec. Place SKILL.md under .codex/skills/<name>/ (project) or ~/.codex/skills/<name>/ (personal) and Codex loads the name+description at session start, then the full body on demand. A parallel, broader convention .agents/skills/ (searched from cwd up to repo root, then ~/.agents/skills/) also exists across multiple tools - if your Codex CLI version prioritizes that path instead, mirror the same SKILL.md there.",
26
26
  "verified": true,
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.17.2"
23
+ "version": "1.18.0"
24
24
  },
25
25
  "loaderBehaviour": "CHANGED as of 2026-07-11: GitHub Copilot added a proper '.github/skills/' Agent Skills directory (December 2025), alongside the older single-file .github/copilot-instructions.md convention. This config targets the new skills-directory form. If your Copilot version predates this (pre Dec 2025), use the instructions-append fallback instead (see fallback below).",
26
26
  "fallback": {
@@ -20,7 +20,7 @@
20
20
  "description": "Build and modify Mosaic Pro (Nextend) sites by writing the underlying data model directly - no visual editor, no DOM. Query the real surface (122 node types, 181 node properties, 98 style properties, 53 style states, 151 element classes, 74 dynamic variables, 114 REST routes, 23 tables) instead of guessing, with every node type placed on a live site one at a time and asserted against the delivered HTML, every style and node property swept against the compiled CSS and markup, and 569 responsive declarations checked against the stylesheet the site actually served.",
21
21
  "license": "MIT",
22
22
  "author": "moksa (https://moksaweb.com)",
23
- "version": "1.17.2"
23
+ "version": "1.18.0"
24
24
  },
25
25
  "loaderBehaviour": "CHANGED as of 2026-07-11: Gemini CLI now natively supports the same SKILL.md standard as Claude Code and Codex CLI - the same directory-based skill works unmodified. Gemini CLI discovers skills in this precedence order: built-in, extension skills, ~/.gemini/skills/ (personal), .gemini/skills/ (project, shared via version control). At session start Gemini injects each discovered skill's name+description into the system prompt and calls activate_skill when a task matches.",
26
26
  "verified": true,
@@ -77,7 +77,7 @@ const rows = (p) => read(p).trim().split("\n").length - 1; // minus the header
77
77
  const counts = {
78
78
  "data/node-verification.csv": 122,
79
79
  "data/style-verification.csv": 98,
80
- "data/node-property-verification.csv": 181,
80
+ "data/node-property-verification.csv": 182,
81
81
  "data/rwd-verification.csv": 731,
82
82
  "data/browser-verification.csv": 3988,
83
83
  "data/style-state-verification.csv": 52,
@@ -1,6 +1,6 @@
1
1
  step,result,detail
2
2
  nested commit accepted,PASS,no exception from the commit
3
- page still renders,PASS,28853 bytes delivered
3
+ page still renders,PASS,28783 bytes delivered
4
4
  title rendered,PASS,the accordion title is in the delivered HTML
5
5
  content rendered,PASS,the accordion content is in the delivered HTML
6
6
  carries plugin markup,PASS,found 'mosaic-accordion' in the output