mosaic-headless 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +1 -1
- 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/failure-modes.md +15 -0
- package/tools/build_site.py +43 -0
package/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: |
|
|
|
4
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 page re-read in Chromium at three viewports so a rule that is present, correct and still wrong cannot pass.
|
|
5
5
|
license: "MIT"
|
|
6
6
|
author: "moksa (https://moksaweb.com)"
|
|
7
|
-
version: "1.5.
|
|
7
|
+
version: "1.5.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Headless Mosaic
|
|
@@ -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.5.
|
|
23
|
+
"version": "1.5.1"
|
|
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.5.
|
|
23
|
+
"version": "1.5.1"
|
|
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.5.
|
|
23
|
+
"version": "1.5.1"
|
|
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.5.
|
|
23
|
+
"version": "1.5.1"
|
|
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.5.
|
|
23
|
+
"version": "1.5.1"
|
|
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.5.
|
|
3
|
+
"version": "1.5.1",
|
|
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",
|
|
@@ -188,6 +188,21 @@ wrappers were sitting correctly in the database and in the committed JSON.
|
|
|
188
188
|
The tell is the byte count: if the size the builder reports and the size you fetch
|
|
189
189
|
disagree, you are not looking at what you wrote.
|
|
190
190
|
|
|
191
|
+
**And the cache cuts the other way too, which is worse.** Once every tool in the
|
|
192
|
+
toolchain cache-busts, nothing is left looking at the page a visitor receives. A
|
|
193
|
+
build finishes, every check reports green - responsive verified, computed values
|
|
194
|
+
agreed, design audit clean, the entrance animation asserted on seven counts - and
|
|
195
|
+
the site is still serving the previous document to everybody. Measured here: a
|
|
196
|
+
visitor got 141,134 bytes with none of the new work in it while a cache-busted fetch
|
|
197
|
+
of the same URL gave 148,457. Nothing was wrong except that nobody had looked.
|
|
198
|
+
|
|
199
|
+
`build_site.py` now fetches each page twice at the end of the build, plain and
|
|
200
|
+
cache-busted, and says so when they disagree. It cannot purge the cache - that needs
|
|
201
|
+
credentials a build script has no business holding - but it must not be silent. Two
|
|
202
|
+
fetches of the same live page differ by a few hundred bytes anyway, since nonces and
|
|
203
|
+
ids regenerate per request, so the tolerance is proportional: natural variance
|
|
204
|
+
measured at 0.25%, a stale document at 5%.
|
|
205
|
+
|
|
191
206
|
`tools/verify_rwd.py` now appends a unique `_v=<ms>` query string and sends
|
|
192
207
|
`Cache-Control: no-cache` on every fetch, so the assertion is always made against the
|
|
193
208
|
document that was actually committed. Do the same in anything else that checks a page
|
package/tools/build_site.py
CHANGED
|
@@ -32,6 +32,7 @@ import argparse
|
|
|
32
32
|
import json
|
|
33
33
|
import os
|
|
34
34
|
import sys
|
|
35
|
+
import urllib.request
|
|
35
36
|
import urllib.parse
|
|
36
37
|
import uuid
|
|
37
38
|
|
|
@@ -148,6 +149,45 @@ def build_page_document(client, cfg, master_id, template_id, tree, surface):
|
|
|
148
149
|
return len(records)
|
|
149
150
|
|
|
150
151
|
|
|
152
|
+
def cache_check(url, _fresh_bytes=None):
|
|
153
|
+
"""Fetch the URL twice - once as a visitor, once cache-busted - and compare.
|
|
154
|
+
|
|
155
|
+
Every check in this repo cache-busts, for good reason: a verifier that reads a
|
|
156
|
+
stale copy reports the previous build. But that means nothing here had ever
|
|
157
|
+
looked at the page an actual VISITOR receives. A full-page cache in front of
|
|
158
|
+
WordPress - Varnish on Cloudways, any CDN - keeps serving the old document after
|
|
159
|
+
a perfectly successful build, and the whole toolchain reports green while the
|
|
160
|
+
site shows yesterday's page. Measured here: 141,134 bytes to a visitor while the
|
|
161
|
+
build had just produced 148,457.
|
|
162
|
+
|
|
163
|
+
Both fetches happen here, at the same moment, because comparing against a size
|
|
164
|
+
measured earlier in the build folds in every byte that changed in between. Two
|
|
165
|
+
fetches of the same live page still differ by a few hundred bytes - nonces and
|
|
166
|
+
ids are regenerated per request - so the tolerance is proportional: natural
|
|
167
|
+
variance measured at 0.25%, a stale document at 5%.
|
|
168
|
+
|
|
169
|
+
This cannot purge the cache; that needs credentials this tool has no business
|
|
170
|
+
holding. Silence is the one thing it must not do.
|
|
171
|
+
"""
|
|
172
|
+
def get(u):
|
|
173
|
+
req = urllib.request.Request(u, headers={"User-Agent": "Mozilla/5.0"})
|
|
174
|
+
with urllib.request.urlopen(req, timeout=60) as r:
|
|
175
|
+
return r.read(), (r.headers.get("Age") or r.headers.get("X-Cache") or "")
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
sep = "&" if "?" in url else "?"
|
|
179
|
+
fresh, _ = get("%s%s_v=%d" % (url, sep, uuid.uuid4().int % 10 ** 9))
|
|
180
|
+
plain, age = get(url)
|
|
181
|
+
except Exception as exc: # noqa: BLE001
|
|
182
|
+
return "could not compare the cached page: %s" % exc
|
|
183
|
+
|
|
184
|
+
if abs(len(plain) - len(fresh)) <= max(512, len(fresh) // 100):
|
|
185
|
+
return ""
|
|
186
|
+
return ("STALE CACHE: a visitor gets %d bytes, a fresh fetch gives %d%s"
|
|
187
|
+
" - purge the page cache or the site keeps serving the old document"
|
|
188
|
+
% (len(plain), len(fresh), (" (Age %s)" % age) if age else ""))
|
|
189
|
+
|
|
190
|
+
|
|
151
191
|
def main():
|
|
152
192
|
ap = argparse.ArgumentParser()
|
|
153
193
|
ap.add_argument("--config", required=True)
|
|
@@ -168,6 +208,9 @@ def main():
|
|
|
168
208
|
ok = len(body) >= MIN_HEALTHY_BYTES
|
|
169
209
|
print(" %-16s %-7s %6d bytes %3d nodes %s" % (
|
|
170
210
|
page["slug"], "OK" if ok else "BROKEN", len(body), n, url))
|
|
211
|
+
stale = cache_check(url, len(body))
|
|
212
|
+
if stale:
|
|
213
|
+
print(" %-16s %s" % ("", stale))
|
|
171
214
|
|
|
172
215
|
|
|
173
216
|
if __name__ == "__main__":
|