Skip to content

Tooling

Nick edited this page Apr 29, 2026 · 4 revisions

Tooling

All scripts live in bin/ at the monorepo root. They accept a positional theme name, default to the cwd if it contains a theme.json, and most support --all to operate on every theme. Every script also responds to --help.

# Run the full check suite against one theme
python3 bin/check.py obel --quick
python3 bin/check.py chonk --quick

# Run it against every theme
python3 bin/check.py --all --quick

# Rebuild a theme's INDEX.md
python3 bin/build-index.py obel

# Inspect tokens
python3 bin/list-tokens.py --theme obel colors
cd obel && python3 ../bin/list-tokens.py colors    # cwd-detected

# Clone Obel into a new theme variant (sibling of obel/ inside the monorepo)
python3 bin/clone.py acme

The full toolchain

Script What it does
check.py Runs every project check (JSON, PHP, blocks, tokens, AI fingerprints, contrast, hex/important allow-lists, …). Use this before every commit. Pass --visual to also run the snap-gated visual regression sweep.
build-index.py Regenerates a theme's INDEX.md (token map, template list, block style list).
validate-theme-json.py Validates a theme's theme.json block names against the live Gutenberg + WooCommerce sources.
list-templates.py Prints every template the theme could ship and the URL it serves.
list-tokens.py Inspects design tokens defined in theme.json.
clone.py Scaffolds a new sibling theme from Obel with the names rewritten.
seed-playground-content.py Populates <theme>/playground/{content,images}/ from the canonical W&O source, rewriting image URLs to point at the new theme.
sync-playground.py Inlines playground/*.php into every theme's playground/blueprint.json and rewrites the importWxr URL to the per-theme content.xml.
personalize-microcopy.py Re-runnable, idempotent voice pass. Rewrites every cloned-from-Obel visible string into the target theme's brand voice using a per-theme substitution map; refuses to start if any replacement would cascade. Required after clone.py so check_all_rendered_text_distinct_across_themes passes.
build-theme-screenshots.py Generates the WP-admin Themes-card screenshot.png for one theme (or --all) by cropping the latest snap baseline (or fresh tmp/snaps/ shot) to the WordPress 1200×900 spec. Required after every clone.py so check_theme_screenshots_distinct doesn't fail on the byte-identical Obel placeholder.
build-redirects.py Regenerates docs/<theme>/<page>/index.html short URLs that GH Pages serves at demo.regionallyfamous.com/.
append-wc-overrides.py Generates and appends WooCommerce override CSS chunks (the sentinel-bracketed Phase A through Phase K chunks) into each theme's theme.json styles.css. Re-runnable; idempotent.
install-hooks.py Wires core.hooksPath to .githooks/ and smoke-tests the gate so the local pre-commit + pre-push checks fire on every commit and push. Run once on a fresh clone.
snap.py Visual-snapshot framework. Boots WordPress Playground locally for a theme, captures Playwright screenshots + DOM heuristics + axe-core a11y + INSPECT measurements + scripted interactive flows across every (route × viewport) defined in bin/snap_config.py, diffs against committed baselines under tests/visual-baseline/, and emits a tiered pass / warn / fail gate. Subcommands: doctor, shoot, serve, diff, baseline, report, check. See Visual Snapshots.
snap-vision-review.py Pixel-level design critique. Hands each snap PNG + the theme's design-intent.md rubric to Claude, receives vision:* findings (overpowered hero, flat hierarchy, content orphans, etc.), and merges them into *.findings.json so they triage identically to axe violations. Daily $ budget tracked in tmp/vision-spend.jsonl.
lint.py Single-entry runner for ruff format --check, ruff check, mypy, plus a node --check smoke on the blocks validator. Parallelizable; CI uses the same entry.

Pipeline / theme factory

Scripts that together form the five-stage factory: Ideate → Design → Verify → Self-heal → Ship. See the How it works overview.

Script What it does
concept_seed.py The concept catalogue — one Python list of concept recipes (palette hex, typography, voice tags) that drives the public concept queue.
paint-mockup.py Turns a concept seed into the two-view browser mockup PNG at a fixed 1376×768 aspect. Emits an AI-image prompt deterministically interpolated from the concept's palette + typography + tags (never hand-written).
concept-to-spec.py Distills a concept into a machine-readable tmp/specs/<slug>.json ready for design.py. Default path uses an LLM for voice/typography choices; --no-llm is a deterministic fallback.
design.py The deterministic spine. Two subcommands: build --spec X.json runs the structural phases (clone Obel → swap tokens → seed Playground → sync blueprint → first check.py pass) and exits 0 when the theme renders. dress <slug> runs the content-fit phases (photos, microcopy, front-page restructure, vision review, check --phase all) and exits 0 only when green.
design-batch.py --from-concepts N-at-a-time wrapper around design.py. Pulls unbuilt concepts from concept_seed.py, builds each in its own branch, auto-pushes and opens a PR with --auto merge enabled. Embeds verify-theme.py verdicts in the PR body.
design-watch.py Human-friendly observer. Writes a live STATUS.md to the worktree as design.py / design-batch.py works, with per-phase timings and a ranked list of current blockers. Pass --auto-unblock to invoke the self-healing loop.
design_unblock.py Self-healing loop. Classifies blockers into stable categories (microcopy-duplicate, hover-contrast, photo-collision, vision-overpowered, …), builds an evidence packet (relevant file slices + the exact rule that fired + rendered screenshot), hands it to Claude under strict guardrails (no edits outside the theme slug, no !important), applies the proposed patch, and re-runs the affected verification ladder. Every attempt is recorded to repair-attempts.jsonl for audit.
verify-theme.py --strict Reproduces CI's theme gate locally against a pushed branch. Reports branch-ready / static-check / snap-shoot / evidence-check phases as JSON or markdown.
visual-matrix.py Resolves "is this PR adding a brand-new theme?" from git — consumed by the vision-reviewed required for new themes gate in CI.
triage.py Fast issue-triage helper. Reads the latest findings.json tree and prints a ranked list of the loudest fixes pending, bucketed by theme.
audit-concepts.py Cross-checks mockups/ against bin/concept_seed.py::CONCEPTS and the GH Pages concept queue. Surfaces drift (a concept without a mockup, or vice versa).
check-concept-similarity.py Flags near-duplicate concepts via perceptual hashing, with human-approved pairs allowlisted in bin/concept-similarity-allowlist.json.
build-concept-meta.py Builds <slug>.meta.json sidecars for every concept from the concept seed.
build-theme-status.py Rebuilds docs/themes/index.html (the shipping dashboard) from every theme's readiness.json.

Docs

Script What it does
build-redirects.py Regenerates docs/<theme>/<page>/index.html short URLs that GH Pages serves at demo.regionallyfamous.com/. Also regenerates the concept queue docs/concepts/index.html from mockups/ and preserves the human-authored .md files in PRESERVED_FILES.
build-brand-assets.py Rasterizes docs/favicon.svg to PNG/ICO and renders the Open Graph share card.
build-snap-gallery.py Regenerates docs/snaps/ (the retina baseline gallery) from the committed tests/visual-baseline/ tree.
sync-wiki.py Mirrors docs/*.md into the GitHub wiki as Title-Case pages, rewriting ./other-doc.md links to wiki page links and ../AGENTS.md references to absolute GitHub URLs. Refreshes Home.md + _Sidebar.md. Dry-run by default; pass --apply to commit + push.

The pre-commit gate

bin/check.py is the load-bearing single command:

python3 bin/check.py --all              # every static check, every theme
python3 bin/check.py --all --quick      # skip slow checks (link probes, etc.)
python3 bin/check.py --visual           # also run snap-gated visual sweep
python3 bin/check.py chonk              # one theme
python3 bin/check.py chonk --visual --visual-scope=quick   # smoke test

Static checks include (non-exhaustive):

  • theme.json is valid JSON and every block name resolves against the live Gutenberg + WooCommerce block registries.
  • No !important outside the explicitly allow-listed sentinel-bracketed chunks.
  • No raw hex colors outside the palette (with the same allow-list mechanism for intentional gradient stops).
  • Color palette meets WCAG AA contrast (4.5:1 normal text, 3:1 non-text) against every base/surface combination.
  • Hover/focus states stay legible (the "accent collapses to invisible against base" footgun).
  • WC card surfaces aren't horizontally zero-padded (the GRID_FIX regression).
  • WC totals blocks have explicit padding (Phase H is intact).
  • Distinctive chrome present (no theme falls back to plain WC defaults).
  • INDEX.md is in sync with current theme.json.
  • No AI fingerprints (the "I cannot fulfill" / "as a language model" sweep) in any rendered template.
  • No two themes ship identical screenshot.png bytes (check_theme_screenshots_distinct — fires when a freshly-cloned theme still has Obel's placeholder; fix by re-running bin/build-theme-screenshots.py <theme>).
  • No two themes ship the same user-visible string (check_all_rendered_text_distinct_across_themes); the WC microcopy maps must also be distinct across themes (check_wc_microcopy_distinct_across_themes).
  • Every theme's Playground blueprint has its content payload seeded (check_playground_content_seeded).
  • The comments.html template-part renders one core/comments-title (no hand-typed second <h3>Comments</h3>) and playground/wo-configure.php ships the comment_author_email backfill so identicons hash distinctly.

The hooks layer

bin/check.py runs in three places, each closing a different bypass:

  • .githooks/pre-commit — runs the gate on every git commit.
  • .githooks/pre-push — re-runs the gate AND a bin/append-wc-overrides.py drift check on every git push (catches --no-verify commits).
  • .github/workflows/check.yml — server-side, on every PR and push to main (authoritative).

The local hooks only fire if git config core.hooksPath points at .githooks/; that's not on by default for a fresh clone, so run python3 bin/install-hooks.py once after cloning.

When in doubt, read the failure message — every check explains what it found and links to the file path / line / rule that fired.

Clone this wiki locally