# Agent brief — GATED reconcile (objective coord-diff gate as the done-criterion)

Reconcile ONE CFS page to Figma so it matches, **section by section, desktop then mobile**, and prove
it with the deterministic gate `tools/coord-diff.py` — NOT your own opinion. A section is done only when
its gate output is **clean OR every remaining line has a one-line documented reason**.

Read FIRST: `.claude/skills/reconcile-section/SKILL.md` (the law) and `docs/_agent-reconcile-brief.md`
(the **browser-queue protocol** — acquire a slot before any browser use, release at end).

## The gate (this is what "done" means)
`tools/coord-diff.py` walks a Figma node's leaves (text geometry+font+visibility+inline-emphasis runs
AND visual leaves: images + icons) and diffs them against a DOM dump. It flags POS/FONT/MISSING/
HIDDEN-RENDERED/EXTRA/MISSING-EMPHASIS/VIS-POS/VIS-SIZE/VIS-MISSING/VIS-EXTRA. It can't be fooled —
measure-by-hand sampling is what missed bugs before; this enumerates EVERYTHING.

## Per-section loop (repeat until clean-or-explained, then commit)
1. **Map the section** to its Figma node id(s) and our DOM selector. `get_node_outline(<frame>)`, sort
   children by bbox.y. NOTE flattened pages: headings may be separate sibling nodes and a "section" may
   span several Figma nodes / more than one DOM block (e.g. a hero whose node includes an overlapping
   card). Pick the Figma node whose bbox covers the section; scope the DOM selector to match it.
2. **Save the Figma node JSON to disk**: call `get_node(<id>, depth 6)` and Write the EXACT raw JSON to
   `/tmp/fig-<id>.json` (no edits).
3. **Dump our DOM** for the matching selector (acquire a browser slot first):
   ```
   B="$HOME/.claude/skills/gstack/browse/dist/browse"   # only if you got the `browse` slot
   $B viewport 1440x2000; $B goto "http://localhost:8778/html/<page>.html"
   $B js "$(sed 's/__ROOT__/<selector>/' tools/dom-dump.js)" > /tmp/dom-<sec>.json
   ```
   (Playwright/Chrome slots: run the same JS via browser_evaluate / evaluate_script and write its
   return to /tmp/dom-<sec>.json.)
4. **Run the gate**: `python3 tools/coord-diff.py --figma /tmp/fig-<id>.json --dom /tmp/dom-<sec>.json --tol 6 --vtol 8`
5. **Fix every flagged line**, by class:
   - **POS/FONT/VIS-POS/VIS-SIZE** → adjust the element's geometry/typography to the Figma number.
   - **HIDDEN-RENDERED** → the design hides that node (`visible:false`); REMOVE it from our render.
   - **MISSING** → build the missing Figma-visible leaf.
   - **MISSING-EMPHASIS** → add class-free inline `<strong>`/`<u>` inside the `<p>` for that run
     (CMS-safe; never put a class on `<p>`).
   - **VIS-MISSING / wrong icon-or-image** → **EXTRACT THE REAL FIGMA ASSET**, don't approximate:
     image fill → `get_image_to_disk(hash, outDir=assets/images/<page>/...)`; vector/icon →
     `get_screenshot_to_disk(<id>, format:SVG)` then copy into assets; wire it at the Figma size.
   - **EXTRA** → either remove it, or document the reason (e.g. breadcrumb modeled as one Figma node
     but split into semantic crumbs — keep, note it).
   - Genuinely-explainable residuals (Figma authoring artifacts, breadcrumb-split, sub-tolerance
     glyph-width) → keep + write a one-line reason. Everything else gets fixed.
6. **Re-dump + re-gate** until clean-or-explained. THEN trigger any hover/interaction states and prove
   they fire (computed style), per the skill.
7. **Commit** that section. Append the section's final gate output + your triage to
   `docs/inspection/<page>-gated.md`.

## Interactive / multi-variant components (the gate CANNOT catch this — do it manually)
`coord-diff.py` compares ONE rendered state to one Figma node. It will NOT tell you a section is a
static mock of an interactive component. So for EVERY `INSTANCE` in a section:
- check its `componentSetId` (get_node) and run `get_variant_set(<setId>)`.
- If it has **>1 variant** (e.g. a stepper with a variant per step, a tab set, a carousel) OR any
  node has `reactions` (ON_CLICK/ON_HOVER), it is INTERACTIVE — you must build **every state** (pull
  each variant's content) and **wire the interaction** in markup + js/main.js, then verify switching
  works (click → state changes, read it back). Building only the default variant = a defect, even
  though the gate shows that one state "clean". Real example: blog "Step-by-Step Guide" (Component 159)
  was a 5-variant stepper shipped as a static step-1 mock.

## Then MOBILE
Repeat the whole loop at `viewport 393x852` against the MOBILE frame nodes (often a different structure).
Confirm `document.documentElement.scrollWidth <= 393`.

## Rules
- Bare `<p>` (CMS) — typography on the wrapper or a cms-* class; inline emphasis via class-free strong/u.
- Tailwind CDN arbitrary values. Dynamic tab counts must wrap (stress-test).
- Respect `visible:false` — never render a layer the design hides.
- Never eyeball a screenshot to derive a number — the gate + IR are the source of truth.
- Commit per section. Return a concise summary: sections done, per-section final gate status, real
  assets extracted, residuals/reasons, open client questions. RELEASE your browser slot at the end.
