similarbuild 0.1.0 → 0.2.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/CHANGELOG.md +1 -1
- package/README.md +2 -2
- package/package.json +1 -1
- package/templates/commands/build-page.md +73 -14
- package/templates/commands/build-site.md +122 -13
- package/templates/commands/clip-section.md +67 -8
- package/templates/memory/anti-patterns.md +12 -12
- package/templates/memory/fixes.md +17 -17
- package/templates/presets/shopify-section.yaml +4 -4
- package/templates/presets/wp-elementor.yaml +4 -4
- package/templates/skills/sb-build-shopify/references/shopify-build-rules.md +2 -2
- package/templates/skills/sb-build-wp/SKILL.md +9 -0
- package/templates/skills/sb-build-wp/references/wp-build-rules.md +44 -2
- package/templates/skills/sb-build-wp/scripts/build-wp.mjs +132 -7
- package/templates/skills/sb-build-wp/scripts/tests/test-build-wp.mjs +174 -0
- package/templates/skills/sb-compare-visual/SKILL.md +8 -8
- package/templates/skills/sb-compare-visual/scripts/compare-visual.mjs +7 -7
- package/templates/skills/sb-compare-visual/scripts/lib/compare-tokens.mjs +1 -1
- package/templates/skills/sb-crawl-and-list/SKILL.md +1 -1
- package/templates/skills/sb-extract-assets/scripts/extract-assets.mjs +50 -2
- package/templates/skills/sb-extract-assets/scripts/tests/test-extract-assets.mjs +107 -1
- package/templates/skills/sb-inspect-live/SKILL.md +30 -2
- package/templates/skills/sb-inspect-live/scripts/inspect-live.mjs +736 -4
- package/templates/skills/sb-inspect-live/scripts/tests/test-inspect-live.mjs +263 -1
- package/templates/skills/sb-review-checks/references/review-rules.md +1 -1
- package/templates/skills/sb-review-checks/scripts/lib/anti-patterns.mjs +8 -2
- package/templates/skills/sb-review-checks/scripts/tests/test-anti-patterns.mjs +23 -0
- package/templates/skills/sb-test-interactivity/SKILL.md +133 -0
- package/templates/skills/sb-test-interactivity/scripts/test-interactivity.mjs +970 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-broken.html +32 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-good.html +47 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/deferred-listeners.html +67 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/details-good.html +25 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/dialog-good.html +38 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/no-interactive.html +15 -0
- package/templates/skills/sb-test-interactivity/scripts/tests/test-test-interactivity.mjs +223 -0
- package/templates/skills/sb-tweak/SKILL.md +6 -6
- package/templates/skills/sb-tweak/references/tweak-patterns.md +4 -4
- package/templates/skills/sb-tweak/scripts/tests/test-element-locator.mjs +1 -1
- package/templates/skills/sb-tweak/scripts/tests/test-intent-parser.mjs +1 -1
- package/templates/skills/sb-tweak/scripts/tests/test-tweak.mjs +2 -2
- package/templates/skills/sb-tweak/scripts/tweak.mjs +10 -10
- package/templates/skills/{sb-validate-render → sb-validate-static-render}/SKILL.md +6 -6
- package/templates/skills/{sb-validate-render/scripts/tests/test-validate-render.mjs → sb-validate-static-render/scripts/tests/test-validate-static-render.mjs} +5 -5
- package/templates/skills/{sb-validate-render/scripts/validate-render.mjs → sb-validate-static-render/scripts/validate-static-render.mjs} +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -31,7 +31,7 @@ Claude Code.
|
|
|
31
31
|
- `sb-build-shopify` — compositor Shopify Section `.liquid` com `{% schema %}`
|
|
32
32
|
editável, settings agrupadas, presets, sem `image_picker default`,
|
|
33
33
|
`{{ block.shopify_attributes }}` em iterações.
|
|
34
|
-
- `sb-validate-render` — render offline via preset YAML, captura screenshot +
|
|
34
|
+
- `sb-validate-static-render` — render offline via preset YAML, captura screenshot +
|
|
35
35
|
probe de tokens; suporta `--assets-map-path` pra Shopify mock context (resolve
|
|
36
36
|
`image_picker` settings via id-keyword × context-substring).
|
|
37
37
|
- `sb-compare-visual` — pixelmatch + token cross-check, scope-aware com
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ determinísticas** atrás de **3 slash commands** que entregam o arquivo pronto:
|
|
|
26
26
|
Pipeline padrão (cada step é uma sub-skill):
|
|
27
27
|
|
|
28
28
|
```
|
|
29
|
-
inspect-live → extract-assets → build-{wp|shopify} → validate-render
|
|
29
|
+
inspect-live → extract-assets → build-{wp|shopify} → validate-static-render
|
|
30
30
|
→ compare-visual → review-checks → (auto-correct loop) → entrega
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -175,7 +175,7 @@ Cada arquivo é machine-parseable (campos
|
|
|
175
175
|
| `sb-extract-assets` | Baixa imagens, strip metadata (EXIF/XMP/IPTC), dedupe content-hash. |
|
|
176
176
|
| `sb-build-wp` | Compõe HTML/Elementor com defensive specificity + a11y/perf. |
|
|
177
177
|
| `sb-build-shopify` | Compõe `.liquid` com `{% schema %}` editável + shims locais. |
|
|
178
|
-
| `sb-validate-render` | Renderiza fragment offline (preset YAML), captura screenshot+tokens.|
|
|
178
|
+
| `sb-validate-static-render` | Renderiza fragment offline (preset YAML), captura screenshot+tokens.|
|
|
179
179
|
| `sb-compare-visual` | pixelmatch + token cross-check, scope-aware crops simétricos. |
|
|
180
180
|
| `sb-review-checks` | Cheerio audit de 14 anti-patterns + 12 design checks (a11y/perf/web).|
|
|
181
181
|
| `sb-tweak` | Edita arquivo entregue via pedido natural PT/EN, atomic-revert. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "similarbuild",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Visual migration framework for Claude Code — clone a live page, get a paste-ready WordPress/Elementor or Shopify section file, validated and auto-corrected.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,9 +13,9 @@ Receive ONE URL and a target (WP or Shopify) and deliver ONE file the user can p
|
|
|
13
13
|
|
|
14
14
|
## The four non-negotiables (inherited from the bootstrap)
|
|
15
15
|
|
|
16
|
-
1. **Mobile-first sempre.** Default viewport `390×844` on every `sb-inspect-live` and `sb-validate-render` call. Override only if the user explicitly demanded desktop.
|
|
16
|
+
1. **Mobile-first sempre.** Default viewport `390×844` on every `sb-inspect-live` and `sb-validate-static-render` call. Override only if the user explicitly demanded desktop.
|
|
17
17
|
2. **Defensive specificity is mandatory.** This is enforced inside `sb-build-wp` / `sb-build-shopify` — your job is to never strip `fixHints` that target it.
|
|
18
|
-
3. **Validate before showing.** NEVER print the build path, contents, or "ready to ship" message to the user before BOTH `sb-validate-render` AND `sb-compare-visual` confirm `diffPercent < threshold` (or, after auto-correction exhausted, you escalate explicitly).
|
|
18
|
+
3. **Validate before showing.** NEVER print the build path, contents, or "ready to ship" message to the user before BOTH `sb-validate-static-render` AND `sb-compare-visual` confirm `diffPercent < threshold` (or, after auto-correction exhausted, you escalate explicitly).
|
|
19
19
|
4. **No fabrication.** If `sb-extract-assets` returns a `failed[]` entry, surface it; never invent a substitute URL. If `sb-inspect-live` returns `widgetBlocked: true`, stop and escalate to user — don't compose from a blocked page.
|
|
20
20
|
|
|
21
21
|
---
|
|
@@ -29,11 +29,12 @@ Receive ONE URL and a target (WP or Shopify) and deliver ONE file the user can p
|
|
|
29
29
|
| `sb-inspect-live` | **Bash** → `node .claude/skills/sb-inspect-live/scripts/inspect-live.mjs ...` | 95%+ deterministic. Stable CLI. LLM adds zero value to the invocation — it just passes args. |
|
|
30
30
|
| `sb-extract-assets` | **Bash** → `node .claude/skills/sb-extract-assets/scripts/extract-assets.mjs ...` | Same. Pure download + sanitize + dedupe. |
|
|
31
31
|
| `sb-build-wp` / `sb-build-shopify` | **Skill** tool → `Skill(skill="sb-build-wp", args="...")` | **The composition is creative**: pick the pattern, name the scope, place defensive specificity. The skill's SKILL.md + `references/wp-build-rules.md` are the LLM's manual. Bash can't compose. |
|
|
32
|
-
| `sb-validate-render` | **Bash** → `node .claude/skills/sb-validate-render/scripts/validate-render.mjs ...` | Headless render + token probe. Fully scripted. |
|
|
32
|
+
| `sb-validate-static-render` | **Bash** → `node .claude/skills/sb-validate-static-render/scripts/validate-static-render.mjs ...` | Headless render + token probe. Fully scripted. |
|
|
33
|
+
| `sb-test-interactivity` | **Bash** → `node .claude/skills/sb-test-interactivity/scripts/test-interactivity.mjs ...` | Headless behavioral probe (aria-controls / details / dialog). Diagnostic gate — `passed:false` becomes a warning, not a block. |
|
|
33
34
|
| `sb-compare-visual` | **Bash** → `node .claude/skills/sb-compare-visual/scripts/compare-visual.mjs ...` | pixelmatch + token diff. Pure determinism. |
|
|
34
35
|
| `sb-review-checks` | **Bash** → `node .claude/skills/sb-review-checks/scripts/review-checks.mjs ...` | cheerio + regex. Pure determinism. Its `candidateFix` strings are the contract — you forward them verbatim, never reformat. |
|
|
35
36
|
|
|
36
|
-
**Why not all-Skill:** the deterministic five would burden the orchestrator with their SKILL.md instructions on every loop iteration (the auto-correct loop calls `validate-render` + `compare-visual` + `review-checks` up to N+1 times). Bash keeps each call to one CLI invocation and one JSON parse.
|
|
37
|
+
**Why not all-Skill:** the deterministic five would burden the orchestrator with their SKILL.md instructions on every loop iteration (the auto-correct loop calls `validate-static-render` + `compare-visual` + `review-checks` up to N+1 times). Bash keeps each call to one CLI invocation and one JSON parse.
|
|
37
38
|
|
|
38
39
|
**Why not all-Bash:** `sb-build-wp` has no compose CLI — its `build-wp.mjs` only handles `validate` and `write` subcommands. The actual composition is the LLM reading the skill's references and producing HTML. That work has to happen via Skill.
|
|
39
40
|
|
|
@@ -100,11 +101,11 @@ If the user passes any other flag, ignore it and continue (don't error on unknow
|
|
|
100
101
|
3. **Project slug.**
|
|
101
102
|
- If `--project-name <slug>` is given: use it verbatim (validate it's URL-safe; warn if not).
|
|
102
103
|
- Else: derive from URL hostname.
|
|
103
|
-
- Parse hostname (e.g. `https://www.
|
|
104
|
+
- Parse hostname (e.g. `https://www.example-store.com/path?q=x` → `www.example-store.com`).
|
|
104
105
|
- Strip `www.`, strip leading subdomain only when it's `www`/`m`/`store`/`shop`.
|
|
105
106
|
- Strip TLD (last `.xxx` or `.xxx.yy` for known compound TLDs like `.com.br`, `.co.uk`).
|
|
106
107
|
- Lowercase, replace any non-`[a-z0-9]` with `-`, collapse repeats, trim leading/trailing `-`.
|
|
107
|
-
- Examples: `https://
|
|
108
|
+
- Examples: `https://example-store.com` → `example-store`; `https://www.lojaexemplo.com.br/products/x` → `lojaexemplo`.
|
|
108
109
|
- Hold as `{project-slug}` for the rest of the run.
|
|
109
110
|
|
|
110
111
|
4. **Project structure.** Compute `{project-root}/{output_folder}/{project-slug}/` and ensure these subdirs exist (mkdir -p, idempotent):
|
|
@@ -202,7 +203,7 @@ Skill(
|
|
|
202
203
|
|
|
203
204
|
If you have a `{designKnowledgeSubset}` or `{patternsSubset}` to pass, include them as additional `args` keys (`design-knowledge-inline=<base64-or-path>`); otherwise omit and let the skill use its bundled defaults.
|
|
204
205
|
|
|
205
|
-
**The skill returns:** absolute path to the written file, validator's report (errors/warnings), and a one-line note on the chosen pattern. Do not request the file's contents back — you'll let `sb-validate-render` open it.
|
|
206
|
+
**The skill returns:** absolute path to the written file, validator's report (errors/warnings), and a one-line note on the chosen pattern. Do not request the file's contents back — you'll let `sb-validate-static-render` open it.
|
|
206
207
|
|
|
207
208
|
If the skill's validator returns errors and the skill couldn't fix them, surface to the user; this is a builder-side defect, not an auto-correct case.
|
|
208
209
|
|
|
@@ -211,7 +212,7 @@ If the skill's validator returns errors and the skill couldn't fix them, surface
|
|
|
211
212
|
## Step 4 — Validate render (Bash)
|
|
212
213
|
|
|
213
214
|
```bash
|
|
214
|
-
node .claude/skills/sb-validate-render/scripts/validate-render.mjs \
|
|
215
|
+
node .claude/skills/sb-validate-static-render/scripts/validate-static-render.mjs \
|
|
215
216
|
--file "{output-path}" \
|
|
216
217
|
--preset "{preset}" \
|
|
217
218
|
--output-dir "{output_folder}/{project-slug}/reports/validations/{slug}-{iteration}" \
|
|
@@ -227,7 +228,7 @@ Where `{preset}` is `wp-elementor` or `shopify-section`, `{slug}` is the section
|
|
|
227
228
|
`image_picker` setting that the builder emitted *without a `default`* (per
|
|
228
229
|
anti-pattern #12) renders as the `{% else %}` placeholder — a neutral grey
|
|
229
230
|
fallback — which inflates `compare-visual` diff% by ~30pp against the live
|
|
230
|
-
photo with no actionable fix. With it, `validate-render` mocks the
|
|
231
|
+
photo with no actionable fix. With it, `validate-static-render` mocks the
|
|
231
232
|
image_picker context from matching assetsMap entries (id-keyword → context-
|
|
232
233
|
substring heuristic) so the rendered fragment shows real imagery.
|
|
233
234
|
|
|
@@ -246,6 +247,31 @@ Capture stdout JSON → `render`. Hold `{render-screenshot}` (`render.screenshot
|
|
|
246
247
|
|
|
247
248
|
---
|
|
248
249
|
|
|
250
|
+
## Step 4-bis — Test interactivity (Bash, diagnostic gate)
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
node .claude/skills/sb-test-interactivity/scripts/test-interactivity.mjs \
|
|
254
|
+
--file "{output-path}" \
|
|
255
|
+
--preset "{preset}" \
|
|
256
|
+
--output-dir "{output_folder}/{project-slug}/reports/validations/{slug}-{iteration}"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Capture `interactivity-report.json` (same dir as `validate-static-render`). Hold the parsed report.
|
|
260
|
+
|
|
261
|
+
**Diagnostic mode (NOT a blocker):**
|
|
262
|
+
|
|
263
|
+
- Exit `!= 0` → log stderr verbatim and continue to Step 5 with no warning. Common hint: `playwright`/chromium missing — suggest `npx playwright install chromium`. Don't escalate.
|
|
264
|
+
- Exit `0` AND `passed === true` → silently proceed. No warning, no status change.
|
|
265
|
+
- Exit `0` AND `passed === false`:
|
|
266
|
+
- **Append** to `interactivityWarnings` array (NOT single-shot overwrite): `interactivityWarnings.push({ iteration: <n>, tests: report.tests.filter(t => !t.passed) })`. Iterations with 0 warnings do NOT append. Cross-iter history preserved — useful for the success/escalation path (Steps 8/10) which renders a section in `report.html` summarizing which iter saw which break.
|
|
267
|
+
- Status badge on the build's report ganha sufixo `+!interactive` (e.g. `✅+!interactive`). Calculated dynamically: applies if `interactivityWarnings.length > 0` em qualquer iter.
|
|
268
|
+
- **Final status remains whatever the decision matrix decides** (Step 7). No promotion to ❌. The skill is diagnostic, not a gatekeeper — policy locked in G2 spec change log. A user who wants hard blocking can re-run with the warning visible and choose to discard the build.
|
|
269
|
+
- Continue to Step 5.
|
|
270
|
+
|
|
271
|
+
**Why diagnostic and not blocker:** `sb-test-interactivity` is new (G2). False-positives during early adoption are expected (web components outside the canonical whitelist, drawers with atypical transitions). Promoting to hard gate before maturity would block good builds. Explicit warning + `interactivityWarnings` in the report gives visibility without blocking. Promotion to blocker is a separate spec decision (G8+).
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
249
275
|
## Step 5 — Compare visual (Bash)
|
|
250
276
|
|
|
251
277
|
```bash
|
|
@@ -283,7 +309,7 @@ drift. Crop both sides to their measured bboxes whenever possible.
|
|
|
283
309
|
of points with no actual visual drift.
|
|
284
310
|
|
|
285
311
|
Both DPR flags default to `3` (matches `sb-inspect-live`'s iPhone 14 profile and
|
|
286
|
-
`sb-validate-render`'s aligned `deviceScaleFactor=3`, Pattern #22). Don't override
|
|
312
|
+
`sb-validate-static-render`'s aligned `deviceScaleFactor=3`, Pattern #22). Don't override
|
|
287
313
|
unless one of those skills was invoked at a different DPR.
|
|
288
314
|
|
|
289
315
|
Capture stdout JSON → `compare`. Note the path to the persisted `report.json` → `{compare-report-path}`.
|
|
@@ -322,6 +348,39 @@ Capture stdout JSON → `review`. Note `review.passed` and the `violations[]` ar
|
|
|
322
348
|
|
|
323
349
|
## Step 7 — Decision matrix (Pattern #28)
|
|
324
350
|
|
|
351
|
+
**Pre-check 0 (coverage gate, runs FIRST):**
|
|
352
|
+
|
|
353
|
+
Calcule contra schema **real** do `sb-inspect-live`:
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
buildHeight = render.geometry.totalHeight # primary: from sb-validate-static-render
|
|
357
|
+
liveMainHeight = inspection.dom[0].bbox.h # primary: root walked node bbox (body/main)
|
|
358
|
+
|| (walk inspection.dom recursively, collect classified-sections bboxes,
|
|
359
|
+
return max(s.bbox.y + s.bbox.h) - min(s.bbox.y))
|
|
360
|
+
# fallback: span das classified sections (warn)
|
|
361
|
+
coverageRatio = buildHeight / liveMainHeight
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`inspection.dom` is **array of root nodes** (schema in `.claude/skills/sb-inspect-live/scripts/inspect-live.mjs:165,1221`). Each node has `bbox: {x, y, w, h}` and optional `sectionType` for classified sections.
|
|
365
|
+
|
|
366
|
+
If `inspection.dom[]` is empty AND no classified sections collectable → log warn `[build-page] WARN: coverage gate skipped — empty inspection.dom`. Skip coverage gate; proceed to existing matrix. Persist `coverage = null`.
|
|
367
|
+
|
|
368
|
+
If `buildHeight` is missing (defeito do `sb-validate-static-render`) → HALT with clear error `[build-page] FATAL: render.geometry.totalHeight missing`. Don't try to recover.
|
|
369
|
+
|
|
370
|
+
If `liveMainHeight < 100` (degenerate) → log warn `[build-page] WARN: liveMainHeight < 100, coverage gate skipped`; skip; persist `coverage.skippedReason = 'degenerate-height'`.
|
|
371
|
+
|
|
372
|
+
**Tier triage:**
|
|
373
|
+
|
|
374
|
+
| Range | Status | Action |
|
|
375
|
+
| --- | --- | --- |
|
|
376
|
+
| `< 0.40` | ❌ **incomplete** | Status: `"incomplete — built {ratio*100:.0f}% of source"`. Goes to existing Step 9 (auto-correct loop) if budget remains; existing `review.violations[]` driven fixHints feed back to composer. **NO new EXTENSION marker** — Step 9 already handles auto-correct via review-derived fixHints. Coverage `< 0.40` overrides matrix to ❌ (without it, matrix could route to ⚠️ and miss the issue). |
|
|
377
|
+
| `0.40 — 0.85` | ⚠️ **partial** | Status: `"partial — built {ratio*100:.0f}%"`. Proceeds to matrix below (does NOT bypass). Coverage warning rides on top of whatever the matrix decides. |
|
|
378
|
+
| `>= 0.85` | (proceed) | Coverage OK. Matrix below runs silently. |
|
|
379
|
+
|
|
380
|
+
Persist `coverage = { buildHeight, liveMainHeight, ratio, source: 'rootBbox'|'sectionsSpan', missingSections?: [...], skippedReason?: <string> }` in the success/escalation report. `missingSections` (when `ratio < 0.85`): walk `inspection.dom` recursively, collect classified-section nodes whose `bbox.y >= buildHeight`.
|
|
381
|
+
|
|
382
|
+
**Existing matrix (runs only when coverage gate didn't route to ❌):**
|
|
383
|
+
|
|
325
384
|
You hold both `compare.passed` and `review.passed` now. Apply this matrix:
|
|
326
385
|
|
|
327
386
|
| `review.passed` | `compare.passed` | Action |
|
|
@@ -366,7 +425,7 @@ You only reach here when `review.passed === true` AND `compare.passed === true`.
|
|
|
366
425
|
|
|
367
426
|
## Step 9 — Auto-correct loop body
|
|
368
427
|
|
|
369
|
-
You only reach here from Step 7's matrix when **both** `review.passed === false` AND `compare.passed === false`. The pre-checks (no-auto-correct, budget exhausted) and the validate-render hard-failure (`tiny-screenshot`) all reroute to Step 10.
|
|
428
|
+
You only reach here from Step 7's matrix when **both** `review.passed === false` AND `compare.passed === false`. The pre-checks (no-auto-correct, budget exhausted) and the validate-static-render hard-failure (`tiny-screenshot`) all reroute to Step 10.
|
|
370
429
|
|
|
371
430
|
`review.violations[]` is in hand from Step 6 — fixHints to feed back into the builder.
|
|
372
431
|
|
|
@@ -446,10 +505,10 @@ You only reach here when the budget is spent. Be honest:
|
|
|
446
505
|
|
|
447
506
|
## Output structure (recap, for sanity)
|
|
448
507
|
|
|
449
|
-
After a successful run on `https://
|
|
508
|
+
After a successful run on `https://example-store.com`:
|
|
450
509
|
|
|
451
510
|
```
|
|
452
|
-
{output_folder}/
|
|
511
|
+
{output_folder}/example-store/
|
|
453
512
|
├── clean/
|
|
454
513
|
│ └── home/hero.html ← the deliverable
|
|
455
514
|
├── assets/
|
|
@@ -484,7 +543,7 @@ Never write outside `{project-slug}/`. Cross-project assets sharing happens only
|
|
|
484
543
|
When this command is first wired up, the canonical end-to-end smoke test is:
|
|
485
544
|
|
|
486
545
|
```
|
|
487
|
-
/build-page https://
|
|
546
|
+
/build-page https://example-store.com
|
|
488
547
|
```
|
|
489
548
|
|
|
490
549
|
Expected: a clean `home/hero.html` (or the home's first detected section), under-threshold diff, report.html opening successfully. That's the MVP milestone.
|
|
@@ -13,9 +13,9 @@ Receber UMA URL raiz e um target (WP ou Shopify), descobrir todas as páginas re
|
|
|
13
13
|
|
|
14
14
|
## The five non-negotiables
|
|
15
15
|
|
|
16
|
-
1. **Mobile-first sempre.** Default viewport `390×844` em toda chamada `sb-inspect-live` e `sb-validate-render`. Override só se o usuário pedir desktop explicitamente.
|
|
16
|
+
1. **Mobile-first sempre.** Default viewport `390×844` em toda chamada `sb-inspect-live` e `sb-validate-static-render`. Override só se o usuário pedir desktop explicitamente.
|
|
17
17
|
2. **Defensive specificity é mandatório.** Aplicado dentro de `sb-build-wp`/`sb-build-shopify` — seu trabalho é nunca comer `fixHints` que cubram isso.
|
|
18
|
-
3. **Validate before showing.** NUNCA imprima paths de build, conteúdo do HTML, ou mensagem "ready to ship" antes de `sb-validate-render` E `sb-compare-visual` confirmarem `diffPercent < threshold` por página (ou, após o auto-correct esgotar, marque a página como ⚠️ explicitamente). Isso vale por página — uma falha em `pdp/foo.html` não bloqueia a entrega de `home/hero.html`, mas cada página ainda passa pela porta antes de virar ✅.
|
|
18
|
+
3. **Validate before showing.** NUNCA imprima paths de build, conteúdo do HTML, ou mensagem "ready to ship" antes de `sb-validate-static-render` E `sb-compare-visual` confirmarem `diffPercent < threshold` por página (ou, após o auto-correct esgotar, marque a página como ⚠️ explicitamente). Isso vale por página — uma falha em `pdp/foo.html` não bloqueia a entrega de `home/hero.html`, mas cada página ainda passa pela porta antes de virar ✅.
|
|
19
19
|
4. **No fabrication.** `assetsMap.failed[]` é forwardado pro builder — nunca invente URL substituta. `inspection.widgetBlocked: true` em uma página → marque essa página como ⚠️ e siga o batch; em UMA página inicial bloqueada (a raiz), discuta com o usuário antes de prosseguir.
|
|
20
20
|
5. **Único checkpoint humano = confirmar a lista de páginas.** Esta é a regra inegociável que distingue `/build-site` de `/build-page`. NÃO faça outras perguntas no meio do batch. Auto-correção é loop fechado, por página, sem interromper o usuário. A única outra interação opcional é o auto-learn batched no final.
|
|
21
21
|
|
|
@@ -31,7 +31,8 @@ Receber UMA URL raiz e um target (WP ou Shopify), descobrir todas as páginas re
|
|
|
31
31
|
| `sb-inspect-live` | **Bash** | 95%+ deterministic. Stable CLI. Roda 1× por página. |
|
|
32
32
|
| `sb-extract-assets` | **Bash** | Pure download + sanitize + dedupe. **`--existing-assets-dir` apontado para `{output_folder}/{project-slug}/assets/` em todas as páginas do batch** — dedupe automático cross-page por content-hash. |
|
|
33
33
|
| `sb-build-wp` / `sb-build-shopify` | **Skill** tool | A composição é criativa. Roda 1× por página (mais até N retries por página). |
|
|
34
|
-
| `sb-validate-render` | **Bash** | Headless render + token probe. Pure scripted. |
|
|
34
|
+
| `sb-validate-static-render` | **Bash** | Headless render + token probe. Pure scripted. |
|
|
35
|
+
| `sb-test-interactivity` | **Bash** | Headless behavioral probe (aria-controls / details / dialog). Diagnostic gate — `passed:false` vira warning em `pageResults[]`, não bloqueia. |
|
|
35
36
|
| `sb-compare-visual` | **Bash** | pixelmatch + token diff. Pure determinismo. |
|
|
36
37
|
| `sb-review-checks` | **Bash** | cheerio + regex. Pure determinismo. `candidateFix` strings são contrato — forward verbatim. |
|
|
37
38
|
|
|
@@ -66,6 +67,7 @@ O texto após `ARGUMENTS:` contém a entrada do usuário. Extraia:
|
|
|
66
67
|
| `--no-auto-correct` | no | false | Em cada página, escala no primeiro decision-matrix que pediria loop. |
|
|
67
68
|
| `--max-pages <n>` | no | (deixa o default da skill, `200`) | Repassa pra `sb-crawl-and-list` como hard cap. |
|
|
68
69
|
| `--sitemap-path <path>` | no | (none) | Repassa pra `sb-crawl-and-list`. Útil pra SPAs ou sites que bloqueiam crawler. |
|
|
70
|
+
| `--no-globals` | no | false | Opt-out explícito do Step 4d (globals auto-extract). Sem essa flag, header/footer compartilhados são extraídos pra `clean/global/` quando `crawl.pageCount >= 3` (default ON). Use quando o site não tem chrome compartilhado entre páginas, ou quando estiver fazendo um run de debug que precisa ver o markup completo por página. |
|
|
69
71
|
| `--dry-run` | no | false | Roda crawl + checkpoint + plan-summary, **não builda**. Imprime o plano (URLs, types, assets estimados) e sai. |
|
|
70
72
|
|
|
71
73
|
Flag desconhecida → ignore com warning de uma linha. Não erre.
|
|
@@ -289,11 +291,50 @@ node .claude/skills/sb-extract-assets/scripts/extract-assets.mjs \
|
|
|
289
291
|
|
|
290
292
|
`assetsMap.failed[]` non-empty → forwarde pro builder, **não** pare a página.
|
|
291
293
|
|
|
292
|
-
### Step 4d —
|
|
294
|
+
### Step 4d — Extract global sections (header/footer compartilhados)
|
|
293
295
|
|
|
294
|
-
|
|
296
|
+
**Default ON** quando `crawl.pageCount >= 3` AND a flag `--no-globals` NÃO foi passada. Caso contrário, skip este Step inteiro com uma linha em `decisions.md` (`Step 4d skipped: pageCount<3` OU `Step 4d skipped: --no-globals opt-out`).
|
|
295
297
|
|
|
296
|
-
|
|
298
|
+
Estado mantido pelo orchestrator durante o batch:
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
globalsExtracted = {
|
|
302
|
+
header: null | <output-path>, // ex: "clean/global/header.html"
|
|
303
|
+
footer: null | <output-path>,
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Detector estrutural** (sem hash, sem regex sobre HTML cru):
|
|
308
|
+
|
|
309
|
+
- **Header** = primeiro elemento da `inspection.dom` (descida em DFS) cujo `tag === 'header'` E que tenha algum descendant cujo `tag === 'nav'`. Se ausente: **skip globals (não extrai nem strippa) + log warning** em `decisions.md`: `[build-site] WARN: Step 4d header detector missed — no <header> semântico com <nav>; globals not extracted, pages keep chrome inline`. Continue pipeline normalmente. **NÃO pergunte ao humano** — non-negotiable #5 (`single human checkpoint = page list confirmation`) proíbe queries mid-batch. Decisão de fix manual fica pra próximo run com `--no-globals` ou refresh do crawl.
|
|
310
|
+
|
|
311
|
+
- **Footer** = último elemento que é **descendant direto-de-body-ou-main** (i.e., `parent.tag === 'body'` OU `parent.tag === 'main'`) cujo `tag === 'footer'` OU cujo atributo `role === 'contentinfo'`. **NÃO** o último em DFS pós-ordem — isso pegava blog-post `<footer>` ou article `<footer>` em vez do site footer (bug example-shop-class em sites de conteúdo). Se ausente: skip + warn (mesmo padrão do header acima).
|
|
312
|
+
|
|
313
|
+
**Trigger window:** Step 4d roda apenas quando `globalsExtracted.header === null` (e separadamente para footer). Ou seja, só na primeira inspection que casar o detector. Inspeções subsequentes não re-processam — o asset já está em disco e é referenciado.
|
|
314
|
+
|
|
315
|
+
**Extração:**
|
|
316
|
+
1. Localize header e/ou footer no `inspection.dom` (em memória).
|
|
317
|
+
2. Serialize cada um pro fragment HTML/Liquid passando pelo composer (`sb-build-wp` ou `sb-build-shopify`) com hint `--target-section=header|footer` (composer trata como input de seção, não página inteira). Output: `clean/global/{header,footer}.{html|liquid}`.
|
|
318
|
+
3. Set `globalsExtracted.header` (e/ou `.footer`) = output path.
|
|
319
|
+
|
|
320
|
+
**Stripper (mandatório quando globals extracted):** ANTES de invocar o composer pra páginas individuais (Step 4e):
|
|
321
|
+
|
|
322
|
+
1. Clone a `inspection` em memória.
|
|
323
|
+
2. Em `inspection.dom`, **remova** as subtrees do `<header>` semântico (se `globalsExtracted.header !== null`) e do `<footer>`/`[role=contentinfo]` (se `globalsExtracted.footer !== null`).
|
|
324
|
+
3. Passe a inspection editada ao composer. Composer não vê header/footer e não pode fabricá-los.
|
|
325
|
+
4. Após o composer retornar, **prepende** ao output uma linha de comentário:
|
|
326
|
+
- HTML: `<!-- sb-build-site: header in clean/global/header.html -->\n` (se header extracted)
|
|
327
|
+
- HTML: `<!-- sb-build-site: footer in clean/global/footer.html -->` (se footer extracted, no fim)
|
|
328
|
+
- Liquid: idem com `{% comment %} ... {% endcomment %}`
|
|
329
|
+
5. Grava em `clean/{home,pdp,...}/{slug}.html` o output já strippado-e-comentado.
|
|
330
|
+
|
|
331
|
+
**Stripper validation:** após gravar o file, faça um grep rápido:
|
|
332
|
+
```
|
|
333
|
+
grep -nE "<header[ >]|<footer[ >]" clean/{home,pdp,...}/{slug}.{html,liquid}
|
|
334
|
+
```
|
|
335
|
+
Padrão sem `^` anchor pra pegar pretty-printed (indented ` <header>`). Glob `{html,liquid}` cobre ambos os targets (WP + Shopify). Se retornar match, é defeito — composer ignorou a inspection editada e re-fabricou, ou stripper passou tag misformada. Log `[build-site] WARN: stripper miss in {slug}.{ext}` em `decisions.md`. Não blocking — orchestrator continua.
|
|
336
|
+
|
|
337
|
+
**Por que detector estrutural e não hash:** hash exige inspection emitir hashes comparáveis. Detector estrutural usa só tags semânticas que `sb-inspect-live` já captura na DOM tree. Funciona com qualquer inspection que tenha um DOM serializado, sem mudança upstream. Cobre o caso comum (sites WordPress/Shopify usam `<header>`/`<footer>` semântico). Para sites com chrome em `<div class="site-header">` sem semântica, o Ask First gate força decisão consciente em vez de heurística silenciosa.
|
|
297
338
|
|
|
298
339
|
### Step 4e — Build (Skill)
|
|
299
340
|
|
|
@@ -313,7 +354,7 @@ Se o validator interno do builder retornar erros que ele não conseguiu corrigir
|
|
|
313
354
|
### Step 4f — Validate render (Bash)
|
|
314
355
|
|
|
315
356
|
```bash
|
|
316
|
-
node .claude/skills/sb-validate-render/scripts/validate-render.mjs \
|
|
357
|
+
node .claude/skills/sb-validate-static-render/scripts/validate-static-render.mjs \
|
|
317
358
|
--file "{output-path}" \
|
|
318
359
|
--preset "{preset}" \
|
|
319
360
|
--output-dir "{output_folder}/{project-slug}/reports/validations/{slug}-{iteration}" \
|
|
@@ -322,10 +363,33 @@ node .claude/skills/sb-validate-render/scripts/validate-render.mjs \
|
|
|
322
363
|
[--assets-map-path "{assets-map-path}"]
|
|
323
364
|
```
|
|
324
365
|
|
|
325
|
-
**`--assets-map-path` (Pattern #32, Shopify-only).** Quando `target === shopify` e Step 4c produziu um `assetsMap`, passe `--assets-map-path "{assets-map-path}"`. Sem isso, `image_picker` settings sem `default` (anti-pattern #12) renderizam o placeholder `{% else %}` no validate, inflando o diff visual em ~30pp contra a foto real da live por motivo não-acionável. Com a flag, `validate-render` popula o mock context pelo match id-keyword → context-substring no assetsMap. Pra `target === wp`, não passe — não há `image_picker` no preset WP.
|
|
366
|
+
**`--assets-map-path` (Pattern #32, Shopify-only).** Quando `target === shopify` e Step 4c produziu um `assetsMap`, passe `--assets-map-path "{assets-map-path}"`. Sem isso, `image_picker` settings sem `default` (anti-pattern #12) renderizam o placeholder `{% else %}` no validate, inflando o diff visual em ~30pp contra a foto real da live por motivo não-acionável. Com a flag, `validate-static-render` popula o mock context pelo match id-keyword → context-substring no assetsMap. Pra `target === wp`, não passe — não há `image_picker` no preset WP.
|
|
326
367
|
|
|
327
368
|
Branches idênticas ao `/build-page` (Step 4): `tiny-screenshot` → hard fail desta iteração; `viewportOverflow: true` → flag pra comparator + suprime `--crop-build-bbox` no Step 4g.
|
|
328
369
|
|
|
370
|
+
### Step 4f-bis — Test interactivity (Bash, diagnostic gate)
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
node .claude/skills/sb-test-interactivity/scripts/test-interactivity.mjs \
|
|
374
|
+
--file "{output-path}" \
|
|
375
|
+
--preset "{preset}" \
|
|
376
|
+
--output-dir "{output_folder}/{project-slug}/reports/validations/{slug}-{iteration}"
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Capture `interactivity-report.json` (mesmo dir do `validate-static-render`).
|
|
380
|
+
|
|
381
|
+
**Modo diagnóstico (não-blocker):**
|
|
382
|
+
|
|
383
|
+
- Exit `!= 0` → log stderr verbatim em `pageResults[].errors`. Skipa o gate pra essa iteração. Continua pipeline (Step 4g) normalmente. Hint comum: `playwright`/chromium não instalado — sugira `npx playwright install chromium`.
|
|
384
|
+
- Exit `0` AND `passed === true` → silently proceed. Sem warning, sem mudança de status.
|
|
385
|
+
- Exit `0` AND `passed === false`:
|
|
386
|
+
- `pageResults[].interactivityWarnings` é **array de iteration entries** (NÃO single-shot overwrite). A cada iteration que detecta warnings, **append**: `{ iteration: <n>, tests: report.tests.filter(t => !t.passed) }`. Iterations com 0 warnings NÃO appende nada (não polui o array). Esta estrutura preserva histórico cross-iter — útil pro Step 6 auto-learn cross-page repetition (Cap D), que precisa enxergar TODOS os warnings observados em qualquer iter pra detectar pattern repetido.
|
|
387
|
+
- Status badge no console output ganha sufixo `+!interactive` (ex: `✅+!interactive`, `⚠️+!interactive`). Sufix calculado dinamicamente: applies se `pageResults[i].interactivityWarnings.length > 0` em qualquer iter (mesmo que iter atual seja clean).
|
|
388
|
+
- **Status final permanece o que a decision matrix decidir** (Step 4i). Sem promoção pra ❌. Diagnostic gate, não gatekeeper — política definida em G2 spec change log.
|
|
389
|
+
- Continua pipeline (Step 4g).
|
|
390
|
+
|
|
391
|
+
**Por que diagnostic e não blocker:** `sb-test-interactivity` é skill nova ainda em early-adoption. False-positives nesta janela são esperados (web components fora da whitelist canônica, drawers com transições atípicas). Promover a hard gate antes da skill amadurecer geraria fricção em builds bons. Warning explícito + `interactivityWarnings` em `pageResults[]` dá visibilidade ao usuário sem bloquear. Mudança pra blocker é decisão de spec própria (G8+).
|
|
392
|
+
|
|
329
393
|
### Step 4g — Compare visual (Bash)
|
|
330
394
|
|
|
331
395
|
```bash
|
|
@@ -356,6 +420,41 @@ Capture `review`.
|
|
|
356
420
|
|
|
357
421
|
### Step 4i — Decision matrix (Pattern #28)
|
|
358
422
|
|
|
423
|
+
**Pre-check 0 (coverage gate, runs FIRST, before the matrix):**
|
|
424
|
+
|
|
425
|
+
Calcule contra schema **real** do `sb-inspect-live` (não invenção):
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
buildHeight = render.geometry.totalHeight # source primário do sb-validate-static-render
|
|
429
|
+
liveMainHeight = inspection.dom[0].bbox.h # primary: root walked node bbox (body/main)
|
|
430
|
+
|| (walk inspection.dom recursively, collect classified-sections bboxes,
|
|
431
|
+
return max(s.bbox.y + s.bbox.h) - min(s.bbox.y))
|
|
432
|
+
# fallback: span das classified sections (warn)
|
|
433
|
+
coverageRatio = buildHeight / liveMainHeight
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Onde `inspection.dom` é **array de root nodes** (não um object com `.mainBbox`/`.sections` — schema confirmado em `.claude/skills/sb-inspect-live/scripts/inspect-live.mjs:165,1221`). Cada node tem `bbox: {x, y, w, h}` e opcional `sectionType` (presente em sections classificadas durante walk).
|
|
437
|
+
|
|
438
|
+
Se `inspection.dom[]` estiver vazio (page bloqueada, cross-origin only, etc.) E sem classified sections coletáveis → log warn `[build-site] WARN: coverage gate skipped — empty inspection.dom`. Skip coverage gate; prossegue pra matrix com `pageResults[].coverage = null`.
|
|
439
|
+
|
|
440
|
+
Se `buildHeight` está ausente (defeito do `sb-validate-static-render`) → HALT com erro claro `[build-site] FATAL: render.geometry.totalHeight missing — validate-static-render defect`. Não tente recuperar.
|
|
441
|
+
|
|
442
|
+
Se `liveMainHeight < 100` (degenerate: inspection com altura sub-viewport, geralmente erro upstream) → log warn `[build-site] WARN: liveMainHeight < 100, coverage gate skipped`; skip; prossegue pra matrix com `pageResults[].coverage.skippedReason = 'degenerate-height'`.
|
|
443
|
+
|
|
444
|
+
**Tier triage:**
|
|
445
|
+
|
|
446
|
+
| Range | Status | Ação |
|
|
447
|
+
| --- | --- | --- |
|
|
448
|
+
| `< 0.40` | ❌ **incomplete** | `pageResults[].status = ❌`; mensagem: `"incomplete — built {ratio*100:.0f}% of source"`. Vai direto pra Step 4j (auto-correct loop) se budget permite, OR Step 4k (escalation) se exhausted. **NÃO** dispara EXTENSION mode novo: o auto-correct existente já consome `review.violations[]` como `fixHints`. Coverage `< 0.40` apenas força o status ❌ no matrix override (em vez de deixar a matrix decidir) — o composer continua recebendo o feedback canonical via review-checks. |
|
|
449
|
+
| `0.40 — 0.85` | ⚠️ **partial** | `pageResults[].status = ⚠️` com nota: `"partial — built {ratio*100:.0f}%"`. Prossegue pra matrix abaixo (não bypassa). Coverage warning sobrevive como overlay no status final. |
|
|
450
|
+
| `>= 0.85` | (proceed) | Coverage OK. Prossegue pra matrix abaixo silenciosamente. |
|
|
451
|
+
|
|
452
|
+
Anote em `pageResults[].coverage = { buildHeight, liveMainHeight, ratio, source: 'rootBbox'|'sectionsSpan', missingSections?: [...], skippedReason?: <string> }`.
|
|
453
|
+
|
|
454
|
+
`missingSections` é derivado quando `ratio < 0.85`: walk `inspection.dom` recursivamente, coletar nodes com `sectionType` cujo `bbox.y >= buildHeight` (sections que existem na live mas ficaram além do build). Útil pro auto-correct hint.
|
|
455
|
+
|
|
456
|
+
**Decision matrix existente (roda só se coverage gate não escalou pra ❌):**
|
|
457
|
+
|
|
359
458
|
| `review.passed` | `compare.passed` | Ação na página |
|
|
360
459
|
| --- | --- | --- |
|
|
361
460
|
| true | true | ✅ **Página pronta.** Atualiza `pageResults[]` com sucesso, próxima página. |
|
|
@@ -363,7 +462,7 @@ Capture `review`.
|
|
|
363
462
|
| true | false | ⚠️ **Visual drift sem fixHints.** `pageResults[].status = ⚠️` com top-5 `structuredDiffs`. Próxima página. |
|
|
364
463
|
| false | false | 🔄 **Auto-correct loop.** Vai pra Step 4j. |
|
|
365
464
|
|
|
366
|
-
Pré-checks (idênticos ao `/build-page`):
|
|
465
|
+
Pré-checks adicionais (idênticos ao `/build-page`):
|
|
367
466
|
1. `--no-auto-correct` foi passado → escala primeiro diff de cada página: rotas que iriam pra Step 4j viram ⚠️ direto.
|
|
368
467
|
2. `iteration >= auto_correct_max_iterations` (default 2) → idem, vira ⚠️.
|
|
369
468
|
3. **Mesmo `violations[]` 2 iterações seguidas** → fixHint não pegou. Não rode 3ª. ⚠️ imediato.
|
|
@@ -383,7 +482,7 @@ Skill(
|
|
|
383
482
|
|
|
384
483
|
### Step 4k — Append to pageResults
|
|
385
484
|
|
|
386
|
-
Após cada página, append `{url, type, slug, status, diffPercent, iterations, outputPath, screenshotsLive, screenshotsBuild, diffMap, violations}` ao `pageResults[]`. Imprima a linha de status no console (formato no início do Step 4).
|
|
485
|
+
Após cada página, append `{url, type, slug, status, diffPercent, iterations, outputPath, screenshotsLive, screenshotsBuild, diffMap, violations, coverage, interactivityWarnings}` ao `pageResults[]`. Imprima a linha de status no console (formato no início do Step 4). Se `interactivityWarnings.length > 0`, sufixa o status badge com `+!interactive`. Se `coverage.ratio < 0.85`, anota `(coverage {ratio*100:.0f}%)` na linha.
|
|
387
486
|
|
|
388
487
|
---
|
|
389
488
|
|
|
@@ -393,9 +492,11 @@ Depois que TODAS as páginas confirmadas foram processadas (`pageResults[].lengt
|
|
|
393
492
|
|
|
394
493
|
1. **Escreva `{output_folder}/{project-slug}/reports/index.html`** com:
|
|
395
494
|
- Header com root URL, target, project-slug, timestamp do run, totals (`X✅ / Y⚠️ / Z❌`).
|
|
396
|
-
- Tabela com uma linha por página: status badge, type, URL → output path link, diff %, iterations, screenshot side-by-side (live + build, thumbs com link pro full), link pro diff map, violations resumo.
|
|
495
|
+
- Tabela com uma linha por página: status badge (com sufixos `+!interactive` quando aplicável), type, URL → output path link, diff %, **coverage %**, iterations, screenshot side-by-side (live + build, thumbs com link pro full), link pro diff map, violations resumo, link pro `interactivity-report.json` quando `interactivityWarnings.length > 0`.
|
|
397
496
|
- Section "Auto-correct details" listando páginas com iteration > 0 e o que mudou.
|
|
398
497
|
- Section "Escalations" com páginas ⚠️/❌ — top diffs visuais e candidate fixes inline.
|
|
498
|
+
- Section "Interactivity warnings" listando páginas com `interactivityWarnings.length > 0`, agrupadas por type de teste reprovado (aria-controls / details-summary / dialog) e mostrando o trigger/target + check name por failure.
|
|
499
|
+
- Section "Coverage warnings" listando páginas com `coverage.ratio < 0.85`, ordenadas por ratio asc — primeiro caso é o mais crítico.
|
|
399
500
|
- Footer com: link pra `pages-confirmed.json`, link pro `crawl/pages-list.json` (raw discovery), config snapshot.
|
|
400
501
|
|
|
401
502
|
2. **Persistência cumulativa.** Se o `report.html` já existir (rerun), preserve runs anteriores em uma section "Previous runs" (hierárquica por timestamp). O run mais recente fica no topo. NÃO sobrescreva tudo.
|
|
@@ -406,9 +507,17 @@ Depois que TODAS as páginas confirmadas foram processadas (`pageResults[].lengt
|
|
|
406
507
|
|
|
407
508
|
## Step 6 — Batched auto-learn prompt
|
|
408
509
|
|
|
409
|
-
Único momento opcional de interação após o checkpoint.
|
|
510
|
+
Único momento opcional de interação após o checkpoint. Dispare se EITHER condição (a) OU (b) for atendida:
|
|
511
|
+
|
|
512
|
+
**(a) Iter+success (regra original):** existe ao menos uma página com `iterations > 0` E `status === ✅` — i.e. um fixHint do `sb-review-checks` destravou um build durante o batch. Sinal forte: pattern proven.
|
|
513
|
+
|
|
514
|
+
**(b) Cross-page repetition (nova clause):** a MESMA `violation` (matchada por `id`/`pattern-name` em `review.violations[].id`) aparece em pelo menos `ceil(0.3 * pageCount)` páginas do batch — ou seja, em ≥30% das páginas do batch (com floor de 1 pra batches pequenos: batch de 3 → threshold 1; batch de 10 → threshold 3). Sinal médio: pattern que repete batch-wide é provavelmente generalizável mesmo que ninguém destrave.
|
|
515
|
+
|
|
516
|
+
A clause (b) cobre o blind spot do example-shop: heurística #5b false-positive em 10/10 páginas, todas marcadas ⚠️ ou ❌ (nenhuma com iter>0+✅), pattern não persistido. Com a nova clause, batch-wide repetition triggera o digest mesmo sem destrave.
|
|
517
|
+
|
|
518
|
+
A lista para o digest é a UNIÃO dos patterns detectados pelas duas clauses (deduplicado por `id`).
|
|
410
519
|
|
|
411
|
-
1. Escaneie esses fixHints. Para cada um, cheque se ele já está coberto pela memory cascade carregada na Step 0. Mantenha só os GENERALIZÁVEIS NOVOS (não específicos do site).
|
|
520
|
+
1. Escaneie esses fixHints/violations. Para cada um, cheque se ele já está coberto pela memory cascade carregada na Step 0. Mantenha só os GENERALIZÁVEIS NOVOS (não específicos do site).
|
|
412
521
|
|
|
413
522
|
2. Se a lista pós-filtragem não está vazia, pergunte UMA vez ao usuário, em português, em formato de digest:
|
|
414
523
|
|
|
@@ -15,9 +15,9 @@ Receber UMA URL + UM seletor CSS + um target (WP ou Shopify) e entregar UM arqui
|
|
|
15
15
|
|
|
16
16
|
## The four non-negotiables (herdados do bootstrap)
|
|
17
17
|
|
|
18
|
-
1. **Mobile-first sempre.** Default viewport `390×844` em toda chamada `sb-inspect-live` e `sb-validate-render`. Override só se o usuário pedir desktop explicitamente.
|
|
18
|
+
1. **Mobile-first sempre.** Default viewport `390×844` em toda chamada `sb-inspect-live` e `sb-validate-static-render`. Override só se o usuário pedir desktop explicitamente.
|
|
19
19
|
2. **Defensive specificity é mandatório.** Aplicado dentro de `sb-build-wp` / `sb-build-shopify` — seu trabalho é nunca comer `fixHints` que cubram isso.
|
|
20
|
-
3. **Validate before showing.** NUNCA imprima path de build, conteúdo, ou mensagem "ready to ship" antes de `sb-validate-render` E `sb-compare-visual` confirmarem `diffPercent < threshold` (ou, após o auto-correct esgotar, escale explicitamente).
|
|
20
|
+
3. **Validate before showing.** NUNCA imprima path de build, conteúdo, ou mensagem "ready to ship" antes de `sb-validate-static-render` E `sb-compare-visual` confirmarem `diffPercent < threshold` (ou, após o auto-correct esgotar, escale explicitamente).
|
|
21
21
|
4. **No fabrication.** `assetsMap.failed[]` não some — surface ao builder; nunca invente URL substituta. `inspection.widgetBlocked: true` → pare e escale; não componha de página bloqueada. **Selector que não casa nada** → pare e escale; não fall-back pra full-page silenciosamente (isso é fabricação de escopo).
|
|
22
22
|
|
|
23
23
|
---
|
|
@@ -31,7 +31,8 @@ Receber UMA URL + UM seletor CSS + um target (WP ou Shopify) e entregar UM arqui
|
|
|
31
31
|
| `sb-inspect-live` | **Bash** → `node .claude/skills/sb-inspect-live/scripts/inspect-live.mjs ...` | 95%+ deterministic. Aceita `--selector <css>` nativamente — escopo limitado ao elemento + descendentes. |
|
|
32
32
|
| `sb-extract-assets` | **Bash** → `node .claude/skills/sb-extract-assets/scripts/extract-assets.mjs ...` | Pure download + sanitize + dedupe. **`inspection.imgUrls` já vem filtrado pelo selector** — extract roda nessa lista enxuta. |
|
|
33
33
|
| `sb-build-wp` / `sb-build-shopify` | **Skill** tool → `Skill(skill="sb-build-wp", args="...")` | A composição é criativa. O skill SKILL.md + `references/wp-build-rules.md` é o manual do LLM. Bash não compõe. |
|
|
34
|
-
| `sb-validate-render` | **Bash** → `node .claude/skills/sb-validate-render/scripts/validate-render.mjs ...` | Headless render + token probe. Fully scripted. |
|
|
34
|
+
| `sb-validate-static-render` | **Bash** → `node .claude/skills/sb-validate-static-render/scripts/validate-static-render.mjs ...` | Headless render + token probe. Fully scripted. |
|
|
35
|
+
| `sb-test-interactivity` | **Bash** → `node .claude/skills/sb-test-interactivity/scripts/test-interactivity.mjs ...` | Headless behavioral probe (aria-controls / details / dialog). Diagnostic gate — `passed:false` vira warning, não bloqueia. |
|
|
35
36
|
| `sb-compare-visual` | **Bash** → `node .claude/skills/sb-compare-visual/scripts/compare-visual.mjs ...` | pixelmatch + token diff. Pure determinismo. |
|
|
36
37
|
| `sb-review-checks` | **Bash** → `node .claude/skills/sb-review-checks/scripts/review-checks.mjs ...` | cheerio + regex. Pure determinismo. `candidateFix` strings são contrato — forward verbatim. |
|
|
37
38
|
|
|
@@ -241,7 +242,7 @@ Skill(
|
|
|
241
242
|
|
|
242
243
|
Se tiver `{designKnowledgeSubset}`/`{patternsSubset}` curados, inclua como args extras (`design-knowledge-inline=<base64-or-path>`); senão omita e deixe o skill usar bundled defaults.
|
|
243
244
|
|
|
244
|
-
**O skill retorna:** path absoluto do arquivo escrito, validator report (errors/warnings), e nota de uma linha sobre o pattern escolhido. Não peça o conteúdo do arquivo de volta — o `sb-validate-render` abre.
|
|
245
|
+
**O skill retorna:** path absoluto do arquivo escrito, validator report (errors/warnings), e nota de uma linha sobre o pattern escolhido. Não peça o conteúdo do arquivo de volta — o `sb-validate-static-render` abre.
|
|
245
246
|
|
|
246
247
|
Se o validator interno do skill retornar erros que ele não conseguiu corrigir → surface ao usuário; isso é defeito do builder, não caso de auto-correct.
|
|
247
248
|
|
|
@@ -250,7 +251,7 @@ Se o validator interno do skill retornar erros que ele não conseguiu corrigir
|
|
|
250
251
|
## Step 4 — Validate render (Bash)
|
|
251
252
|
|
|
252
253
|
```bash
|
|
253
|
-
node .claude/skills/sb-validate-render/scripts/validate-render.mjs \
|
|
254
|
+
node .claude/skills/sb-validate-static-render/scripts/validate-static-render.mjs \
|
|
254
255
|
--file "{output-path}" \
|
|
255
256
|
--preset "{preset}" \
|
|
256
257
|
--output-dir "{output_folder}/{project-slug}/reports/validations/{section-slug}-{iteration}" \
|
|
@@ -272,6 +273,31 @@ Capture stdout JSON → `render`. Hold `{render-screenshot}` (`render.screenshot
|
|
|
272
273
|
|
|
273
274
|
---
|
|
274
275
|
|
|
276
|
+
## Step 4-bis — Test interactivity (Bash, diagnostic gate)
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
node .claude/skills/sb-test-interactivity/scripts/test-interactivity.mjs \
|
|
280
|
+
--file "{output-path}" \
|
|
281
|
+
--preset "{preset}" \
|
|
282
|
+
--output-dir "{output_folder}/{project-slug}/reports/validations/{section-slug}-{iteration}"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Capture `interactivity-report.json` (mesmo dir do `validate-static-render`).
|
|
286
|
+
|
|
287
|
+
**Modo diagnóstico (não-blocker):**
|
|
288
|
+
|
|
289
|
+
- Exit `!= 0` → log stderr verbatim e continue pra Step 5 sem warning. Hint comum: `playwright`/chromium faltando — sugira `npx playwright install chromium`. Não escala.
|
|
290
|
+
- Exit `0` AND `passed === true` → silently proceed. Sem warning, sem mudança de status.
|
|
291
|
+
- Exit `0` AND `passed === false`:
|
|
292
|
+
- **Append** ao `interactivityWarnings` array (NÃO single-shot overwrite): `interactivityWarnings.push({ iteration: <n>, tests: report.tests.filter(t => !t.passed) })`. Iterations com 0 warnings NÃO appende. Histórico cross-iter preservado — útil pro success/escalation path (Steps 8/10) que renderiza section em `report.html` resumindo qual iter viu qual break.
|
|
293
|
+
- Status badge da section build ganha sufixo `+!interactive` (e.g. `✅+!interactive`). Calculado dinamicamente: applies se `interactivityWarnings.length > 0` em qualquer iter.
|
|
294
|
+
- **Status final permanece o que a decision matrix decidir** (Step 7). Sem promoção pra ❌. Skill é diagnóstica, não gatekeeper — política locked em G2 spec change log.
|
|
295
|
+
- Continue pra Step 5.
|
|
296
|
+
|
|
297
|
+
**Por que diagnostic e não blocker:** `sb-test-interactivity` é skill nova (G2). False-positives em early adoption são esperados (web components fora da whitelist canônica, drawers com transições atípicas). Promover a hard gate antes da skill amadurecer geraria fricção em builds bons. Warning explícito + `interactivityWarnings` no report dá visibilidade sem bloquear. Mudança pra blocker é decisão de spec própria (G8+).
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
275
301
|
## Step 5 — Compare visual (Bash)
|
|
276
302
|
|
|
277
303
|
```bash
|
|
@@ -295,7 +321,7 @@ node .claude/skills/sb-compare-visual/scripts/compare-visual.mjs \
|
|
|
295
321
|
|
|
296
322
|
Pattern #27 ainda aplica do lado do build — se a section em mobile fica em 390×507 dentro de viewport 390×844, sem o crop sobra 337px de espaço branco que infla diff% gratuitamente.
|
|
297
323
|
|
|
298
|
-
DPR flags default `3` (matches `sb-inspect-live` iPhone 14 + `sb-validate-render` `deviceScaleFactor=3`, Pattern #22). Não override.
|
|
324
|
+
DPR flags default `3` (matches `sb-inspect-live` iPhone 14 + `sb-validate-static-render` `deviceScaleFactor=3`, Pattern #22). Não override.
|
|
299
325
|
|
|
300
326
|
Capture stdout JSON → `compare`. Note path do `report.json` persistido → `{compare-report-path}`.
|
|
301
327
|
|
|
@@ -333,6 +359,39 @@ Capture stdout JSON → `review`. Note `review.passed` e o array `violations[]`
|
|
|
333
359
|
|
|
334
360
|
## Step 7 — Decision matrix (Pattern #28)
|
|
335
361
|
|
|
362
|
+
**Pre-check 0 (coverage gate, runs FIRST):**
|
|
363
|
+
|
|
364
|
+
Para `/clip-section`, "live" é o elemento alvo do selector. Calcule contra schema **real** do `sb-inspect-live`:
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
buildHeight = render.geometry.totalHeight # primary: from sb-validate-static-render
|
|
368
|
+
liveMainHeight = inspection.sectionBoundingBox.h # primary: bbox do selector target (set when --selector é passed)
|
|
369
|
+
|| inspection.dom[0].bbox.h # fallback 1: root walked node bbox (warn — clip pode ser menor que a página)
|
|
370
|
+
|| (walk inspection.dom recursively, collect classified-sections bboxes,
|
|
371
|
+
return max(s.bbox.y + s.bbox.h) - min(s.bbox.y))
|
|
372
|
+
# fallback 2: classified sections span (warn)
|
|
373
|
+
coverageRatio = buildHeight / liveMainHeight
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
`inspection.sectionBoundingBox` é o bbox do selector alvo (schema em `inspect-live.mjs:163,1219`). Quando `--selector` é passed, este field é populated. Sem selector, fica `null` e cai pros fallbacks.
|
|
377
|
+
|
|
378
|
+
If `buildHeight` is missing → HALT com erro `[clip-section] FATAL: render.geometry.totalHeight missing`.
|
|
379
|
+
If `liveMainHeight` fell back → log warning ao user em `report.html` notes.
|
|
380
|
+
If `liveMainHeight < 100` → log warn `[clip-section] WARN: liveMainHeight < 100, coverage gate skipped`; skip; persist `coverage.skippedReason = 'degenerate-height'`.
|
|
381
|
+
If `inspection.dom[]` empty AND no classified sections → log warn `[clip-section] WARN: coverage gate skipped — empty inspection.dom`; skip; `coverage = null`.
|
|
382
|
+
|
|
383
|
+
**Tier triage:**
|
|
384
|
+
|
|
385
|
+
| Range | Status | Ação |
|
|
386
|
+
| --- | --- | --- |
|
|
387
|
+
| `< 0.40` | ❌ **incomplete** | Status: `"incomplete — built {ratio*100:.0f}% of source clip"`. Vai pra Step 9 (auto-correct loop) se budget permite; existing review-derived fixHints feed back to composer. **NO new EXTENSION marker** — Step 9 já consome `review.violations[]` canonical. Coverage `< 0.40` apenas força ❌ no matrix override. |
|
|
388
|
+
| `0.40 — 0.85` | ⚠️ **partial** | Status: `"partial — built {ratio*100:.0f}%"`. Prossegue pra matriz abaixo (não bypassa). Coverage warning sobrevive como overlay. |
|
|
389
|
+
| `>= 0.85` | (proceed) | Coverage OK. Matriz roda silenciosamente. |
|
|
390
|
+
|
|
391
|
+
Persist `coverage = { buildHeight, liveMainHeight, ratio, source: 'sectionBoundingBox'|'rootBbox'|'sectionsSpan', missingSections?: [...], skippedReason?: <string> }` em report. `missingSections` (quando `ratio < 0.85`): walk `inspection.dom` recursivamente, collect classified-section children do selector cujo `bbox.y >= buildHeight`.
|
|
392
|
+
|
|
393
|
+
**Existing matrix (roda só se coverage gate não routou pra ❌):**
|
|
394
|
+
|
|
336
395
|
Você tem `compare.passed` E `review.passed` agora. Aplique:
|
|
337
396
|
|
|
338
397
|
| `review.passed` | `compare.passed` | Ação |
|
|
@@ -513,7 +572,7 @@ Nunca escreva fora de `{project-slug}/`. Cross-project asset sharing acontece s
|
|
|
513
572
|
Quando este command for primeiro wired-up, smoke test canônico:
|
|
514
573
|
|
|
515
574
|
```
|
|
516
|
-
/clip-section https://
|
|
575
|
+
/clip-section https://example-store.com .ai-hero --target shopify
|
|
517
576
|
```
|
|
518
577
|
|
|
519
|
-
Esperado: arquivo `{output_folder}/
|
|
578
|
+
Esperado: arquivo `{output_folder}/example-store/clean/sections/ai-hero.liquid` em até ~30s, diff sob threshold, decision matrix funcionando, report.html abrindo. Sem crawl, sem checkpoint humano. MVP milestone do `/clip-section`.
|