Back to projects
Active Started Sep 2025

Interface Built Right

End-to-end UI design tool for AI coding agents. Web/iOS/macOS archetype routing, /ibr:build orchestrator, custom CDP engine, deterministic rule + sensor layers.

TypeScript Chrome DevTools Protocol Pixelmatch Zod macOS Accessibility API

Install

claude plugin marketplace add tyroneross/RossLabs-AI-Toolkit
claude plugin install ibr@rosslabs-ai-toolkit

The Problem

AI coding agents can render a UI without knowing whether it is the right UI. Tests pass, the page returns 200, the screenshot looks plausible — and the navbar is shifted 40px, the empty state has no CTA, the iOS app violates HIG, the form has no focus indicators. A regression checker tells you something changed. It does not tell you what to build, which patterns fit the platform, or whether the result is discoverable. IBR started as the regression checker; v1.0+ is the rest.

What I Built

IBR is an end-to-end design tool for AI coding agents — design, build, and validate interfaces across iOS, macOS, and web. The validation engine that used to be the product is now the verification layer underneath a build orchestrator, platform-aware design routers, and a custom CDP engine. Works from terminal, Codex, Claude Code slash commands, or code. Zero config.

Architecture

IBR is a deterministic tool package (npm + Claude Code/Codex plugin) that a host LLM agent calls — it is not itself an LLM caller. Every scan, resolution, and diff below runs as plain TypeScript algorithms against real browser data; the only model in the loop is the calling agent (Claude Code, Codex, or whichever assistant invoked ibr). The trace below follows one page through ibr scan / ibr ask / the /ibr:build Validate step, from browser connect to verdict.

flowchart LR
  A["Host agent: Claude Code / Codex / CLI"] --> B["CDP engine: WebSocket to Chrome,<br/>or safaridriver for Safari"]
  B --> C["Hydration wait + AX-tree capture<br/>+ DOM snapshot"]
  C --> D{"assessUnderstanding()<br/>AX-tree quality score"}
  D -->|score ≥ 0.6| E["Text-only: AX tree"]
  D -->|score < 0.6| F["+ screenshot attached"]
  E --> G["Element resolution:<br/>queryAXTree → Jaro-Winkler → vision-fallback flag"]
  F --> G
  G --> H["DOM chunking: interactive/leaf elements<br/>(60-70% fewer tokens)"]
  H --> I["Tier 1: deterministic rule engine<br/>wcag-contrast, touch-targets, calm-precision"]
  H --> J["Tier 2: sensor layer<br/>visualPatterns, componentCensus, contrast, nav..."]
  I --> K["summarize.ts: structured report<br/>(~60% token cut)"]
  J --> K
  K --> L{"Baseline image exists?"}
  L -->|yes| M["Pixelmatch diff (0.1 threshold)<br/>+ verdict-policy classification"]
  L -->|no| N["Scan/ask result returned"]
  M --> O["MATCH / EXPECTED_CHANGE /<br/>UNEXPECTED_CHANGE / LAYOUT_BROKEN"]
  N --> P["Host agent"]
  O --> P
  P --> Q["iterate.ts: re-scan to confirm<br/>0 issues before claiming success"]
  Q -.approved preferences.-> R[("Filesystem memory store<br/>summary.json + preferences/ + archive/")]
  R -.injected via prompt hooks.-> A

How it works

  1. Entry — the host agent calls ibr scan <url>/ibr ask on the CLI, one of the MCP tools (scan, snapshot, compare, list_sessions served over JSON-RPC 2.0/stdio from dist/mcp/server.js), or the /ibr:build orchestrator’s Implement/Validate step.
  2. Browser connect — the custom EngineDriver opens a raw WebSocket to Chrome using Node 22’s built-in WebSocket (no Puppeteer, no Playwright dependency), or drives Safari via safaridriver + the macOS Accessibility API.
  3. Capture — waitForHydration/waitForStableTree poll for Next.js/React hydration markers and a stable accessibility tree, then the engine pulls the AX tree and a DOM snapshot.
  4. Modality decision — assessUnderstanding() scores the AX tree on text quality, semantic relevance, and structural clarity; a score ≥0.6 proceeds text-only, below that a screenshot is attached to the result.
  5. Element resolution — when a specific element is targeted by intent, resolve() runs a 3-tier lookup: queryAXTree-first exact/near match, then Jaro-Winkler fuzzy string scoring; below 0.3 confidence it sets visionFallback: true so the calling agent (not IBR) knows to look at a screenshot itself.
  6. Extraction — the full DOM is chunked down to interactive/leaf elements only, cutting 60-70% of tokens before anything reaches the model, into EnhancedElement[] with computed styles, roles, and handler data.
  7. Tier 1 checks — the deterministic rule engine runs wcag-contrast, touch-targets, and calm-precision presets as pure algorithms (WCAG 2.1 luminance math, 44px/24px sizing checks, Gestalt/Fitts/Hick rules) — zero LLM tokens, structured ruleId/severity/fix output.
  8. Tier 2 summarization — the sensor layer (visualPatterns, componentCensus, interactionMap, contrast, navigation, semanticState, oneLiners) condenses the element set into structured summaries; summarize.ts’s own docstring states the layer is “pure deterministic algorithms — no LLM calls, no npm dependencies.”
  9. Visual diff (when a baseline exists) — compare.ts runs a Pixelmatch pixel diff (0.1 color-sensitivity default) region by region, and verdict-policy.ts classifies the result as MATCH, EXPECTED_CHANGE, UNEXPECTED_CHANGE, or LAYOUT_BROKEN against provenance-tracked thresholds (each boundary in the policy carries a basis/rationale/reviewedAt, not a bare magic number).
  10. Result handoff and re-check — the scan/ask/compare result returns to the host agent; ibr ask compresses this to one verdict (~600 bytes vs. ~19KB for a full scan). iterate.ts forces a re-scan before the agent is allowed to claim success (“never claim success without a second confirmation” — credited in-code to the Anthropic harness pattern).
  11. Memory persistence — preferences the user approves are written to a filesystem-only three-tier store (summary.json hot cache under 2KB, preferences/ detail files, archive/ for evicted entries, 50-item LRU cap), Zod-validated, and re-injected into future prompts via hooks rather than re-fetched.

Models — IBR calls no LLM or vision model itself. package.json has no Anthropic/OpenAI/LangChain SDK dependency (commander, nanoid, pixelmatch, pngjs, zod only), and the summarization/rule layers are explicitly documented in-source as pure deterministic algorithms. The “vision fallback” and “screenshot needed” signals are boolean flags computed from AX-tree heuristics — they tell the calling agent (Claude Code, Codex — already multimodal) to look at a screenshot itself; IBR never performs inference. Imagegen concept generation, when used, is invoked by the host agent as an upstream step, not by IBR. This is a deliberate design choice: every verdict IBR returns is reproducible and free to run, since none of the scan/compare/rule pipeline consumes model tokens.

Tools & Infra — Custom CDP engine over Node 22’s built-in WebSocket (no Puppeteer/Playwright — smaller footprint, direct control over AX-tree polling and hydration waits). Pixelmatch + pngjs for pixel-level visual diffing, no external image-diff service. Zod for schema validation and auto-migration across config, session, and memory/preference files. Commander for the ibr CLI; nanoid for session/preference IDs. An MCP server (JSON-RPC 2.0 over stdio) exposes scan/snapshot/compare/list_sessions so Claude Code and Codex can call IBR as a tool instead of shelling out. Safari support via safaridriver WebDriver plus a native Swift binary (ibr-ax-extract, Package.swift) for macOS Accessibility API extraction; a second Swift binary (sim-driver) drives the iOS Simulator. No database anywhere in the stack — sessions, memory, and baselines are all JSON on disk under .ibr/. Distribution is an npm package (@tyroneross/interface-built-right) plus Claude Code/Codex plugin manifests; there is no server-side hosting, everything runs in the caller’s terminal or CI against a locally launched or attached browser.

/ibr:build Orchestrator

/ibr:build <topic> runs a fixed sequence:

  1. Preamble — platform, scope, design mode, archetype hints, UI template, references, density
  2. Optional imagegen concepts — generated only when useful; require explicit approval before becoming a visual-target
  3. Design Director — produces design-intent.json, specialist planning passes, target roles, and validation criteria
  4. Brainstorm & Plan — guided exploration with platform-specific rules and a concrete implementation plan
  5. Implement — Calm Precision, web/iOS/macOS routers, component patterns, data-viz guidance when needed
  6. Validate — scan, match wireframe / visual targets, test interactions, iterate until passing

The orchestrator is the one place that knows the active design system, the reference templates, and the verification gates — so an agent calling it cannot skip the design step on the way to a passing test. Specialist passes cover flow, visual system, interaction states, content states, Mockup Gallery targets, data visualization, and validation. IBR does not spawn tiny per-atom agents; those stay inside component patterns and design tokens.

Two-Tier Architecture

Part of the verification-gated agent-work pattern (see /toolkit/patterns/verification-gated-agents) — here: a two-tier scan (zero-token deterministic rule engine + structured sensor summaries) feeds a Pixelmatch diff at a 0.1 threshold, classified MATCH / EXPECTED_CHANGE / UNEXPECTED_CHANGE / LAYOUT_BROKEN, before an agent-driven UI change is considered done.

IBR scans return structured data, not raw element dumps. Two layers run on every scan.

Tier 1 — Deterministic Rule Engine (no LLM, zero tokens). Pure algorithms against runtime data:

Rule PresetWhat It ChecksAlgorithm
wcag-contrastText contrast ratios, AA and AAAWCAG 2.1 relative luminance
touch-targetsInteractive element sizing44px mobile (WCAG 2.5.5), 24px desktop (WCAG 2.5.8)
calm-precisionGestalt, Signal-to-Noise, Fitts, Hick, Content-Chrome, Cognitive LoadPrinciple-based checks

Tier 2 — Sensor Layer (structured summaries). Pre-computed so the model focuses on judgment, not pattern re-discovery:

SensorWhat It Produces
visualPatternsGroups elements by style fingerprint per category
componentCensusTag/role counts + orphan cursor:pointer elements with no handler
interactionMapWhich interactive-looking elements actually have handlers
contrastWCAG pass/fail grouped, only failures listed
navigationLink structure with depth and counts
semanticStatePage intent, states, available actions
oneLiners5-second scannable summary lines

ibr scan --output summary cuts roughly 60% of tokens.

Platform Routers

Webweb-design-router classifies into seven archetypes (SaaS dashboard, data/research tool, editor/workbench, AI agent chat, commerce/checkout, content/publication, internal admin). Each archetype sets defaults for navigation, density, primary content, mobile behavior, and validation focus.

iOSios-design-router classifies into six archetypes — Utility, Content/Feed, Productivity, Consumer/Habit, Editorial, Tool/Pro — and routes to matching reference files. A meditation app and a developer tool both ask “what navigation pattern?” and get different correct answers without the agent guessing. Sits next to ios-design (HIG rules: navigation, color, type, motion, SF Symbols, materials, Liquid Glass) and apple-platform (architecture, SwiftData, Watch connectivity, concurrency, CI/CD, TestFlight — folded in from the standalone apple-dev skill, which is now deprecated).

Reference library — seven domain files lifted from the Calm Precision iOS design system, loaded on demand by archetype rather than all at once: navigation, lists and cards, buttons, color and typography, motion and states, task economy, and the archetype catalog the router reads from.

CDP Engine

The browser layer is a custom Chrome DevTools Protocol engine over WebSocket. No Playwright, no Puppeteer, no heavyweight automation dependency. Built-in LLM-native features the legacy stack could not do cleanly:

FeatureWhat it does
queryAXTree-first resolutionFind elements by semantic name + role, not fragile CSS selectors. 3-tier: queryAXTree search → Jaro-Winkler fuzzy → vision-fallback flag (returned to the calling agent, not resolved by IBR)
DOM chunkingFilter to interactive/leaf elements; 60–70% fewer tokens
Adaptive modalityScores AX tree quality. High → text data; low → include screenshot. Vision only when needed
Resolution cacheSame intent → element query twice = instant; clears on navigation
observe / extract / interactPreview available actions, pull structured data, click/type by accessible name
Hydration waitingDetects Next.js/React markers + polls AX tree until stable — eliminates “0 elements” on modern SPAs

Safari support via safaridriver + macOS AX API; cross-browser diff via ibr compare-browsers.

Verification, Still

Comparison did not go away — it became a step inside the loop instead of the whole product. Capture a baseline, run the change, diff with Pixelmatch at a 0.1 threshold, classify the result as MATCH, EXPECTED_CHANGE, UNEXPECTED_CHANGE, or LAYOUT_BROKEN, and feed the verdict back into the iterate phase. Landmark detection extracts the accessibility tree on capture so the verdict carries page intent (auth form, list view, dashboard) rather than just a pixel count.

Memory

A three-tier preference store keeps the agent’s design context small and current: summary.json as a hot cache under 2KB, a preferences/ directory with full details, and an archive/ for evicted entries. Cap is 50 active preferences; the least recently used drops to archive when a 51st arrives. Prompt hooks inject relevant baselines and preferences into the agent’s context before a UI change instead of asking it to fetch them. Zod schemas validate every preference file and auto-migrate old shapes when the format changes.

Tech stack

TypeScript, custom CDP engine (Node 22 built-in WebSocket, no Puppeteer/Playwright), Pixelmatch + pngjs (visual diff), Zod (schema validation), Commander (CLI), nanoid (IDs), MCP server over JSON-RPC 2.0/stdio, Safari via safaridriver + native Swift AX extraction (macOS Accessibility API), filesystem-only session/memory store (no database), npm + Claude Code/Codex plugin distribution (no server hosting).

Results

Token reduction is the only measured efficiency figure in this build; the numbers below are the ones already reported per-feature elsewhere in this doc, consolidated here.

MechanismToken Reduction
ibr scan --output summary (sensor-layer summarization vs. raw dump)~60%
DOM chunking (filter to interactive/leaf elements before AX resolution)60–70%

Verification accuracy (MATCH/EXPECTED_CHANGE/UNEXPECTED_CHANGE/LAYOUT_BROKEN classification correctness) and design-quality delta (pre/post Calm Precision + archetype routing) — ⚠️ no benchmark yet.

Why the Repositioning

IBR already had build orchestration, design-system enforcement, and platform skills. Calling it a testing tool described one slice and left the other three under-marketed and under-used. The new framing — design-first, verified end to end — matches what the plugin actually does on a real build, and gives the iOS work, the apple-platform integration, and the CDP engine a coherent place to live.