L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Design et création · SKILL.md

skeleton-extractor

skeleton-extractor

Use when transforming an HTML mockup into a draft design-skeleton.v1 YAML. Walks the DOM via puppeteer-core at 3 breakpoints, extracts bboxes + computed styles (resolved back to declared tokens) + wired interaction handlers, and emits one draft ready for visual-architect review. Runs the Node subprocess under trusted_runner discipline (CB3-compliant: truste…

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: skeleton-extractor description: "Use when transforming an HTML mockup into a draft design-skeleton.v1 YAML. Walks the DOM via puppeteer-core at 3 breakpoints, extracts bboxes + computed styles (resolved back to declared tokens) + wired interaction handlers, and emits one draft ready for visual-architect review. Runs the Node subprocess under trusted_runner discipline (CB3-compliant: trusted runner owns execution, skill only generates draft). Invoked by visual-architect at design-phase freeze; NEVER writes the frozen skeleton itself." disambiguation: Turns an HTML mockup into a DRAFT skeleton by measuring the DOM. Reviewing and FREEZING that skeleton at design time is visual-architect.

skeleton-extractor

Transforms an HTML mockup into a pre-freeze draft design-skeleton.v1 YAML. One skill, one operation (extract), one subprocess, one stdout JSON blob.

When to invoke

Called by visual-architect at design-phase freeze (see design doc §2.5), after the user has approved an HTML mockup. The draft it emits is reviewed by the user, amended (binds_to URIs, bbox confirmations, unresolved-token decisions), then HMAC-signed and written to .design-ledger/skeletons/<screen>.yaml. Skeleton-extractor does NOT freeze — that is visual-architect's job.

When NOT to invoke

  • After the skeleton is frozen — verification is visual-arbiter's job (pure-Python verdict on stdout, CB4).
  • For micro-drift re-approval — that is design-drift-arbiter's job.
  • As a runtime live probe on the built product — subprocess targets only the approved mockup.

Public API

A single op.

extract

python3 skills/skeleton-extractor/scripts/extract.py \
  --mockup /abs/path/to/mockup.html \
  --out    /abs/path/out.draft.yaml \
  [--breakpoints 420,700,1280] \
  [--tokens-path /abs/path/to/index.yaml]

Arguments:

FlagRequiredDefaultMeaning
--mockupyes—Absolute path to the HTML mockup (file read via file:// URL)
--outyes—Where the draft YAML is written (trusted_runner.atomic_write_bytes)
--breakpointsno420,700,1280Comma-list of viewport widths; one extraction pass per width
--tokens-pathnononeOptional path to index.yaml with declared tokens: block, used for getComputedStyle back-resolution
--timeoutno120Node subprocess timeout seconds

Output shape

Draft YAML conforming to design-skeleton.v1 (§2.3):

schema: design-skeleton.v1
draft: true
generated_by: skeleton-extractor@1.0.0
generated_at: "2026-04-24T00:00:00Z"
breakpoints: [420, 700, 1280]
fonts_loaded: true
fonts_ready_max_ms: 412
elements:
  - id: grid_card_nth_of_type_1
    selector: "#grid .card:nth-of-type(1)"
    role: article
    kind: element
    bbox:
      mobile:  {x:16, y:96,  w:388, h:260}
      tablet:  {x:24, y:116, w:320, h:240}
      desktop: {x:48, y:136, w:384, h:220}
    tokens_used:
      background_color: "token://color.paper"
      border_color:     "token://color.ink"
    tokens_raw_per_bp: { ... }           # preserved for the arbiter's audit trail
    interactions:
      - {event: click, binds_to: null}   # user fills during visual-architect freeze
    shadow_dom_opaque: false
unresolved_tokens_report:
  - {selector: ".banner", field: color, computed: "#ac3b3b", breakpoint: desktop}
concerns: []

Flow (HTML mockup path → subprocess → draft YAML)

  1. Python wrapper (scripts/extract.py) parses args, loads declared tokens, checks that /bin/google-chrome and the Node binary exist.
  2. Wrapper spawns node skills/_meta/skeleton_extractor.mjs with a sanitized env (mirrors trusted_runner._run_pytest), a 120s timeout, and a single JSON stdin payload {mockupHtml, breakpoints, tokens}.
  3. Node subprocess (_meta/skeleton_extractor.mjs, ~300 LOC) launches puppeteer-core pointed at /bin/google-chrome. For each breakpoint: setViewport before page.goto(file://mockup, {waitUntil: "networkidle0"}), then await document.fonts.ready + 300ms settle (font-race guard, §2.10). Walks visible elements with role/data-*/event-handler, emits per-element {selector, role, bbox, tokens_raw, interactions, shadow_dom_opaque}.
  4. Node resolves computed tokens_raw back to declared tokens (§2.4 step 4). var(--accent-sun) chains resolve through :root. First exact match wins; unresolved values are surfaced in unresolved_tokens_report — never auto-added to index.yaml (D2 strict).
  5. Node merges per-breakpoint elements by stable selector into one record with a bbox: {mobile, tablet, desktop} map.
  6. Node emits ONE JSON blob on stdout; exit 0 on success.
  7. Python wrapper parses the JSON, serializes to YAML, and writes via trusted_runner.atomic_write_bytes. No partial writes, no stream output, no ledger writes from inside the subprocess.

CB3 compliance

The subprocess is CB3-compliant: trusted_runner (invoked from the Python wrapper) owns subprocess execution and captures stdout; the skill/subprocess only generates a draft. The final persisted file goes through atomic_write_bytes (same helper bob uses for bundles and claims).

Edge cases (§2.10)

Edge caseHandling
Font-loading racefonts.ready + 300ms settle; >2s = warning concern; >5s = blocker concern + fonts_loaded: false
Viewport-unit CSS (vw/vh)setViewport always called BEFORE goto() — deterministic per breakpoint
Pseudo-elementsWalked by DOM iteration; current version records host element tokens only; ::before/::after explicit slots are v2
Shadow DOMOpen roots recursed; closed roots → shadow_dom_opaque: true marker on host (arbiter treats as black box)
Responsive accident vs intentbbox deltas between neighboring breakpoints are recorded but NOT auto-flagged; visual-architect reviews
Animations / transitionsStatic after-settle state only; must_satisfy.known_deferred: [animations] is visual-architect's concern

Failure surface

ConditionExitObservation emitted (category, fingerprint)
/bin/google-chrome missing3external_tool_fail, chrome_absent
node binary missing2external_tool_fail, node_absent
Subprocess non-zero exit2external_tool_fail, exit-<N>
Subprocess exceeds 120s2external_tool_slow, timeout-120s
Malformed stdout JSON2external_tool_fail, malformed_output

All observations are fail-open: claude_observe import errors, runtime errors inside the observation writer, or env-adoption gaps never block the caller. The Python exception still propagates so bob/visual-architect sees the authoritative failure.

Determinism / caveats

  • The extractor output is deterministic given identical inputs (same mockup bytes, same Chrome version, same declared tokens). Networked mockups are out of scope — waitUntil: "networkidle0" is for same-origin static assets only.
  • The draft is unsigned. Only visual-architect signs after user review.
  • Token resolution uses first-match-wins on the flattened token index; declared aliases that collide are a user error surfaced at freeze time, not extraction time.

References

  • Design doc: /path/to/project/docs/plans/2026-04-23-ecosystem-keystone-design.md (§2.3, §2.4, §2.10)
  • Contract map: progress/contract-map.yaml skeleton-extractor component (TS-SE-01..04)
  • Precedent: /path/to/projects/test_flow/test-driver.mjs (puppeteer-core invocation pattern)
  • Subprocess: ~/.claude/skills/_meta/skeleton_extractor.mjs
  • Tests: ~/.claude/skills/skeleton-extractor/tests/
Skills enregistrées