mosaic-headless 1.32.1 → 1.32.2
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/SKILL.md +20 -5
- package/assets/templates/platforms/claude-ai.json +1 -1
- package/assets/templates/platforms/claude-code.json +1 -1
- package/assets/templates/platforms/codex-cli.json +1 -1
- package/assets/templates/platforms/copilot.json +1 -1
- package/assets/templates/platforms/gemini-cli.json +1 -1
- package/package.json +1 -1
- package/references/design-system.md +34 -1
- package/references/dialog-and-triggers.md +5 -0
- package/references/dynamic-content.md +15 -1
- package/references/styling.md +3 -1
- package/references/templates-and-conditions.md +47 -11
- package/tools/build_page.py +15 -7
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: |
|
|
|
5
5
|
license: "MIT"
|
|
6
6
|
metadata:
|
|
7
7
|
author: "moksa (https://moksaweb.com)"
|
|
8
|
-
version: "1.32.
|
|
8
|
+
version: "1.32.2"
|
|
9
9
|
homepage: "https://github.com/Moksa1123/mosaic-headless"
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -490,10 +490,12 @@ so the pattern is in the data, not just in this paragraph.
|
|
|
490
490
|
properties whose descriptor exists, and those are created during sync on a
|
|
491
491
|
`DataMeta` that starts empty. Animate with the CSS state path, which is fully
|
|
492
492
|
verified: 36 of 37 probed states compile to exactly the promised selector.
|
|
493
|
-
-
|
|
494
|
-
`
|
|
495
|
-
|
|
496
|
-
|
|
493
|
+
- An element **condition** puts its comparator INSIDE the subject's options
|
|
494
|
+
(`subject.<type>Options.comparator`). One level up is a PHP fatal that blanks the
|
|
495
|
+
whole page; so is `{"v": …}` as a `variable` subject's `dynamicCode` (a plain
|
|
496
|
+
string there). A server-condition `regexp` needs delimiters (`/./`) - `.` never
|
|
497
|
+
matches and the element silently disappears. Driven end to end on a client build;
|
|
498
|
+
references/templates-and-conditions.md
|
|
497
499
|
|
|
498
500
|
## Orient yourself in this order
|
|
499
501
|
|
|
@@ -821,6 +823,19 @@ post — `build_all.py` resets first for that reason.
|
|
|
821
823
|
default - it is a `<span>`, a label rather than a control. Measured all three.
|
|
822
824
|
`menu-link` and `wysiwyg-link` still render `<span>` without a `url`.
|
|
823
825
|
- **`text` renders as `<div>` by default** — set `tagName` for `<h1>`, `<p>` and so on.
|
|
826
|
+
- **`menu-link` is `width: fit-content` by default** (`.m-menu-link{display:flex;gap:4px;
|
|
827
|
+
padding:4px 8px;width:fit-content;…}`). Used as a cell of a button row or a grid -
|
|
828
|
+
a phone contact bar on `2fr 1fr 1fr` - its background hugs the text and the cell
|
|
829
|
+
stays empty around it, whatever `display` or `justify-content` you give it. Set
|
|
830
|
+
`width: 100%` on it (reported from a client build; the rule read from 1.0.10's
|
|
831
|
+
`style.css`). The same rule's `align-items:center` matters when a `menu-link`
|
|
832
|
+
wraps a whole card: set `alignItems: stretch` and `textAlign: left` on it.
|
|
833
|
+
- **`customDeclarations` is all or nothing, and its unit list is short.** The
|
|
834
|
+
validator parses every declaration and drops the WHOLE block if one value fails.
|
|
835
|
+
`!important` fails, and so does any unit outside `ValidatorCustomDeclarations`'s
|
|
836
|
+
list - `dvh`, `svh`, `lvh`, `dvw`, container units: `height: 100dvh` takes every
|
|
837
|
+
other line in the block with it, silently. The structured `height` / `maxHeight`
|
|
838
|
+
fields do accept `dvh`.
|
|
824
839
|
- **`youtube` renders `<mosaic-youtube><iframe>` with no `title`** - the iframe gets
|
|
825
840
|
only `loading`, `src`, `allow` and an id, so an accessible name has to be added
|
|
826
841
|
some other way. `youtubeUrl` is `{"v": "https://www.youtube.com/watch?v=…"}`;
|
|
@@ -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.32.
|
|
23
|
+
"version": "1.32.2"
|
|
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.32.
|
|
23
|
+
"version": "1.32.2"
|
|
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.32.
|
|
23
|
+
"version": "1.32.2"
|
|
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.32.
|
|
23
|
+
"version": "1.32.2"
|
|
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.32.
|
|
23
|
+
"version": "1.32.2"
|
|
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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mosaic-headless",
|
|
3
|
-
"version": "1.32.
|
|
3
|
+
"version": "1.32.2",
|
|
4
4
|
"description": "AI-agent skill: build Mosaic Pro (Nextend) WordPress sites by writing the underlying data model directly - 23 custom tables, no visual editor, no DOM. Every node type, style property and node property swept against a live install and asserted on the delivered HTML and compiled CSS. Installs into Claude Code, Cursor, Codex CLI, Gemini CLI, Copilot, Continue, Windsurf and Claude.ai.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wordpress",
|
|
@@ -60,7 +60,40 @@ Three things learned the hard way:
|
|
|
60
60
|
goes away.
|
|
61
61
|
- Look up the ID in `data/variants.csv` by `name`. Several rows share a
|
|
62
62
|
name (`Button` appears four times) because sub-components have their own metas — the
|
|
63
|
-
`selectors` and `parent` columns tell them apart.
|
|
63
|
+
`selectors` and `parent` columns tell them apart. `Item` is the trap: the accordion,
|
|
64
|
+
loop and list items are all `Item` and all have a parent, so "the parent-less one"
|
|
65
|
+
finds none. `build_page.py` takes a theme variant key as the catalog id, the
|
|
66
|
+
`class_name` (`m-loop-item`) or a name, and refuses a name that matches more than
|
|
67
|
+
one row.
|
|
68
|
+
|
|
69
|
+
More from a client build on 1.0.10:
|
|
70
|
+
|
|
71
|
+
- **`HTML` and `Body` emit `html{…}` and `body{…}`**, and `customDeclarations` works
|
|
72
|
+
on them - the place for page-wide settings that have no node.
|
|
73
|
+
- **The loop pagination states work on the variants**:
|
|
74
|
+
`____loop_pagination_number--active` on `Number`,
|
|
75
|
+
`___loop_pagination_button--disabled` on `Pagination button`.
|
|
76
|
+
- **Variant CSS is printed early in `<head>`.** At equal specificity, a plugin's
|
|
77
|
+
`<link>` stylesheet and a page's own `<style>` both come later and win. Moving a
|
|
78
|
+
rule from a plugin stylesheet into a variant can therefore bring back a page rule
|
|
79
|
+
the plugin had been overriding.
|
|
80
|
+
- **The Gutenberg variants are scoped to block markup.** They match
|
|
81
|
+
`:is(.entry-content, .wp-block-post-content) X`, and `List` matches only
|
|
82
|
+
`ul.wp-block-list` - a list written by Rank Math's FAQ block or in raw HTML has no
|
|
83
|
+
such class and is not styled. A `wpContent` node renders
|
|
84
|
+
`.m-post-content > .wp-site-blocks > .entry-content`.
|
|
85
|
+
|
|
86
|
+
## Universal classes
|
|
87
|
+
|
|
88
|
+
A universal class is the theme-global class a user names (the editor's "classes"),
|
|
89
|
+
committed on the master document under the key `universalClass` with
|
|
90
|
+
`data: {name, label, states}` - `states` is the same structure as a variant's. A node
|
|
91
|
+
takes it through `style.universalClasses`, a map `{<classID>: <classID>}` (a map to a
|
|
92
|
+
universal SUB class ID applies that sub class instead; `ElementMResourceStyle.php`).
|
|
93
|
+
The emitted class is the `name` (sanitized, any `m-` prefix dropped - the
|
|
94
|
+
class-name grammar is in SKILL.md), and its CSS is printed only
|
|
95
|
+
on a page where some node uses it. `status: "delete"` removes one - unlike a variant,
|
|
96
|
+
a universal class is not catalog-backed.
|
|
64
97
|
|
|
65
98
|
## Collection variables: the design tokens
|
|
66
99
|
|
|
@@ -604,6 +604,11 @@ document. Lay the forms out by positioning the WINDOW instead, which leaves
|
|
|
604
604
|
| corner notice | no padding | `top:90px; right:24px`, a small `max-width` |
|
|
605
605
|
| full takeover | no padding | `top:0; right:0; bottom:0; left:0`, `max-width:none` |
|
|
606
606
|
|
|
607
|
+
"No padding" has to be written: the host starts with the browser's own
|
|
608
|
+
`dialog { padding: 1em }`, because Mosaic's `.m-modal` rule resets inset, margin and
|
|
609
|
+
border but not padding. A client build measured a drawer's host at `padding:
|
|
610
|
+
14.408px` - 1em of its fluid body size - with no modal variant on the page.
|
|
611
|
+
|
|
607
612
|
`transform` is a structured value, so the centring pull-back is two entries
|
|
608
613
|
(`translateX`, `translateY`), not a string — `references/styling.md` has the shape.
|
|
609
614
|
|
|
@@ -191,7 +191,21 @@ value `{"v": "@LOOP('post/work_category', 1, 'term_id')"}`. A Meta Box checkbox
|
|
|
191
191
|
`settings: {"key": "work_featured"}` and a `text` comparator
|
|
192
192
|
`{"operator": "equals", "settings": {"value": "1"}}` - six posts back, in order.
|
|
193
193
|
|
|
194
|
-
`orderByClauses`
|
|
194
|
+
`orderByClauses` is a flat list of clauses, each `{uuid, type, <type>Options:
|
|
195
|
+
{settings}}` (`ValidatorOrderBy.php`), under `wpPostType<Type>Options`. Sorting by a
|
|
196
|
+
post column:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
[{"type": "postVariable", "uuid": "<uuid>", "postVariableOptions": {"settings": {
|
|
200
|
+
"key": "post_date", "cast": "auto", "direction": "ASC"}}}]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Every settings field is required, `cast` included.** The validator runs each of the
|
|
204
|
+
type's fields and rejects the clause on the first missing one, and a rejected clause
|
|
205
|
+
is stored as `[]` - no error, and the loop falls back to the default newest-first
|
|
206
|
+
order. Reported from a client build on 1.0.10; the validator read from source.
|
|
207
|
+
|
|
208
|
+
`orderByClauses` also accepts `random`: `{"type": "random", "uuid": "<uuid>",
|
|
195
209
|
"randomOptions": {"settings": {}}}`, a different order on every uncached render. Under
|
|
196
210
|
a page cache that is one order per cache lifetime, not per visitor.
|
|
197
211
|
|
package/references/styling.md
CHANGED
|
@@ -179,7 +179,9 @@ which are `SingleTransformTypeFactory` and do take `{"value": …}`.
|
|
|
179
179
|
rewrites stored rows, and `build_page.py` still accepts the old spelling in a spec)
|
|
180
180
|
accepts raw CSS text and emits it verbatim into the rule:
|
|
181
181
|
`"customDeclarations": "outline: 2px dashed rgb(7,8,9);"` compiled through
|
|
182
|
-
unchanged.
|
|
182
|
+
unchanged. Every value is still validated, and one bad value - `!important`, a `dvh`
|
|
183
|
+
or other unit outside the validator's list - drops the whole block silently. It is the
|
|
184
|
+
fallback for anything whose structured shape you have not pinned
|
|
183
185
|
down — at the cost of bypassing the collection-variable layer entirely.
|
|
184
186
|
|
|
185
187
|
`tools/build_page.py` wraps the five shapes above as `radius`, `shadow`,
|
|
@@ -88,11 +88,12 @@ interaction gating and form actions — four contexts, one grammar.
|
|
|
88
88
|
"uuid": "<uuid>",
|
|
89
89
|
"type": "serverCondition",
|
|
90
90
|
"serverConditionOptions": {
|
|
91
|
-
"subject":
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
"subject": {"type": "httpGet",
|
|
92
|
+
"httpGetOptions": {
|
|
93
|
+
"settings": {"name": "show"},
|
|
94
|
+
"comparator": {"type": "text", // INSIDE <type>Options
|
|
95
|
+
"textOptions": {"operator": "equals",
|
|
96
|
+
"settings": {"value": "yes"}}}}}
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
]
|
|
@@ -103,6 +104,41 @@ interaction gating and form actions — four contexts, one grammar.
|
|
|
103
104
|
The `<id>Options` suffix is the same convention as everywhere else in Mosaic
|
|
104
105
|
(`RuleTypeAbstract::getOptionsDataName()` returns `getID() . 'Options'`).
|
|
105
106
|
|
|
107
|
+
**The comparator lives inside the subject's options**, beside `settings`, not beside
|
|
108
|
+
`subject`. The validator reads it as `subject.<type>Options.comparator`
|
|
109
|
+
(`ValidatorEvaluator.php`) and so does the evaluator (`RuleTypeAbstract.php`:
|
|
110
|
+
`$subjectData->{$subjectType . 'Options'}->comparator`). Put it one level up and the
|
|
111
|
+
evaluator finds no comparator and calls
|
|
112
|
+
`SubjectMeta::getLocalOrGlobalComparatorMetaByID(null)`, which is a PHP fatal
|
|
113
|
+
(`Undefined property $comparator`) that takes the WHOLE page down, not just the
|
|
114
|
+
element. Reported from a client build on 1.0.10 and confirmed in the source; the post
|
|
115
|
+
loop filters in `references/dynamic-content.md` use the same nesting.
|
|
116
|
+
|
|
117
|
+
"Show only when a field has a value", as it ran on a client site on 1.0.10:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{"uuid": "<uuid>", "type": "serverCondition", "serverConditionOptions": {"subject": {
|
|
121
|
+
"type": "variable", "variableOptions": {
|
|
122
|
+
"settings": {"dynamicCode": "@VAR('post/meta_x')"},
|
|
123
|
+
"comparator": {"type": "text", "textOptions": {
|
|
124
|
+
"operator": "regexp", "settings": {"value": "/./"}}}}}}}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- **`dynamicCode` is a plain string.** The `variable` subject hands its setting
|
|
128
|
+
straight to the evaluator; `{"v": "@VAR(…)"}` there reaches
|
|
129
|
+
`LocalContext::evaluateDynamicCodeToStringOrArray()` as a `stdClass` and is a
|
|
130
|
+
page-wide fatal. (A post loop FILTER value is the opposite: it is `{"v": …}`.)
|
|
131
|
+
- **`regexp` needs its delimiters here.** `ServerConditionComparatorText` passes the
|
|
132
|
+
value to `preg_match()` as the whole pattern, so it is `/./`, not `.`. A pattern
|
|
133
|
+
without delimiters makes `preg_match` return false, which is read as "no match":
|
|
134
|
+
the element disappears on every page and nothing is reported. A post loop filter's
|
|
135
|
+
`regexp` is the opposite again - it becomes SQL `REGEXP` and takes NO delimiters
|
|
136
|
+
(`FilterSubjectPostTypes.php`).
|
|
137
|
+
- Units in one group AND: `regexp /./` plus `not-contains` on the same variable is
|
|
138
|
+
"has a value and is not X".
|
|
139
|
+
- Inside a `wpPostType` loop, `@VAR('<loopNamespace>/meta_x')` evaluates per item, so
|
|
140
|
+
each row shows or hides on its own field.
|
|
141
|
+
|
|
106
142
|
**An empty or absent `conditions` matches.** `matchConditions()` returns `true` when
|
|
107
143
|
the evaluation yields null, so a template with no conditions always applies and an
|
|
108
144
|
element with none always renders. Conditions subtract, they never add.
|
|
@@ -134,9 +170,9 @@ can be an `@` expression rather than a literal — see `references/dynamic-conte
|
|
|
134
170
|
## Verification status
|
|
135
171
|
|
|
136
172
|
The routing rules and the condition grammar above are read from source and from the
|
|
137
|
-
live metas endpoints.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
173
|
+
live metas endpoints. Element conditions (`node.data.conditions`) have been driven end
|
|
174
|
+
to end on a client build on 1.0.10 with the shape above - the `variable` subject, the
|
|
175
|
+
`text` comparator's `regexp` and `not-contains`, inside and outside a post loop. Other
|
|
176
|
+
subjects are read from source only; build one, then read the row back and diff it
|
|
177
|
+
against what you sent. A wrong shape is either silently dropped or a fatal on render,
|
|
178
|
+
so load the page after every condition you commit.
|
package/tools/build_page.py
CHANGED
|
@@ -492,17 +492,25 @@ def theme_records(spec, doc, surface):
|
|
|
492
492
|
|
|
493
493
|
classes = theme.get("variants") or theme.get("elementClasses") or {}
|
|
494
494
|
if classes:
|
|
495
|
-
|
|
496
|
-
for row in load_csv("variants.csv"):
|
|
497
|
-
by_name.setdefault(row["name"], []).append(row)
|
|
495
|
+
catalog = load_csv("variants.csv")
|
|
498
496
|
recs = []
|
|
499
497
|
for name, states in classes.items():
|
|
500
|
-
|
|
498
|
+
# a catalog id, a class_name (m-loop-item) or a name. Names repeat -
|
|
499
|
+
# `Item` is the accordion, loop AND list item, all three with a parent -
|
|
500
|
+
# so a name only resolves when it is the sole row, or the sole
|
|
501
|
+
# parent-less one, carrying it
|
|
502
|
+
matches = ([m for m in catalog if m["id"] == name]
|
|
503
|
+
or [m for m in catalog if m["class_name"] == name]
|
|
504
|
+
or [m for m in catalog if m["name"] == name])
|
|
501
505
|
if not matches:
|
|
502
|
-
sys.exit("no variant
|
|
506
|
+
sys.exit("no variant %r; see data/variants.csv" % name)
|
|
507
|
+
roots = [m for m in matches if not m["parent"]]
|
|
508
|
+
if len(matches) > 1 and len(roots) != 1:
|
|
509
|
+
sys.exit("variant name %r is ambiguous; use one of: %s" % (
|
|
510
|
+
name, ", ".join(m["class_name"] for m in matches)))
|
|
503
511
|
# a fresh UUID here is accepted and then silently dropped, so the meta ID
|
|
504
|
-
# is the only thing that works
|
|
505
|
-
top =
|
|
512
|
+
# is the only thing that works
|
|
513
|
+
top = roots[0] if len(matches) > 1 else matches[0]
|
|
506
514
|
recs.append({"newRevisionRecord": {
|
|
507
515
|
"ID": top["id"], "parentType": "", "parentID": "", "ordering": "a0",
|
|
508
516
|
"status": "publish", "revision": "", "version": "",
|