workbench-builder — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited workbench-builder (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
| File | When to load |
|---|---|
references/orchestrator.md | Running the pipeline — the five phases, the subagent prompt shape, check-in cadence, stuck detection |
references/write-protocol.md | Canonical write-protocol block, copied verbatim into every phase worklog and subagent prompt |
references/architecture.md | The stack contract — SSE-as-invalidation, the fan-out, useRegion, the two-way loop, gotchas |
references/dependencies.md | The pinned dependency set, Astryx grounding commands (bunx astryx component), the Graphite theme |
references/rendering.md | Markdown/mermaid/ECharts patterns, the palette pairing rule, disclosure, overflow guards |
references/recipes.md | The recipe catalog — one data model + layout per workbench type, with the unique value of each |
templates/server.ts | Backend scaffold — schema, SSE fan-out + publish(), region queries, mutation + loop endpoints |
templates/src/ | Frontend scaffold — main.tsx, App.tsx, useRegion.ts, charts.ts, region components |
templates/index.html | Fullstack entry Bun.serve imports — the whole "no bundler" trick is this one file |
templates/package.json | The exact-pinned dependency manifest copied into every workbench |
templates/graphite-theme/ | Canonical Graphite theme — defineTheme source, built CSS/JS, the @font-face fallback |
templates/terminal-helper.ts | Zero-dependency bun script the terminal/agent runs to act on shared state and close the loop |
templates/wait-for-work.ts | Wake-on-work watcher — blocks on the SSE stream, exits with a digest when the human hands off work |
templates/worklog-skeleton.md | Per-phase worklog with the write-protocol block embedded and a Status: IN PROGRESS line |
One skill, one pipeline. Takes a coding/eval/PR/data task → a running localhost workbench through five phases. The heart of the work is Phase 0: deciding what KIND of workbench this is and, more importantly, what the shared state / data model is. The data model is the design — everything downstream (regions, components, loop endpoints) falls out of it. Five browser-verified reference implementations ship with the skill: the eval viewer (${CLAUDE_PLUGIN_ROOT}/skills/workbench-builder/eval-viewer/), the document-review / redline surface (${CLAUDE_PLUGIN_ROOT}/skills/workbench-builder/doc-review/), the multi-PR review room (${CLAUDE_PLUGIN_ROOT}/skills/workbench-builder/pr-workbench/), the grid / spreadsheet triage surface (${CLAUDE_PLUGIN_ROOT}/skills/workbench-builder/grid-workbench/), and the multi-surface triage queue (${CLAUDE_PLUGIN_ROOT}/skills/workbench-builder/triage-workbench/); the templates here are generalized from them.
flowchart LR
A[Phase 0<br/>Route<br/>kind + data model] --> B[Phase 1<br/>Scaffold backend<br/>server.ts]
B --> C[Phase 2<br/>Build UI<br/>src/ on Astryx]
C --> D[Phase 3<br/>Wire the loop<br/>terminal scripts]
D --> E[Phase 4<br/>Verify<br/>headless browser]
E --> |Console error / no live update| C
E --> F[Run it<br/>bun --hot server.ts]Phase 0 is inline orchestrator work — name the workbench type and write the data model. Phases 1–3 each own one artifact and can run as a general-purpose Agent against references/orchestrator.md; for a single-surface workbench it is faster to run them inline in sequence. Phase 4 is non-negotiable and runs a real browser, because curl cannot see a guessed component prop, a missing chart registration, or layout overflow. Full runbook with prompts, check-in cadence, and stuck detection lives in references/orchestrator.md.
The recipe is a different data model + layout over the same stack. Route on the verb the user reached for.
| User signal | Recipe / scope |
|---|---|
| "eval viewer" / "show me my judge runs" / "pass/fail board" | Eval viewer — evals + runs + events; outcome badges, run-history chart — BUILT |
| "review this doc" / "let me redline this" / "select text and comment" | Document review / redline — annotations; char-perfect span anchoring — BUILT |
| "PR review room" / "review these N PRs" / "which order do I merge" | PR review room — prs + pr_files + concerns; collisions → merge order — BUILT |
| "spreadsheet" / "data cleanup" / "triage or fix these rows and cells" | Grid / spreadsheet triage — rows + cells_log; click-to-edit, actor audit — BUILT |
| "triage my inbox" / "what needs my attention" / "email/Slack/Asana" | Multi-surface triage — items across sources; mark-handled reply detection — BUILT |
| "trace replay" / "step through this agent run" / "what did the agent do" | Agent trace replay — steps timeline + tool-call detail |
| "refactor cockpit" / "track this big refactor" / "what's left to touch" | Refactor cockpit — targets + edits + progress over modules |
| "prompt lab" / "skill lab" / "compare these prompt variants" | Prompt/skill lab — variants + cases + scores |
| "decision board" / "ADRs" / "log our architecture choices" | ADR board — decisions + options + status |
| "incident timeline" / "build the postmortem timeline" | Incident timeline — events ordered, severity lanes |
| "migration planner" / "plan this migration in waves" | Migration planner — items + waves + dependency edges |
| "build me a little UI for this <task>" | Route to the closest recipe; if none fits, design a fresh data model |
If you cannot name the shared state in one sentence, stop and frame it before scaffolding — see references/recipes.md. Run end-to-end with no approval gates when the ask is clear; the whole thing is disposable and bound to 127.0.0.1, so there is nothing to roll back. Use one AskUserQuestion only when the workbench type or the central data model is genuinely ambiguous.
Each phase writes its artifact to disk as it goes — one unit of thought → edit the file → next unit. Partial work on disk survives timeouts and context pressure; state held in working memory does not. The canonical block lives in references/write-protocol.md and is copied verbatim into every phase worklog (templates/worklog-skeleton.md) and every subagent prompt — one source of truth, no paraphrasing. The load-bearing artifacts are server.ts, the components under src/, and the terminal helper(s) under scripts/. Nothing else is.
Bun.serve + bun:sqlite + React 19 + Astryx, bound to 127.0.0.1, run with bun --hot server.ts. Zero build config: import homepage from "./index.html" makes Bun transpile TSX/CSS on demand, and --hot gives HMR while the UI is reshaped mid-session. One bun install against an exact-pinned package.json is the only setup. The capability comes from how the pieces compose:
event: <region>\ndata: stale). One module-level EventSource feeds the useRegion hook; each named event refetches exactly that region's JSON from /api/regions/<region> and React re-renders. A publish(...regions) fan-out backs it server-side. The region name must be identical in three places — publish(), the wire, useRegion() — or the panel silently freezes. (See references/architecture.md.)bun run scripts; both hit the SAME JSON endpoints over one SQLite file. The human→agent channel — a requests table plus /claude/queue (pull is claiming) and /claude/respond — closes the loop so the human steers and the agent answers, both watching the same state live.templates/wait-for-work.ts runs as a background command and exits with a work-order digest when the human clicks "Hand batch to agent" (immediate) or stops editing for a quiet period (debounce). An agent_watermark cursor over the actor-attributed event log makes each batch exactly-once and keeps the agent's own writes from waking it. (See references/architecture.md.)bunx astryx component <Name> --json (offline, reads the installed manifest). Astryx is beta — a guessed prop renders nothing or throws.frontend-design. This skill ships a 127.0.0.1, no-auth, throwaway surface — it is the opposite of production.bun --hot server.ts is the entire toolchain. Adding a bundler is pure ceremony here.requests table + /claude/queue + /claude/respond — or you have built the wrong thing.event: <region>\ndata: stale, nothing more. The instant you serialize state into the event payload you have a second source of truth and a client-side state model to keep in sync. Keep events as pure invalidation; let the refetch carry the data.Collapsible takes trigger not label; TextArea's handler is changeAction not onChange. Confirm every component against bunx astryx component <Name> — the manifest is offline and version-exact.^ range can pull a breaking release mid-session. Pin exact versions and commit bun.lock (see references/dependencies.md).networkidle never fires. Navigate with domcontentloaded, then wait on a region's data-testid selector (React content arrives after hydration + the first fetch).bun:sqlite is synchronous and the server is one process — one module-level connection, WAL on, is the whole data layer.min-width:auto and refuse to shrink below their content. Put min-width:0 on every column that renders markdown, tables, or diagrams.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.