similarbuild 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/package.json +1 -1
  2. package/templates/commands/build-page.md +62 -493
  3. package/templates/commands/build-site.md +98 -705
  4. package/templates/commands/clip-section.md +102 -501
  5. package/templates/skills/sb-build-wp/SKILL.md +37 -164
  6. package/templates/skills/sb-build-wp/references/wp-build-rules.md +11 -1
  7. package/templates/skills/sb-inspect-live/scripts/inspect-live.mjs +4 -367
  8. package/templates/skills/sb-review-checks/SKILL.md +0 -113
  9. package/templates/skills/sb-review-checks/references/review-rules.md +0 -195
  10. package/templates/skills/sb-review-checks/scripts/lib/anti-patterns.mjs +0 -385
  11. package/templates/skills/sb-review-checks/scripts/lib/cross-reference.mjs +0 -115
  12. package/templates/skills/sb-review-checks/scripts/lib/design-quality.mjs +0 -541
  13. package/templates/skills/sb-review-checks/scripts/review-checks.mjs +0 -250
  14. package/templates/skills/sb-review-checks/scripts/tests/test-anti-patterns.mjs +0 -366
  15. package/templates/skills/sb-review-checks/scripts/tests/test-cross-reference.mjs +0 -170
  16. package/templates/skills/sb-review-checks/scripts/tests/test-design-quality.mjs +0 -493
  17. package/templates/skills/sb-review-checks/scripts/tests/test-review-checks.mjs +0 -267
  18. package/templates/skills/sb-test-interactivity/SKILL.md +0 -133
  19. package/templates/skills/sb-test-interactivity/scripts/test-interactivity.mjs +0 -970
  20. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-broken.html +0 -32
  21. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-good.html +0 -47
  22. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/deferred-listeners.html +0 -67
  23. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/details-good.html +0 -25
  24. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/dialog-good.html +0 -38
  25. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/no-interactive.html +0 -15
  26. package/templates/skills/sb-test-interactivity/scripts/tests/test-test-interactivity.mjs +0 -223
@@ -1,578 +1,179 @@
1
1
  ---
2
- description: SimilarBuild orchestrator — clona UMA section específica de uma página live (via CSS selector), entrega arquivo pronto pra colar como widget HTML / section Liquid, validado e auto-corrigido.
3
- argument-hint: <URL> <selector> [--target wp|shopify] [--project-name <slug>] [--no-auto-correct] [--dry-run]
2
+ description: Clones ONE section of a live page to a paste-ready HTML widget for WordPress/Elementor or Shopify, validated visually against the live screenshot via render+diff loop.
3
+ argument-hint: <URL> [--selector <css>] [--target wp|shopify] [--project-name <slug>] [--max-iterations <n>]
4
4
  ---
5
5
 
6
- # /clip-section — SimilarBuild section-only orchestrator
6
+ # /clip-section — atomic section clone (the canonical unit)
7
7
 
8
- You are the SimilarBuild orchestrator for a single section of a live page. Persona: senior visual-migration engineer — direto, opinionado, fluente em português técnico, alérgico a improviso, devoto de defensive specificity. Você acredita em **"validate before showing the user"** e nunca quebra essa regra.
8
+ You are the SimilarBuild orchestrator for ONE section. The atom of the framework. Pixel parity over architecture.
9
9
 
10
10
  ## Mission
11
11
 
12
- Receber UMA URL + UM seletor CSS + um target (WP ou Shopify) e entregar UM arquivo que o usuário cola no destino como widget/section, **visualmente validado contra a live**, em até N=2 tentativas de auto-correção. Sem crawl, sem checkpoint humano, sem batch single-shot focado.
12
+ Clone ONE section of a live page so it renders visually identical to the live, packaged as a single HTML fragment ready to paste into the destination (Elementor Custom HTML widget or Shopify section).
13
13
 
14
- **Use case canônico:** *"esse hero da loja X é lindo, quero esse hero na minha loja"* → `/clip-section https://lojax.com .hero --target shopify` → arquivo `.liquid` pronto pra instalar como section editável.
14
+ ## The loop (non-negotiable)
15
15
 
16
- ## The four non-negotiables (herdados do bootstrap)
16
+ Per section, run this 4-step cycle. Repeat until `diff% < threshold` OR `iterations >= max`.
17
17
 
18
- 1. **Mobile-first sempre.** Default viewport `390×844` em toda chamada `sb-inspect-live` e `sb-validate-static-render`. Override se o usuário pedir desktop explicitamente.
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-static-render` E `sb-compare-visual` confirmarem `diffPercent < threshold` (ou, após o auto-correct esgotar, escale explicitamente).
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).
18
+ 1. **INSPECT** the live URL (full-page or selector-scoped) capture tokens, screenshot, and DOM tree.
19
+ 2. **BUILD** the section in HTML/CSS/JS with defensive specificity, mobile-first, no fabrication.
20
+ 3. **RENDER** the built fragment locally with Playwright at the same mobile viewport, with a simulated theme reset injected.
21
+ 4. **DIFF** the rendered screenshot against the reference (live) screenshot. If `diff% < threshold` → DONE. If notfeed the diff back into BUILD as `fixHints` and loop.
22
22
 
23
- ---
24
-
25
- ## Architectural decision: how to invoke sub-skills
26
-
27
- **Decision: hybrid — `Bash` para skills determinísticas, `Skill` tool para o composer.** (Mesmo padrão do `/build-page` e `/build-site`.)
28
-
29
- | Sub-skill | Invocation | Why |
30
- | --- | --- | --- |
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
- | `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
- | `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-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. |
36
- | `sb-compare-visual` | **Bash** → `node .claude/skills/sb-compare-visual/scripts/compare-visual.mjs ...` | pixelmatch + token diff. Pure determinismo. |
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. |
38
-
39
- **Decision sobre reuso da lógica de `/build-page`:** orchestration loop interno reusando os mesmos Bash/Skill calls. **Não invoque `/build-page` via Task tool e não delegue pra subagente** — duplicaria contexto, cobraria 2× tokens e o `/clip-section` é mais simples (sem section-type → subfolder routing, sem `clean/home` vs `clean/pdp`, etc.). Orchestre direto neste arquivo.
40
-
41
- ---
42
-
43
- ## Tool dependencies
44
-
45
- - `Bash` — orquestrar, parsear JSON via `jq` ou shell, rodar os `.mjs`, mkdir, ler config
46
- - `Read` — carregar `.claude/sb-config.yaml`, memory files, fragmento buildado
47
- - `Write` — escrever `report.html`, `decisions.md`, anti-patterns auto-aprendidos
48
- - `Edit` — atualização cirúrgica em `report.html` entre iterações
49
- - `Skill` — invocar `sb-build-wp` / `sb-build-shopify`
50
- - `WebFetch` — opcional, sanity-check do URL antes de lançar Playwright
51
-
52
- NÃO use o `Agent` tool pra delegar o workflow — o orchestrator fica na main conversation.
53
-
54
- ---
23
+ When you skip step 3+4, your fragment "viaja" — it diverges from the live without you noticing. This is the lesson from prior iterations.
55
24
 
56
25
  ## Inputs (parse from `ARGUMENTS:`)
57
26
 
58
- O texto após `ARGUMENTS:` contém a entrada do usuário. Extraia:
59
-
60
27
  | Flag | Required | Default | Behavior |
61
- | --- | --- | --- | --- |
62
- | `<URL>` (positional, primeiro) | yes | — | URL absoluta da página que contém a section. Se ausente, pare e pergunte. |
63
- | `<selector>` (positional, segundo) | yes | | CSS selector do elemento a clonar. Se ausente, pare e pergunte. **Validação mínima** (Step 0.3): não-vazio depois de trim, sem `\n`/`\r`/`\t`, e com pelo menos um caractere de selector válido (`.` `#` `[` letra). Se inválido, pare com explicação. |
64
- | `--target wp\|shopify` | no | `default_target` do `.claude/sb-config.yaml`, fallback `wp` | Roteia pro `sb-build-wp` (`wp`) ou `sb-build-shopify` (`shopify`). Se `shopify` foi pedido e a skill não existe (cheque `.claude/skills/sb-build-shopify/SKILL.md`), pare e diga. |
65
- | `--project-name <slug>` | no | auto-derivado do hostname (Step 0.4) | Override do nome do projeto. Útil pra branch experiments (ex: `--project-name lojax-clones`). |
66
- | `--no-auto-correct` | no | false | Escala no primeiro decision-matrix que pediria loop. |
67
- | `--dry-run` | no | false | Roda inspect + extract + plan-summary, **não builda**. Imprime sumário (selector assets que seriam baixados estimativa de complexidade) e sai. |
28
+ |---|---|---|---|
29
+ | `<URL>` (positional) | yes | — | Absolute URL of the live page containing the section. |
30
+ | `--selector <css>` | no | full page | CSS selector to scope the section. Without it, treat the entire page as one section. |
31
+ | `--target wp\|shopify` | no | `wp` from config | Routes to `sb-build-wp` or `sb-build-shopify`. |
32
+ | `--project-name <slug>` | no | hostname-derived | Output folder name. |
33
+ | `--max-iterations <n>` | no | 2 | Cap on build→render→diff loop. After hitting the cap, ship the best attempt + a TODO for manual review. |
34
+ | `--diff-threshold <pct>` | no | from config (default 10) | Diff % below which the section is considered "matching". |
68
35
 
69
- Flag desconhecida ignore com warning de uma linha. Não erre.
36
+ ## Output folder structure
70
37
 
71
- **Disambiguação importante:** seletores podem começar com `-` (ex: `-mod-hero`) e confundir o parser de flags. Trate o segundo argumento posicional como literal antes de tentar parsear flags. Heurística: tudo entre o URL e a primeira ocorrência de `--<flag>` que case `--target|--project-name|--no-auto-correct|--dry-run` é o selector. Se o selector tem espaços (ex: `.section .hero h1`), o usuário precisa quotar — documente isso na mensagem de erro quando o parse falhar.
38
+ ```
39
+ {output_folder}/{project-slug}/sections/{section-slug}/
40
+ ├── reference.png — live screenshot (from sb-inspect-live)
41
+ ├── tokens.json — typography, colors, layout tokens (from sb-inspect-live)
42
+ ├── inspection.json — full inspection bundle (DOM, imgUrls, sectionCrops if multi-section page)
43
+ ├── assets-map.json — URL → localPath (from sb-extract-assets)
44
+ ├── section.html — the cloned fragment (paste-ready)
45
+ ├── rendered.png — local render of section.html (from sb-validate-static-render)
46
+ └── diff.json — pixel + token diff vs reference.png (from sb-compare-visual)
47
+ ```
72
48
 
73
- ---
49
+ When the loop converges, `section.html` matches `reference.png` within threshold. If the loop runs out of iterations, the best attempt is preserved with `diff.json` capturing the residual diff so the user can manually adjust.
74
50
 
75
- ## Step 0 Config + memory + selector validation
76
-
77
- 1. **Load config.** Read `.claude/sb-config.yaml` se existir. Defaults quando ausente ou key faltando:
78
-
79
- ```yaml
80
- output_folder: ./sb-output
81
- default_target: wp
82
- default_viewport: 390
83
- auto_correct_max_iterations: 2
84
- diff_threshold_percent: 10
85
- strip_metadata: true
86
- ```
87
-
88
- Não auto-crie o arquivo — o installer (item #16 do roadmap) é dono disso. Apenas opere com defaults.
89
-
90
- 2. **Memory cascade** (pattern #21). Mesma lógica do `/build-page`: plugin → per-user → bundled fallback.
91
- - **Plugin (versioned)**: `.claude/skills/sb-shared/memory/{anti-patterns,patterns,fixes,design-knowledge}.md` — skip silencioso se ausente.
92
- - **Per-user (auto-learned)**: `~/.claude/similarbuild-memory/{anti-patterns,patterns,fixes}.md` — skip silencioso se ausente. Não crie o diretório eagerly; só se Step 12 tiver algo pra salvar.
93
- - **Bundled fallback**: cada sub-skill carrega o seu — não é seu trabalho.
94
-
95
- Hold em working memory pra (a) filtrar subset relevante por section-type quando invocar `sb-build-{wp,shopify}` (lean context discipline — pattern #3), e (b) reconhecer pattern novo no auto-learn (Step 12).
96
-
97
- **Filtro pra builder:** quando invocar `sb-build-wp`/`sb-build-shopify`, NÃO passe a memory cascade inteira. Passe só:
98
- - Patterns cujo `sectionType` case com `inspection.sectionType` (ou siblings próximos).
99
- - Anti-patterns prováveis pro section-type detectado.
100
- - Design-knowledge tagged pro target (`wp` ou `shopify`) — máximo 1-2 KB curado.
101
-
102
- Se nada curado ainda (early framework), skip — o `references/` bundled da skill é suficiente.
103
-
104
- 3. **Selector validation (mínima).** Pegue o selector raw, trim. Rejeite e pare com mensagem clara se:
105
- - Vazio depois do trim.
106
- - Contém `\n`, `\r`, `\t`, ou caractere de controle (`< 0x20`).
107
- - Maior que 200 chars (defesa contra paste acidental de bloco).
108
- - Não contém nenhum de: letra, dígito, `.`, `#`, `[`, `*`, `:` (algo tem que se parecer com selector).
109
-
110
- Mensagem de erro padrão: `"Selector inválido: '{selector}'. Esperado CSS selector como '.hero', '#main-section', 'section.hero[data-id=foo]'. Selectors com espaço precisam ser quotados na linha de comando."` Pare.
111
-
112
- 4. **Project slug.**
113
- - Se `--project-name <slug>` foi passado: use verbatim (valide URL-safe; warn se não).
114
- - Senão: derive do hostname.
115
- - Parse hostname (ex: `https://www.lojax.com/path?q=x` → `www.lojax.com`).
116
- - Strip `www.`, strip subdomain líder só quando for `www`/`m`/`store`/`shop`.
117
- - Strip TLD (último `.xxx` ou `.xxx.yy` pra TLDs compostos conhecidos como `.com.br`, `.co.uk`).
118
- - Lowercase, replace não-`[a-z0-9]` por `-`, collapse repeats, trim leading/trailing `-`.
119
- - Exemplos: `https://lojax.com` → `lojax`; `https://www.exemplo.com.br/x` → `exemplo`.
120
- - Hold como `{project-slug}` pelo resto do run.
121
-
122
- 5. **Section slug** (derivado do selector — usado pro nome do arquivo final).
123
- - Strip whitespace.
124
- - Se começa com `.`: drop o `.`. Ex: `.hero-section` → `hero-section`.
125
- - Se começa com `#`: drop o `#`. Ex: `#testimonials` → `testimonials`.
126
- - Se começa com tag (ex: `section.hero`): pega a primeira classe/id depois da tag (ex: `section.hero` → `hero`); se não tem nenhuma, pega a tag (ex: `main` → `main`).
127
- - Selectors compostos/complexos (`.foo > .bar`, `[data-x=y]`, `:nth-child(2)`): replace tudo que não for `[a-z0-9]` por `-`, collapse repeats, trim, lowercase, max 60 chars.
128
- - Se o resultado fica vazio ou só `-` depois disso, fallback pra `section-{ts}` (UNIX seconds). Logue.
129
- - Hold como `{section-slug}`.
130
-
131
- 6. **Project structure.** Compute `{project-root}/{output_folder}/{project-slug}/` e garanta os subdirs (mkdir -p, idempotente):
132
-
133
- ```
134
- {output_folder}/{project-slug}/
135
- ├── clean/
136
- │ └── sections/ ← saída do /clip-section vai aqui (sempre)
137
- ├── assets/ ← content-hash images (compartilhado se a pasta já existir)
138
- ├── reports/
139
- │ ├── diffs/
140
- │ └── validations/
141
- └── .sb-memory/ ← project-local memory (decisions, etc.)
142
- ```
143
-
144
- Nunca escreva fora de `{project-slug}/` (isolation guarantee — pattern #15).
145
-
146
- Nota: se o projeto já existe (rerun ou compartilhado com `/build-page`), o `assets/` é reusado naturalmente — `sb-extract-assets` deduplica por content-hash via `--existing-assets-dir`.
51
+ ## Steps (the exact flow)
147
52
 
148
- ---
53
+ ### Step 0 — Config + project slug
149
54
 
150
- ## Step 1 Inspect live com selector (Bash)
55
+ 1. Load `.claude/sb-config.yaml` (fallbacks: `output_folder=./sb-output`, `default_target=wp`, `default_viewport=390`, `diff_threshold_percent=10`, `auto_correct_max_iterations=2`).
56
+ 2. Derive `{project-slug}` from hostname (strip `www.`/`m.`/`store.`/`shop.` + TLD, kebab-case) unless `--project-name` is provided.
57
+ 3. Derive `{section-slug}` from the selector (sanitized) or from `sectionType` returned by inspect. When no selector, use `home` / `pdp-<slug>` / etc.
58
+
59
+ ### Step 1 — Inspect (Bash)
151
60
 
152
61
  ```bash
153
62
  node .claude/skills/sb-inspect-live/scripts/inspect-live.mjs \
154
63
  --url "{URL}" \
155
- --selector "{selector}" \
156
64
  --viewport-width {default_viewport} \
157
65
  --viewport-height 844 \
158
- --output-dir "{output_folder}/{project-slug}/.sb-memory/inspect-{section-slug}-{ts}"
66
+ --output-dir "{output_folder}/{project-slug}/sections/{section-slug}" \
67
+ [--selector "{selector}"]
159
68
  ```
160
69
 
161
- Onde `{ts}` é UNIX seconds pra rerun não clobber. Capture stdout JSON `inspection`.
162
-
163
- **O que muda com `--selector`:**
164
- - `inspection.sectionBoundingBox === null` (a screenshot já É só o elemento — não há "bbox dentro de página inteira" pra recortar).
165
- - `inspection.dom`, `inspection.imgUrls`, `inspection.tokens`, `inspection.pseudoElements` ficam todos escopados ao elemento + descendentes.
166
- - `inspection.sectionType` reflete o tipo detectado dentro do escopo do selector (ou cai pro fallback do inspector).
70
+ The skill writes `screenshot.png` (the reference rename to `reference.png` afterwards) and `inspection.json` (containing `tokens`, `dom`, `imgUrls`, `sectionCrops[]` when multi-section).
167
71
 
168
- **Branch:**
169
- - Exit non-zero → surface stderr ao usuário, pare. Se mencionar `playwright`, sugira `npx playwright install chromium`.
170
- - `inspection.widgetBlocked === true` → pare. "A página live retornou bot-challenge / Cloudflare wall. Tente outro URL, ou abra no browser e me passe o HTML renderizado." NÃO continue.
171
- - `inspection.dom` empty mas `widgetBlocked: false` → pare. **Provavelmente o seletor não casou nada.** Mensagem: `"Selector '{selector}' não casou nenhum elemento em {URL}. Verifique o seletor (use DevTools → Elements → Copy → Copy selector como sanity check), ou tente um seletor mais frouxo."` Pare. **Não fall-back pra full-page** — seria fabricar escopo (não-negociável #4).
172
- - Senão, hold `{inspection-path}` (path pro `inspection.json` salvo no `--output-dir`) e `{inspection-screenshot}` (path pra `screenshot.png` no mesmo dir).
72
+ ```bash
73
+ mv "{section-dir}/screenshot.png" "{section-dir}/reference.png"
74
+ jq '.tokens' "{section-dir}/inspection.json" > "{section-dir}/tokens.json"
75
+ ```
173
76
 
174
- **Lean context** `--url`, `--selector`, viewport, output-dir. Sem memória, sem patterns. Disciplina.
77
+ If `inspection.widgetBlocked` is true halt and surface to user. Don't compose from a blocked page.
175
78
 
176
- ---
177
-
178
- ## Step 2 — Extract assets (Bash)
79
+ ### Step 2 — Extract assets (Bash)
179
80
 
180
81
  ```bash
181
82
  node .claude/skills/sb-extract-assets/scripts/extract-assets.mjs \
182
- --inspection-path "{inspection-path}" \
83
+ --inspection-path "{section-dir}/inspection.json" \
183
84
  --output-dir "{output_folder}/{project-slug}/assets" \
184
85
  --target "{target}" \
185
86
  --existing-assets-dir "{output_folder}/{project-slug}/assets"
186
87
  ```
187
88
 
188
- (`--existing-assets-dir` apontando pro mesmo dir é intencional o dedupe scan do disco.)
189
-
190
- Capture stdout JSON → `assetsMap`. Path persistido → `{assets-map-path}` (script grava `assets-map.json` no `--output-dir`).
89
+ Produces `assets-map.json` next to `inspection.json`. Failed assets become `<!-- TODO: asset failed download -->` placeholders in the build — never fabricate URLs.
191
90
 
192
- **Crítico do `/clip-section`:** `inspection.imgUrls` JÁ vem filtrado pelo escopo do selector o extract baixa só assets dentro da seção. Não tente filtrar mais nada.
91
+ ### Step 3Build (Skill)
193
92
 
194
- **Branch:**
195
- - Exit non-zero → surface stderr, pare.
196
- - `assetsMap.failed[]` não-vazio → NÃO pare. Forwarde pro builder; o builder decide por asset (drop decorativo, placeholder hero, nunca fabrica).
197
- - `assetsMap.assets[].strippedMetadata === false` em alguma entrada → log warning no report; continue.
198
-
199
- **Lean context** — só URL list (via `--inspection-path`) e output dir. Nada mais.
200
-
201
- **Dry-run check:** se `--dry-run` foi passado, AGORA é o ponto de saída. Imprima:
202
-
203
- ```
204
- 🔍 Dry-run /clip-section
205
- URL: {URL}
206
- Selector: {selector}
207
- Section: {section-slug} (type: {inspection.sectionType})
208
- Target: {target}
209
- Assets: {N total} ({K cached, J novos, F failed})
210
- Saída prevista: {output_folder}/{project-slug}/clean/sections/{section-slug}.{ext}
211
- ```
212
-
213
- Escreva `reports/plan-{section-slug}-{ts}.md` com o mesmo conteúdo + lista detalhada dos assets. Pare. NÃO continue pra Step 3.
214
-
215
- ---
216
-
217
- ## Step 3 — Build (Skill)
218
-
219
- Decida a skill: `sb-build-wp` se `target === wp`, senão `sb-build-shopify`. Compute `{output-path}`:
220
-
221
- - WP: `{output_folder}/{project-slug}/clean/sections/{section-slug}.html`
222
- - Shopify: `{output_folder}/{project-slug}/clean/sections/{section-slug}.liquid`
223
-
224
- Filtre o memory cascade pelo subset relevante (per Step 0.2) → `{designKnowledgeSubset}` e `{patternsSubset}`.
225
-
226
- Invoque via **Skill tool**:
93
+ Iteration counter starts at 1.
227
94
 
228
95
  ```
229
96
  Skill(
230
- skill="sb-build-wp",
231
- args="inspection={inspection-path} assets-map={assets-map-path} output-path={output-path} preset=wp-elementor"
97
+ skill="sb-build-wp", # or sb-build-shopify
98
+ args="inspection={section-dir}/inspection.json assets-map={section-dir}/assets-map.json output-path={section-dir}/section.html preset=wp-elementor reference-image={section-dir}/reference.png"
232
99
  )
233
100
  ```
234
101
 
235
- Pra Shopify:
236
- ```
237
- Skill(
238
- skill="sb-build-shopify",
239
- args="inspection={inspection-path} assets-map={assets-map-path} output-path={output-path} preset=shopify-section"
240
- )
241
- ```
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.
244
-
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.
102
+ The composer reads the reference screenshot + tokens + inspection corpus, builds the fragment following `wp-build-rules.md` (defensive specificity, mobile-first, no fabrication, image-first when a region is dominated by `<img>`), validates with `build-wp.mjs validate --inspection-path` (catches fabricated literals), writes the file.
246
103
 
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.
104
+ On retry iterations, pass `previous-html-path={section-dir}/section.html` + `fix-hints-path={section-dir}/diff.json`.
248
105
 
249
- ---
250
-
251
- ## Step 4 — Validate render (Bash)
106
+ ### Step 4 — Render (Bash)
252
107
 
253
108
  ```bash
254
109
  node .claude/skills/sb-validate-static-render/scripts/validate-static-render.mjs \
255
- --file "{output-path}" \
110
+ --file "{section-dir}/section.html" \
256
111
  --preset "{preset}" \
257
- --output-dir "{output_folder}/{project-slug}/reports/validations/{section-slug}-{iteration}" \
112
+ --output-dir "{section-dir}" \
258
113
  --viewport-width {default_viewport} \
259
114
  --viewport-height 844
260
115
  ```
261
116
 
262
- Onde `{preset}` é `wp-elementor` ou `shopify-section`, e `{iteration}` é `0` no primeiro build, `1`/`2` em retries.
263
-
264
- Capture stdout JSON → `render`. Hold `{render-screenshot}` (`render.screenshot`) e `{render-json-path}` (path pro `render.json` salvo).
117
+ Produces `rendered.png` + `render.json` (geometry, token probe). If render fails (tiny screenshot, JS error during page load) → mark this iteration failed, retry build with the error as fixHint.
265
118
 
266
- **Branch:**
267
- - Exit non-zero → se stderr menciona `liquidjs` ou `playwright` faltando, surface install hint; senão pass-through e pare.
268
- - `render.warnings[]` inclui `tiny-screenshot` → trate como hard failure desta iteração (build não renderizou). Skip Steps 5–7 e route pra Step 9 (auto-correct loop) se budget permite, senão Step 10 (escala).
269
- - `render.geometry.viewportOverflow === true` → flag pro comparator (high-severity hint), e ALSO suprima `--crop-build-bbox` no Step 5 (bbox subestimaria com overflow). Continue pra Step 5.
270
- - Senão, continue.
271
-
272
- **Lean context** — file + preset + output-dir + viewport. O skill carrega seu próprio preset YAML.
273
-
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
-
301
- ## Step 5 — Compare visual (Bash)
119
+ ### Step 5 — Diff (Bash)
302
120
 
303
121
  ```bash
304
122
  node .claude/skills/sb-compare-visual/scripts/compare-visual.mjs \
305
- --live-screenshot "{inspection-screenshot}" \
306
- --build-screenshot "{render-screenshot}" \
307
- --output-dir "{output_folder}/{project-slug}/reports/diffs/{section-slug}-{iteration}" \
308
- --tokens-live "{inspection-path}" \
309
- --tokens-build "{render-json-path}" \
310
- --threshold {diff_threshold_percent} \
311
- [--crop-build-bbox "{x},{y},{w},{h}"]
123
+ --live-screenshot "{section-dir}/reference.png" \
124
+ --build-screenshot "{section-dir}/rendered.png" \
125
+ --output-dir "{section-dir}" \
126
+ --tokens-live "{section-dir}/inspection.json" \
127
+ --tokens-build "{section-dir}/render.json" \
128
+ --threshold {diff_threshold_percent}
312
129
  ```
313
130
 
314
- **Scope-aligned diff (anti-pattern #10 + Pattern #27) caso simplificado do `/clip-section`:**
315
-
316
- - **`--crop-live-bbox`** — **NÃO passe.** Como o inspector rodou com `--selector`, `inspection.sectionBoundingBox === null` por design (a screenshot live JÁ é element-only). Adicionar crop aqui seria operação inválida.
317
-
318
- - **`--crop-build-bbox`** — passe `render.geometry.sections[0].bbox.{x,y,w,h}` (ou `render.geometry.probeRoot.bbox` se `sections[]` vazio) quando:
319
- - `render.geometry.viewportOverflow === false` (senão bbox subestima e cliparia overflow real), AND
320
- - O bbox da section é **menor que a altura do viewport capturado** por margem não-trivial (>10% menor). Sections que enchem o viewport não se beneficiam.
321
-
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.
323
-
324
- DPR flags default `3` (matches `sb-inspect-live` iPhone 14 + `sb-validate-static-render` `deviceScaleFactor=3`, Pattern #22). Não override.
325
-
326
- Capture stdout JSON → `compare`. Note path do `report.json` persistido → `{compare-report-path}`.
131
+ Produces `diff.json` (pixel diff%, structured diffs) and `diff-map.png` (red-overlay highlighting divergent regions).
327
132
 
328
- **Não branche no exit ainda.** `review-checks` é o ground truth pra "o build é estruturalmente sound?" (Pattern #28). Capture `compare.{passed, diffPercent}` e siga pra Step 6 — a decision matrix da Step 7 precisa de ambos compare AND review.
133
+ ### Step 6 — Decide
329
134
 
330
- - `1` → script error. Surface stderr, pare.
331
- - `2` → invalid args. Bug neste orchestrator fix the call.
135
+ - `diff.json.passed === true` (diff < threshold) **DONE**. Print success + paths. Section is ready to paste.
136
+ - `diff.json.passed === false` AND `iteration < max_iterations` loop back to Step 3 with `diff.json` as fixHints. Increment iteration.
137
+ - `diff.json.passed === false` AND `iteration >= max_iterations` → **SHIP WITH WARNING**. The fragment is the best attempt; `diff.json` documents the residual gaps. Print a warning + the residual diff% so the user can decide whether to manually tweak or re-run with a tighter selector.
332
138
 
333
- **Lean context**dois screenshot paths + dois token JSONs + threshold + opcional bbox. Só isso.
139
+ ### Step 7 Print summary
334
140
 
335
- ---
336
-
337
- ## Step 6 — Review checks (Bash, sempre roda — Pattern #28)
338
-
339
- ```bash
340
- node .claude/skills/sb-review-checks/scripts/review-checks.mjs \
341
- --file "{output-path}" \
342
- --preset "{preset}" \
343
- --output-dir "{output_folder}/{project-slug}/reports/validations/{section-slug}-{iteration}" \
344
- --compare-diffs "{compare-report-path}"
345
141
  ```
142
+ 🏁 /clip-section {URL} → {section-dir}/
346
143
 
347
- Capture stdout JSON → `review`. Note `review.passed` e o array `violations[]` — ordenado high → medium → low, com `correlatedDiff` setado em entries que sobrepõem high-severity visual diff.
144
+ Section: {section-slug}
145
+ Iterations: {n}
146
+ Diff%: {final-diff-pct}
147
+ Status: ✅ matching | ⚠️ residual diff above threshold
348
148
 
349
- **Por que sempre roda (Pattern #28):** review-checks é auditor puro-determinístico (cheerio + regex, sem chromium, ~0s overhead). Compare-visual mede *visual drift*; review-checks mede *structural soundness*. São ortogonais — um build com `100vh` é estruturalmente quebrado mesmo se o diff cair baixo; um build com HTML perfeito ainda pode driftar visual por font rendering. Rodar review-checks unconditionally faz a Step 7 disparar em sinal, não vibes.
350
-
351
- **Branch no exit code:**
352
- - `0` `review.passed === true`. Continue pra Step 7.
353
- - `3` → `review.passed === false`, violations presentes. Continue pra Step 7.
354
- - `1`/`2` → surface stderr, pare.
355
-
356
- **Lean context** — file + preset + output-dir + compare report path. Nada mais.
357
-
358
- ---
149
+ Files:
150
+ section.html — paste into Elementor Custom HTML / Shopify Liquid
151
+ reference.png live screenshot (your visual ground truth)
152
+ rendered.png — local render (what you'll see when pasted)
153
+ diff.json — structured diff vs reference
359
154
 
360
- ## Step 7 — Decision matrix (Pattern #28)
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
-
395
- Você tem `compare.passed` E `review.passed` agora. Aplique:
396
-
397
- | `review.passed` | `compare.passed` | Ação |
398
- | --------------- | ---------------- | --------------------------------------------------------------------------------------------- |
399
- | true | true | ✅ **Ship.** Vai pra Step 8 (success path). |
400
- | false | true | ⚠️ **Escala com structural warning.** Vai pra Step 10. Build *parece* certo mas `violations[]` carrega defeitos escondidos (a11y, perf, anti-patterns) que o user precisa ver. Não auto-loop — review-checks não é trigger de auto-correct quando visual já casou. |
401
- | true | false | ⚠️ **Escala (visual drift, sem fix actionable).** Vai pra Step 10. Não tem fixHints pra realimentar — patch LLM-side de raw `structuredDiffs` é chute. Surface ao user com diff map. |
402
- | false | false | 🔄 **Auto-correct loop.** Vai pra Step 9. fixHints do review podem dirigir `sb-build-{wp,shopify}` a patch cirúrgico. Itera até flip ou exhaust. |
403
-
404
- **Dois pre-checks antes da matriz disparar:**
405
- 1. Se `--no-auto-correct` foi passado e qualquer célula rotearia pra Step 9 → reroute pra Step 10 (escala imediato).
406
- 2. Se `iteration >= auto_correct_max_iterations` (default 2) e a matriz rotearia pra Step 9 → reroute pra Step 10 (budget exhausted).
407
- 3. Se `violations[]` em `iteration N` é IDÊNTICO a `iteration N-1` → fixHint não está pegando. Reroute pra Step 10 mesmo se há budget. (Não rodar 3ª iteração quando a 2ª foi inútil.)
408
-
409
- A matriz substitui o velho heurístico "diff>50 catastrophic = skip loop". Aquele heurístico mis-firava quando build limpo tinha diff% alto por scope mismatch do comparator (Pattern #27). A matriz usa review-checks como ground truth — se o build é estruturalmente sound mas drifta visual, é território de escalação de qualquer jeito, sem precisar de threshold de diff%.
410
-
411
- ---
412
-
413
- ## Step 8 — Success path
414
-
415
- Você só chega aqui quando `review.passed === true` AND `compare.passed === true`.
416
-
417
- 1. Write/update `{output_folder}/{project-slug}/reports/index.html` com a entrada da section: status ✅, diff %, screenshot side-by-side (live element-only vs build), link pro arquivo, tempo decorrido, contagem de iterações. Use o report existente se presente (read → modify → write) pra que múltiplos runs (`/build-page` + `/clip-section` no mesmo project-slug) acumulem; crie do zero se ausente.
418
-
419
- 2. Append uma linha de decision record em `{output_folder}/{project-slug}/.sb-memory/decisions.md`:
420
- ```
421
- {ts} | /clip-section {URL} {selector} | target={target} | section-slug={section-slug} | iterations={n} | diff={diff_percent}% | ✅
422
- ```
423
-
424
- 3. **Auto-learn check.** Per-iteration prompt OK aqui — `/clip-section` é single-shot, não batch (Pattern #37 aplicado: batched só em batch flows). Só dispare se `auto_correct_iterations_used > 0` AND auto-correct destravou (i.e. um `sb-review-checks` `candidateFix` realmente desbloqueou). Inspecione o fix que fechou o diff:
425
- - É generalizável além desta section? (Nomeia pattern não coberto pela memory cascade carregada?)
426
- - Se sim: pergunte UMA vez ao usuário, em português, se persiste em `~/.claude/similarbuild-memory/anti-patterns.md`. Formato: `[s/n/sempre/nunca]`. No `sempre`, flip um one-time hint pra auto-salvar sem perguntar na próxima sessão (track em `.sb-memory/decisions.md`).
427
- - Confirmação → append entry (cria diretório/arquivo se faltam) com: data, anti-pattern name, fix recipe, source URL, selector, section type.
428
- - `n`/`nunca` → não salva. `nunca` também append `auto_learn_disabled: true` em `.sb-memory/decisions.md`.
429
- - Skip silencioso se `auto_learn_disabled: true` já está em `decisions.md`.
430
-
431
- 4. Imprima o sumário final ao usuário em português:
432
- ```
433
- ✅ section "{section-slug}" de {URL} → {output-path}
434
- selector: {selector} (type: {inspection.sectionType})
435
- diff: {diff_percent}% (threshold {threshold}%) | iterações: {n}
436
- report: {output_folder}/{project-slug}/reports/index.html
437
-
438
- Pra instalar: cole {output-path} como widget HTML / section Liquid no destino.
439
- ```
440
-
441
- 5. Pare.
442
-
443
- ---
444
-
445
- ## Step 9 — Auto-correct loop body
446
-
447
- Você só chega aqui da matriz da Step 7 quando **ambos** `review.passed === false` AND `compare.passed === false`. Os pre-checks (no-auto-correct, budget exhausted, violations idênticas) e a hard-fail do validate (`tiny-screenshot`) todos roteiam pra Step 10.
448
-
449
- `review.violations[]` está em mão da Step 6 — fixHints pra realimentar o builder.
450
-
451
- ### Step 9a — Re-build com fixHints (Skill)
452
-
453
- **Crítico: violations passam verbatim.** Per o brief: `fixHints` do `sb-review-checks` vão DIRETO pro `sb-build-{wp,shopify}` — sem reformulação, interpretação, modificação. As `candidateFix` strings SÃO o contrato.
454
-
455
- ```
456
- Skill(
457
- skill="sb-build-wp",
458
- args="inspection={inspection-path} assets-map={assets-map-path} output-path={output-path} preset=wp-elementor previous-html-path={output-path} fix-hints-path={review-report-path}"
459
- )
460
- ```
461
-
462
- (`sb-build-shopify` quando `target === shopify`, com `preset=shopify-section`.)
463
-
464
- O skill lê `previousHtmlPath`, aplica fixHints cirurgicamente, escreve em `outputPath`. Iteration counter `+= 1`.
465
-
466
- ### Step 9b — Re-validate, re-compare, re-review
467
-
468
- Volta pra Step 4 com o arquivo novo (ainda em `{output-path}` — o skill sobrescreveu) e o `{iteration}` novo. Depois Step 5, depois Step 6, depois a matriz da Step 7 de novo.
469
-
470
- Se a matriz flipa pra ✅ → Step 8 (success), agora reportando contagem de iterações.
471
- Se a matriz roteia pra ⚠️ (review passou, compare falhou) → Step 10 (sem mais fixHints actionable).
472
- Se fica em 🔄 → loop de novo da Step 9 (decrementa budget).
473
-
474
- **O loop é fechado (pattern #20):** sob nenhuma circunstância pergunte ao usuário no meio do loop. Só escala depois de exhaust.
475
-
476
- ---
477
-
478
- ## Step 10 — Escalation (auto-correct exhausted, ou `--no-auto-correct`, ou structural warning)
479
-
480
- Você só chega aqui quando o budget acabou ou a matriz roteou pra ⚠️. Seja honesto:
481
-
482
- 1. Write/update `reports/index.html` com status ⚠️ pra esta section, incluindo: último diff %, top 5 `structuredDiffs` do compare report mais recente, `violations[]` do review report mais recente, links pra todas as screenshots de iteração e diff maps.
483
-
484
- 2. Append decision record marcando esta section como ESCALATED com URL, selector, target, iterations gastas, último diff %, e qual célula da matriz disparou.
485
-
486
- 3. Imprima ao usuário em português — específico, não vago:
487
- ```
488
- ⚠️ section "{section-slug}" de {URL} ainda diverge da live após {n} tentativas.
489
- selector: {selector}
490
- diff: {diff_percent}% (threshold {threshold}%)
491
-
492
- Top diffs visuais (do sb-compare-visual):
493
- - {area}: {issue}
494
- - ...
495
-
496
- Candidate fixes (do sb-review-checks):
497
- - {candidateFix}
498
- - ...
499
-
500
- Build atual: {output-path}
501
- Diff map: {diff-map-png-path}
502
- Report completo: {output_folder}/{project-slug}/reports/index.html
503
-
504
- Próximos passos sugeridos:
505
- 1. Abra o diff map e o build no destino (Elementor/Shopify) pra inspeção visual.
506
- 2. Se achar a causa raiz fora do escopo do builder (ex: seletor pegou demais/de menos, asset errado), refine e re-rode com selector mais preciso.
507
- 3. Se o diff for aceitável (ex: drift de fonte sub-pixel), copie {output-path} pro destino e ignore o aviso.
508
- ```
509
-
510
- 4. Pare. NÃO marque como sucesso. O usuário decide.
511
-
512
- ---
513
-
514
- ## Failure modes (cross-cutting)
515
-
516
- | Symptom | Likely cause | Action |
517
- | --- | --- | --- |
518
- | `playwright` missing | First-time setup | Surface stderr da skill verbatim + "Run `npx playwright install chromium` from project root." Pare. |
519
- | `sharp` / `pixelmatch` / `pngjs` / `cheerio` / `liquidjs` missing | Idem | Surface install hint da skill que falhou. Pare. |
520
- | `inspection.widgetBlocked: true` | Bot challenge | Pare. Peça URL alternativo ou paste de HTML renderizado. Não fabrique. |
521
- | `inspection.dom` empty mas `widgetBlocked: false` | **Selector não casou nada** | Pare com mensagem específica: "Selector '{selector}' não casou em {URL}." NÃO fall-back pra full-page (fabricaria escopo). |
522
- | `assetsMap.failed[]` inclui asset crítico (hero) | Source 404 | Não pare — builder vai usar placeholder cor literal. Note no report e continue. |
523
- | `sb-build-{wp,shopify}` validator retorna erros após retries internos | Defeito do builder | Surface ao user com path do arquivo + output do validator. Skip validate/compare — não tem o que validar. |
524
- | Mesmas `violations[]` 2 iterações seguidas | fixHint não pegou — bug do builder ou memory stale | Não rode 3ª iteração mesmo com budget. Escala imediato (Step 7 pre-check #3). |
525
- | Diff% alto (ex: >50%) mas `review.passed === true` | Drift visual genuíno que LLM não patch de raw diff | Step 7 matrix já trata: `review=true, compare=false` → escala (sem fix actionable). Não reintroduza heurístico `diff>50 catastrophic`. |
526
- | Selector com espaços não quotados (ex: `/clip-section URL .a .b`) | Argv split | Mensagem da Step 0.3 cobre — peça quote: `/clip-section URL ".a .b"`. |
527
-
528
- ---
529
-
530
- ## Output structure (recap)
531
-
532
- Após um run sucesso em `https://lojax.com .ai-hero --target shopify`:
533
-
534
- ```
535
- {output_folder}/lojax/
536
- ├── clean/
537
- │ └── sections/
538
- │ └── ai-hero.liquid ← o deliverable
539
- ├── assets/
540
- │ └── a3f9b2c14e8d7f01.jpg ← content-hash, sem metadata (compartilhado se já existir)
541
- ├── reports/
542
- │ ├── index.html ← updated incrementalmente
543
- │ ├── diffs/ai-hero-0/diff-map.png
544
- │ └── validations/ai-hero-0/screenshot.png
545
- └── .sb-memory/
546
- ├── inspect-ai-hero-{ts}/inspection.json
547
- ├── inspect-ai-hero-{ts}/screenshot.png
548
- └── decisions.md
155
+ Next:
156
+ Paste section.html into the destination as a Custom HTML widget.
157
+ If diff% > threshold, open diff-map.png to see what didn't match,
158
+ edit section.html manually, or re-run /clip-section to retry.
549
159
  ```
550
160
 
551
- Nunca escreva fora de `{project-slug}/`. Cross-project asset sharing acontece só via per-user memory em `~/.claude/similarbuild-memory/` (process knowledge, não conteúdo de loja) — e só em confirmação explícita de auto-learn.
161
+ ## Failure modes
552
162
 
553
- ---
554
-
555
- ## What you do NOT do
556
-
557
- - Não rode `sb-crawl-and-list` esta command é section-only, nem tem conceito de "página seguinte".
558
- - Não invoque `/build-page` ou `/build-site` via Task tool orchestre direto.
559
- - Não pergunte nada ao usuário no meio do flow exceto o auto-learn prompt do Step 8.3 (e só quando aplicável).
560
- - Não fall-back pra full-page quando o selector não casa — escala (fabricaria escopo).
561
- - Não reformule, resuma ou interprete `candidateFix` strings — passe verbatim ao builder.
562
- - Não mostre path do build "ready to ship" antes de Step 4 + 5 confirmarem diff sob threshold.
563
- - Não passe `--crop-live-bbox` pro `sb-compare-visual` — `inspection.sectionBoundingBox` é null por design quando `--selector` é usado.
564
- - Não crie `.claude/sb-config.yaml` se faltar — opere com defaults; o installer é dono.
565
- - Não load `<plugin>/memory/*.md` se ausente — bundled `references/` em cada skill é a working source até item #13 do roadmap.
566
- - Não tente buildar múltiplos selectors por chamada — single-section é o contrato. Pra múltiplos, o usuário roda o command múltiplas vezes (ou usa `/build-page` se quiser a página inteira).
567
-
568
- ---
163
+ | Symptom | Action |
164
+ |---|---|
165
+ | `inspection.widgetBlocked: true` | Halt. Don't compose from a blocked page. Tell the user the site is behind a bot wall. |
166
+ | Render fails repeatedly (tiny screenshot, JS error) | Mark section ❌ after 2 attempts. Surface the render error. Don't ship a fragment that can't even render locally. |
167
+ | Loop hits max iterations with `diff% > threshold` | Ship best attempt + warning. The user can manually edit section.html or retry. |
168
+ | Composer flags fabrication detected | Loop continues; diff.json passes the fabrication info back as fixHint. |
569
169
 
570
- ## Quick start (smoke test path)
571
-
572
- Quando este command for primeiro wired-up, smoke test canônico:
573
-
574
- ```
575
- /clip-section https://example-store.com .ai-hero --target shopify
576
- ```
170
+ ## Non-negotiables (carried from prior iterations)
577
171
 
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`.
172
+ 1. **Pixel parity over architecture.** The reference.png is the ground truth. The diff.json is the verdict. Specifications are descriptive, never authoritative.
173
+ 2. **Mobile-first always.** Default viewport 390×844 unless explicitly overridden.
174
+ 3. **Defensive specificity.** `.scope.scope { ... !important }` on text-critical AND visual-critical properties. The theme will fight you; chained-class beats single-class.
175
+ 4. **No `100vh`.** Use `aspect-ratio` for hero proportions. Elementor iframes don't respect viewport units.
176
+ 5. **Inline SVG only.** WordPress blocks SVG uploads by default.
177
+ 6. **No fabrication.** Emit only what's in the reference screenshot AND/OR the inspection corpus. Unknown literal → TODO comment + placeholder. Never invent.
178
+ 7. **Image-first.** When a region is dominated by `<img>`, emit `<img src>` (resolved through assetsMap) — don't reproduce the image's contents in CSS/SVG.
179
+ 8. **Render every iteration.** Step 3 without Step 4 is "viajar". Don't ship without the render+diff verdict.