OR Key
drop another .md file to compare - side-by-side diff against shape-builder

shape-builder

personal 2 files 10 recent evals

What it does for you

This skill does one job for you, the same careful way every time.

What it produces

A recent result, so you can see the kind of work it returns.

loading…

How to get it

These run inside the Snappy workspace. Want this working in your business? I set skills like this up with you, in one focused week.

Work with me
For developers how this skill is built, graded, and how it runs

at a glance- the short version

eval modeshape

what's inside - the parts that make up a skill 2/4 present

A skill is just a few plain-text files. Only the main one is required. The rest are optional, added as the work needs them. This is what the skill is made of; how it runs is just below.

The skill
state/skills/shape-builder/SKILL.md present
the skill itself, in plain text
The main file. It says what the skill is and lays out the steps in plain English.
Code
state/lib/shape-builder.ts not present
code the skill can run
Optional. Many skills are just words and need no code at all.
Scripts
state/bin/shape-builder/ not present
helper scripts
Optional. Added when a skill has a few commands to run.
Loader
state/skills/shape-builder/AGENTS.md present
what the AI loads on the fly
Loaded automatically the moment this skill is needed. Kept short on purpose.

how it runs - the shared frame every skill uses 3/5 present

Every skill runs the same way. One part does the work, a separate part checks it, and a short loader hands the AI exactly what it needs for the job. Anything this skill doesn't use shows a one-line note saying why, on purpose, not by accident.

makes the work The worker
not present

No work step here. This is probably a skill that reads or coordinates, not one that produces something.

checks the work The reviewer
inferred
shape gate an automatic check
The check is an automatic pass or fail on the shape of the result, run separately from the work itself.
frame
learns Self-correction
present
fixes itself learns from gaps
When a run hits a gap, the skill gets edited on the spot [FIXED] or queued for a bigger rewrite [LOGGED], so it keeps getting better.
tidies up Background fixes
present
queued for rewrite runs in the background
Bigger fixes that can't be made on the spot get queued and rewritten in the background later.
remembers Run history
present
state/log/evals.ndjson shape runs
Every run is written down here, so the next time this skill is used it already knows how the last runs went.
Critical rules the things this skill must not get wrong
  1. genui-library.tsx: Insert the 3-line re-export stub AFTER the last existing import { XyzComponent } from "./genui/xyz"; line, BEFORE const genuiLibrary = .... Never insert inside the genuiLibrary object literal (between root: and components:).
  2. dispatch-card.tsx: Find the final }; that closes the DISPATCH_REGISTRY object (near end of file). Insert the new entry immediately before it. Never insert inside another shape's parse or render function — that nests your entry inside a try-block and breaks both shapes silently.
  3. server.ts: Both sub-steps are mandatory — (a) add the name string to KNOWN_COMPONENTS Set AND (b) add the const xyzRegex + if (xyzRegex.test(intent) && !llmEmittedNames.has("XyzName")) block in the intent-matching section. Skipping either sub-step causes silent null-render.
  4. TypeScript gate: Run cd ~/projects/snappy-chat/web && npx tsc --noEmit 2>&1 | head -20 BEFORE committing. Fix all errors first.

what it has learned - fixes written back in over time sample

When a run hits something this skill didn't handle, the fix gets written back into the skill so it doesn't happen again. FIXED means it was corrected on the spot. LOGGED means it's queued for a bigger rewrite. Either way, the skill gets a little better and never makes the same mistake twice.

  1. Loading feedback rows…

SKILL.md- the skill, written out in plain English

shape-builder

Rare-path builder for a new custom defineComponent shape in snappy-chat. The default path is OpenUI Lang primitives plus skill-owned state/skills/<slug>/resources/*.openui surfaces maintained by dashboard-builder. Use this skill only when the needed experience cannot be composed from the existing Lang library, CanvasFrame/CanvasPanel, Query/Mutation, or a saved resource.

If the request is for a schedule, preview, inspector, dashboard, report, checklist, queue, artifact, or channel-faithful card, create or maintain a skill-owned .openui resource instead. A custom .tsx shape is justified for platform-faithful previews or interaction primitives that many resources will reuse.

When to invoke

  • User asks for a visual that cannot be composed from OpenUI Lang primitives or a saved resources/*.openui surface
  • User describes a concept ("I want a...", "show me a...", "can you make...")
  • The concept is visual or displayable and would benefit from a rich card versus plain text
  • A reusable React component with a fixed schema is needed across multiple resources

Steps

  1. Parse the request -- extract: shape name (PascalCase, e.g. BrainCharacter), file name (kebab-case, e.g. brain-character), description (what it shows and when to use it), and the visual concept (what should render).
  1. Check for existing shape -- grep DISPATCH_REGISTRY to confirm the name is not already registered. Command: grep -n "<Name>" ~/projects/snappy-chat/web/src/dispatch-card.tsx. If it exists, do not recreate it -- emit the existing shape instead.
  1. Read the pattern -- read ~/projects/snappy-chat/web/src/genui/weather-card.tsx as a clean reference for structure, imports, and naming conventions. Note the import pattern: import { type JSX } from "react", import { defineComponent } from "@openuidev/react-lang", import { z } from "zod/v4".
  1. Generate the shape file -- write ~/projects/snappy-chat/web/src/genui/<file-name>.tsx with:
  • File header comment block (shape name, what it does, what it exports).
  • export interface <Name>Args { ... } -- props that make sense for the concept. Keep it to 4-8 props, all optional except one anchor prop.
  • export function <Name>View(props: <Name>Args): JSX.Element { ... } -- React component. Styling rules: only var(--text), var(--surface), var(--surface-elevated), var(--accent), var(--accent-soft), var(--border), var(--text-secondary), var(--radius-sm), var(--font-mono), var(--font-display) CSS variables. No inline colors, no hex values, no external libraries beyond what is already in the React + OpenUI bundle.
  • export const <Name>Component = defineComponent({ name: "<Name>", description: "...", props: z.object({...}), component: ({props}) => <...> }) -- the OpenUI Lang wrapper.
  1. Add re-export stub to genui-library.tsx -- find the last stub block (before export const genuiLibrary) and append THREE lines (not four):
   export { <Name>View, <Name>Component } from "./genui/<file-name>";
   export type { <Name>Args } from "./genui/<file-name>";
   import { <Name>Component } from "./genui/<file-name>";

INSERTION ANCHOR (critical): Insert these three lines immediately after the last existing import { XyzComponent } from "./genui/xyz"; line - the end of the re-export stub block. Do NOT insert inside the const genuiLibrary = { ... } object literal (between root: and components:), and do NOT insert inside any JSX block or function body. The stubs MUST appear before the const genuiLibrary = definition. If you are unsure, search for const genuiLibrary and confirm your insertion point is above that line. Also add <Name>Component to the components[] array at the end of genuiLibrary.

  1. Register in DISPATCH_REGISTRY -- in ~/projects/snappy-chat/web/src/dispatch-card.tsx, add a new entry just before the closing }; of DISPATCH_REGISTRY:
   <Name>: {
     parse: (raw: string) => { try { const o = JSON.parse(raw); return o && typeof o === "object" ? o : null; } catch { return null; } },
     render: (id, a: <Name>Args) => (
       <<Name>View key={id} {...(a as <Name>Args)} />
     ),
   },

INSERTION ANCHOR (critical): The DISPATCH_REGISTRY object closes with a }; near the end of the file. Insert the new entry immediately before that final };. Do NOT insert inside another shape's parse or render function - that puts your entry inside a different shape's try-block and breaks both shapes silently. To find the correct location: search for the last }, followed shortly by }; at the file's end. Your new entry goes between that last }, and the };. Also add the import for <Name>View and <Name>Args at the top of the file near the other genui imports.

  1. Add server matcher -- MANDATORY. This leg is required. Skipping it causes silent null-render with no warning. In ~/projects/snappy-os/state/bin/head-screen/server.ts:

a. Add "<Name>" as a string to the KNOWN_COMPONENTS Set. Find the Set definition (it looks like new Set(["WeatherCard", "DonutChart", ...])) and add your name to the list before the closing ]) or new Set([...]) bracket. b. Add an intent regex constant and emitTriple call in the intent-matching section (just before writeAgUI({ type: "RUN_FINISHED"...}):

   // <Name> -- <what it shows>. Added <date>.
   const <name>Regex = /<regex matching natural language trigger>/i;
   if (<name>Regex.test(intent) && !llmEmittedNames.has("<Name>")) {
     emitTriple("<Name>", { <minimal valid props> });
     llmEmittedNames.add("<Name>");
   }

Verify both (a) and (b) are present before moving to the next step.

  1. TypeScript gate (mandatory before commit) -- Run: cd ~/projects/snappy-chat/web && npx tsc --noEmit 2>&1 | head -20. If any errors appear, fix them before committing. Do NOT commit with outstanding type errors. Common causes: wrong import path, missing type export from genui-library.tsx, or mis-typed props in DISPATCH_REGISTRY render function.
  1. Build and deploy -- cd ~/projects/snappy-chat && bash scripts/build-app.sh --install 2>&1 | tail -10. Build must succeed (exit 0) before claiming the shape is live.
  1. Restart server -- The server.ts process does NOT hot-reload. Kill the old PID and relaunch:
    pkill -f "head-screen/server" 2>/dev/null; sleep 1
    cd ~/projects/snappy-os && bash state/bin/head-screen/launch.sh &
  1. Commit -- stage only the four changed files and commit:
    cd ~/projects/snappy-chat
    git add web/src/genui/<file-name>.tsx web/src/genui-library.tsx web/src/dispatch-card.tsx
    git commit -m "shape-builder: generated <Name> shape from request"
    cd ~/projects/snappy-os
    git add state/bin/head-screen/server.ts
    git commit -m "shape-builder: register <Name> in server.ts KNOWN_COMPONENTS + matcher"
  1. Emit the shape -- emit [[TOOL:<Name>]]{<minimal valid props as JSON>}[[/TOOL]] so the user sees it immediately in the chat surface.
  1. Eval -- append one eval row to state/log/evals.ndjson with score: 1.0 if the shape rendered and the build succeeded, score: 0.0 if the build failed. Use skill: "shape-builder" as the skill field.

AGENTS.md- what the AI loads when this skill comes up

shape-builder - loader

Per-turn rules. Full reference: state/skills/shape-builder/SKILL.md. Wires a generative-UI shape from description to committed, rendered card.

Critical Rules

  1. Check DISPATCH_REGISTRY first - grep ~/projects/snappy-chat/web/src/dispatch-card.tsx for the name. If it exists, emit the existing shape instead of recreating (real incident 2026-04-30: BrainCharacter was already built; verified and emitted).
  1. Use weather-card.tsx as reference - ~/projects/snappy-chat/web/src/genui/weather-card.tsx is the canonical clean template. Don't use random other shapes.
  1. CSS variables only - approved: var(--text) | var(--text-secondary) | var(--surface) | var(--surface-elevated) | var(--accent) | var(--accent-soft) | var(--border) | var(--radius-sm) | var(--font-mono) | var(--font-display). No hex, rgb, or named colors (DESIGN.md + impeccable law). Real incident 2026-04-27: shape used hex #ABCDEF, regressed until replaced with var.
  1. Pi mis-wires wiring legs - explicit anchors required (real incident 2026-04-30: HabitTracker):
  • genui-library.tsx: Insert the 3-line re-export stub AFTER the last existing import { XyzComponent } from "./genui/xyz"; line, BEFORE const genuiLibrary = .... Never insert inside the genuiLibrary object literal (between root: and components:).
  • dispatch-card.tsx: Find the final }; that closes the DISPATCH_REGISTRY object (near end of file). Insert the new entry immediately before it. Never insert inside another shape's parse or render function - that nests your entry inside a try-block and breaks both shapes silently.
  • server.ts: Both sub-steps are mandatory - (a) add the name string to KNOWN_COMPONENTS Set AND (b) add the const xyzRegex + if (xyzRegex.test(intent) && !llmEmittedNames.has("XyzName")) block in the intent-matching section. Skipping either sub-step causes silent null-render.
  • TypeScript gate: Run cd ~/projects/snappy-chat/web && npx tsc --noEmit 2>&1 | head -20 BEFORE committing. Fix all errors first.
  1. Three re-export lines only - genui-library.tsx stubs MUST be exactly: export { View, Component } + export type { Args } + import { Component } (for components[] binding). Bare export { X } from "..." doesn't satisfy isolatedModules (real incident 2026-04-28: four-line stub broke array binding).
  1. All three legs land or silent null-render - (a) tsx file; (b) DISPATCH_REGISTRY in dispatch-card.tsx; (c) KNOWN_COMPONENTS in server.ts. Missing any one = no warning, shape drops. Verify all three. (Real incident 2026-04-28: forgot (c), silent drop.)
  1. server.ts doesn't hot-reload - after editing, kill + relaunch: pkill -f "head-screen/server" 2>/dev/null; sleep 1; cd ~/projects/snappy-os && bash state/bin/head-screen/launch.sh &. Old process continues running otherwise (real incident 2026-04-29).
  1. Build must exit 0 - cd ~/projects/snappy-chat && bash scripts/build-app.sh --install 2>&1 | tail -10. Type errors block ship. Fix before committing.
  1. Explicit pathspecs, two repos - never git add -A. Stage only: snappy-chat (web/src/genui/<file>.tsx web/src/genui-library.tsx web/src/dispatch-card.tsx), snappy-os (state/bin/head-screen/server.ts). Separate commits.
  1. Name uniqueness load-bearing - PascalCase MUST NOT collide with DISPATCH_REGISTRY keys. File name is kebab-case equivalent (BrainCharacter → brain-character.tsx).
  1. Eval row mandatory - append to state/log/evals.ndjson: skill: "shape-builder", score: 1.0 (success) or 0.0 (build failed). No exceptions.

Commands

| ui dashboard | state/skills/shape-builder/resources/ui.openui | | check existing | grep -n "<Name>" ~/projects/snappy-chat/web/src/dispatch-card.tsx | | write tsx | # ~/projects/snappy-chat/web/src/genui/<file>.tsx with ShapeView, ShapeComponent, defineComponent | | re-export stub | edit ~/projects/snappy-chat/web/src/genui-library.tsx: 3 lines + add to components[] | | registry entry | edit ~/projects/snappy-chat/web/src/dispatch-card.tsx: import + DISPATCH_REGISTRY entry | | server matcher | edit ~/projects/snappy-os/state/bin/head-screen/server.ts: add to KNOWN_COMPONENTS + regex + emitTriple | | tsc check | cd ~/projects/snappy-chat && npx tsc --noEmit 2>&1 \| head -20 | | build + deploy | cd ~/projects/snappy-chat && bash scripts/build-app.sh --install 2>&1 \| tail -10 | | restart server | pkill -f "head-screen/server" 2>/dev/null; sleep 1; cd ~/projects/snappy-os && bash state/bin/head-screen/launch.sh & | | commit snappy-chat | git add web/src/genui/<file>.tsx web/src/genui-library.tsx web/src/dispatch-card.tsx && git commit -m "shape-builder: generated <Name>" | | commit snappy-os | git add state/bin/head-screen/server.ts && git commit -m "shape-builder: register <Name>" | | emit inline | [[TOOL:<Name>]]{<minimal JSON props>}[[/TOOL]] | | eval row | append state/log/evals.ndjson: skill="shape-builder" score=1.0\|0.0 actor_session_id=UUID |

Self-Test

An agent reading this should correctly:

  1. [ ] Check DISPATCH_REGISTRY before generating anything
  2. [ ] Use weather-card.tsx as the reference, not another shape
  3. [ ] Write only CSS variables (no hex/rgb/named colors)
  4. [ ] Insert the genui-library.tsx stub BEFORE const genuiLibrary, not inside the object literal
  5. [ ] Insert the DISPATCH_REGISTRY entry before the final };, not inside another shape's function
  6. [ ] Add BOTH server.ts sub-steps: KNOWN_COMPONENTS string AND intent regex + emitTriple block
  7. [ ] Run npx tsc --noEmit and fix all errors before committing
  8. [ ] Add exactly THREE re-export lines to genui-library.tsx
  9. [ ] Restart server.ts after editing it
  10. [ ] Run build and confirm exit 0 before committing
  11. [ ] Commit with explicit pathspecs in two separate repos
  12. [ ] Emit the shape inline so the user sees it immediately
  13. [ ] Append an eval row to evals.ndjson

<!-- kernel-ok: historical attribution for the PID-loader pattern. -->

Self-correcting loader (PID feedback)

This is the rule that makes loaders learn. Ported from snappy-kernel, where it demonstrably worked: the snappy-dom-cartographer skill mapped Skool admin UI once, every later ship-agent inherited the full map for free.

Before you finish, do two things:

1. Fix gaps (P - proportional). MANDATORY. If this loader didn't cover your case - if you had to read another file, run an undocumented command, work around a wrong selector, or discover a quirk - you MUST attempt an Edit to this AGENTS.md before you log.

  • Default action: edit this file. Surgical. Add the missing line, table

row, or code example where it belongs. No restructuring.

  • Only LOGGED is allowed when: the fix needs >10 lines, spans multiple

files, or requires a structural rewrite. In that case the state/regen/drain.sh queue picks it up asynchronously.

  • "I didn't have time" / "it's minor" / "the next agent will figure it out"

are NOT valid reasons. Minor gaps compound - that's the whole point of this loop.

  • Concrete example of a valid P-fix: discover agent-browser screenshot

takes path positionally not as --output <path>. One line in a table. Edit it in. Do not just log it.

  • The goal: the next agent never has to leave the loader.

2. Log the result.

echo "[$(date -u +%FT%TZ)] shape-builder: <what was missing or fixed> [FIXED|LOGGED] action_kind=<kind>" >> state/log/loader-feedback.log
  • <slug> MUST be the literal folder name of this loader

(state/skills/<slug>/AGENTS.md). The class token between [ts] and : is the producer slug, the writeback class, AND the grade class - they must be equal so state/lib/controller-tune.ts can pair the brief.

  • FIXED = you patched this loader inline (P-fix).
  • LOGGED = too large for inline; the PostToolUse enqueue + Stop-hook drain

will rewrite the loader from scratch on next session-end.

  • action_kind is the SECOND pairing predicate (added 2026-04-27, task #327).

Pick the value that describes what you actually did - same slug, different action_kind means the writeback satisfies a different brief layer:

  • shape-ok - only frontmatter-shape verification passed (rare from

a human; usually emitted by the lint, not a loader echo)

  • skill-ran - the skill ran end-to-end and an eval row landed

in state/log/evals.ndjson

  • loader-rewritten - you EDITED this AGENTS.md inline (the FIXED case),

OR the regen drain rewrote it

  • pattern-elevated - you promoted a recurring failure to a Critical Rule

(rule fix or new-skill scaffold) If you LOGGED (couldn't fix inline), omit action_kind - the inferrer will pick it up from your body keywords.

Do not skip this. Every agent run must leave the system better than it found it. The loader is the setpoint; you are the sensor; the gap is the error signal; closing the gap is the correction.

OpenUI Resource

  • Skill-owned OpenUI Lang resource: state/skills/shape-builder/resources/ui.openui. Read it before rendering or editing this skill's generated component surface.
  • Treat this resource as a first-class artifact of the skill, not a generic chat response. Improve it when the skill's user-facing output needs to become richer.
  • System resources compose OpenUI primitives and inherit SnappyChat tokens. Use ui_contract: branded in SKILL.md only for deliberate platform or client visuals.

api.ts- the code it can call

⚠ no api.ts - this skill has no typed action surface

scripts- helper scripts it can run

prose-only skill - no sidecar under state/bin/ yet. Steps, if any, are described in SKILL.md.

how we check it- the checks, plus the last 10 runs

rubric shape schema-shape check (no inline rubric)
recent mean 0.92 · 10 runs actor/auditor: unverifiable
deps none declared
timestamp verb score primary_issue artifact
2026-04-30 22:54Z - 1.00 - -
2026-04-30 05:33Z - 1.00 - -
2026-04-30 04:57Z - 0.60 - -
2026-04-30 04:12Z - 1.00 - -
2026-04-30 04:07Z - 1.00 - -
2026-04-30 22:54Z - 1.00 - -
2026-04-30 05:33Z - 1.00 - -
2026-04-30 04:57Z - 0.60 - -
2026-04-30 04:12Z - 1.00 - -
2026-04-30 04:07Z - 1.00 - -