cp ~/projects/snappy-os/state/bin/shape-builder/ .md file to compare - side-by-side diff against shape-builder
shape-builder
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.
For developers how this skill is built, graded, and how it runs
at a glance- the short version
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.
state/skills/shape-builder/SKILL.md
present
state/lib/shape-builder.ts
not present
state/bin/shape-builder/
not present
state/skills/shape-builder/AGENTS.md
present
how it's graded - what counts as a good run 8 criteria · 7 deterministic · 1 judge
Each row is one thing a good run has to get right. deterministic means a quick check decides, pass or fail. judge means the AI reads the result and rates it. Grading each piece on its own (instead of one overall score) shows exactly where a run fell short, so the fix is obvious.
how it runs - the shared frame every skill uses 2/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.
This skill doesn't fix its own gaps yet.
state/log/evals.ndjson - (a) web/src/genui/<kebab>.tsx — TSX file with View + Component + Args
- (b) DISPATCH_REGISTRY entry in dispatch-card.tsx (before final };, NEVER inside another shape's parse/render)
- (c) KNOWN_COMPONENTS string + intent regex + emitTriple in server.ts (BOTH sub-steps)
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.
- Loading feedback rows…
how the work flows- step by step
cp ~/projects/snappy-os/state/bin/shape-builder/manifest-template.ts /tmp/my-shape.ts
npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts check /tmp/my-shape.ts
npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts apply /tmp/my-shape.ts
npx tsx ~/projects/snappy-os/state/lint/shape-both-library-registration.ts
SKILL.md- the skill, written out in plain English
shape-builder
Rare-path builder for a new custom defineComponent shape in Snappy OS. The default path is OpenUI Lang primitives composed through compose_inline / Lang, optionally persisted as artifact lang_body. Use this skill only when the needed experience cannot be composed from the existing Lang library, Query/Mutation, or a saved artifact.
Rubric
criteria:
- name: primary_path_emits_shape
kind: deterministic
check: "DISPATCH_REGISTRY fires render for <Name>; TOOL_CALL_START/ARGS/END events emit (not text fallback)."
- name: shape_renders_nonzero
kind: deterministic
check: "Rendered shape is visually present (not null-render, not blank card). Screenshot required."
- name: all_three_legs_wired
kind: deterministic
check: "(a) TSX file, (b) DISPATCH_REGISTRY, (c) KNOWN_COMPONENTS in server.ts. Any missing = 0.0."
- name: both_library_registration
kind: deterministic
check: "Component appears in BOTH genuiLibrary AND canvasLibrary components[] arrays."
- name: no_name_collision
kind: deterministic
check: "Name does not collide with openuiChatLibrary exports. If it does, score = 0.0."
- name: css_class_present
kind: deterministic
check: "web/src/styles.css has a .snappy-<kebab> class for the component."
- name: typecheck_and_build_clean
kind: deterministic
check: "npx tsc --noEmit = 0 errors. bash scripts/build-app.sh --install exits 0."
- name: component_shape_valid
kind: judge
check: "Rendered component uses brand design system tokens, correct layout, no console errors."
When to invoke
- User asks for a reusable React component that many generated surfaces will reuse
- A platform-faithful brand row (GmailRow, SlackMessageRow, etc.) is needed
- A form input or interaction primitive is required that cannot be built from existing Lang primitives
- The needed shape has a fixed schema that must be consistent across many surfaces
Steps
DEPRECATED - OLD MANUAL FLOW (kept until new flow has 5+ shipped components)
The manual 13-step flow below is superseded by the manifest flow. New components use the manifest flow. Old components wired via the manual flow continue to work.
NEW FLOW - manifest-driven (use this for all new shapes)
Step 1: Write a manifest
Copy state/bin/shape-builder/manifest-template.ts and fill in the fields.
cp ~/projects/snappy-os/state/bin/shape-builder/manifest-template.ts /tmp/my-shape.ts
# Edit /tmp/my-shape.ts
Key fields:
name: PascalCase. MUST NOT collide with openuiChatLibrary exports (Accordion, Button, Table, DatePicker, etc.). Prefix with brand or "Snappy" when unsure.kind:"brand-row" | "form-input" | "data-display" | "preview" | "primitive"brand: optional - one ofgmail | slack | linear | calendar | github | notion | stripe | freshbooks | typefully | xano | krisp | openrouter. Sets brand color tokens from BRAND_DESIGN_SYSTEM.propsSchema: typed props. Avoid"any"without a// SHAPE-ALLOW-ANY: <reason>comment - theshape-props-schema-strictlint will fail.registerIn: { genui: true, canvas: true }: both required.
Step 2: Validate the manifest
npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts check /tmp/my-shape.ts
Checks: name collision vs openuiChatLibrary, required fields, brand tokens available.
Step 3: Generate the 3 legs
npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts apply /tmp/my-shape.ts
The generator:
- Writes
web/src/genui/<kebab-name>.tsx(TSX file with View + Component + Args) - Inserts the 3-line re-export stub into
genui-library.tsx(beforegenuiLibrary) - Prints manual-apply snippets for
dispatch-card.tsxandserver.ts
Step 4: Apply the manual snippets
The generator prints snippets for:
web/src/dispatch-card.tsx: import + DISPATCH_REGISTRY entry (before final};)state/bin/head-screen/server.ts: KNOWN_COMPONENTS string + intent regex + emitTripleweb/src/styles.css: CSS class using onlyvar(--...)tokens
Apply each snippet. Critical anchors:
- dispatch-card.tsx: new entry goes BEFORE the final
};of DISPATCH_REGISTRY. Never inside another shape'sparseorrenderfunction. - server.ts: add name string to KNOWN_COMPONENTS Set AND add the regex + emitTriple block in the intent-matching section. Both sub-steps required or silent null-render.
- genui-library.tsx: add
ShapeNameComponentto BOTHgenuiLibrary.components[]ANDcanvasLibrary.components[].
Step 5: Run pre-commit lint gates
npx tsx ~/projects/snappy-os/state/lint/shape-both-library-registration.ts
npx tsx ~/projects/snappy-os/state/lint/shape-name-collision.ts
npx tsx ~/projects/snappy-os/state/lint/shape-styles-css-class.ts
npx tsx ~/projects/snappy-os/state/lint/shape-props-schema-strict.ts
All must pass before proceeding.
Step 6: TypeScript gate (mandatory)
cd ~/projects/snappy-os-app/apps/snappy-os/web && npx tsc --noEmit 2>&1 | head -20
Fix all errors. Do NOT commit with type errors.
Step 7: Build and deploy
cd ~/projects/snappy-os-app/apps/snappy-os && bash scripts/build-app.sh --install 2>&1 | tail -10
Must exit 0.
Step 8: Auto-verify render
bash ~/projects/snappy-os/state/bin/shape-builder/verify.sh ShapeName
Takes a screenshot of the running app and checks for MountUiError. Pass required before commit.
Step 9: Commit (explicit pathspecs, two repos)
# Snappy OS app
cd ~/projects/snappy-os-app/apps/snappy-os
git add web/src/genui/<kebab-name>.tsx web/src/genui-library.tsx web/src/dispatch-card.tsx web/src/styles.css
git commit -m "shape-builder: add <Name> manifest-driven shape"
# snappy-os
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"
Step 10: Eval row
# append to state/log/evals.ndjson
npx tsx state/lib/log.ts append evals '{"skill":"shape-builder","shape":"<Name>","score":1.0}'
Brand design system
All brand-row shapes use the BRAND_DESIGN_SYSTEM tokens from state/lib/shape-manifest.ts. These map to CSS vars that already exist in web/src/styles.css:
- Accent:
var(--channel-<brand>-accent)- primary brand color - Soft:
var(--channel-<brand>-soft)- tinted background fill - Standard row: 52px height, 10px/16px padding, 6px brand dot indicator (not a side stripe)
- Typography: 14px/500 primary, 13px/400 muted secondary, 14px/600 tabular-nums amount
No side-stripe borders. The impeccable absolute ban applies. Use brand dot indicators instead.
Lint gates (4 new gates, all in state/lint/)
| Gate | What it checks |
|---|---|
shape-both-library-registration.ts | Component in BOTH genuiLibrary AND canvasLibrary |
shape-styles-css-class.ts | .snappy-<kebab> CSS class exists in styles.css |
shape-name-collision.ts | Name does not collide with openuiChatLibrary exports |
shape-props-schema-strict.ts | No z.any() without SHAPE-ALLOW-ANY comment |
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.
Critical Rules
- Check DISPATCH_REGISTRY first -
grep -n "<Name>" ~/projects/snappy-os-app/apps/snappy-os/web/src/dispatch-card.tsx. If it exists, emit the existing shape.
- Name collision is a silent killer - run
npx tsx ~/projects/snappy-os/state/lint/shape-name-collision.tsBEFORE writing code.DatePicker,Button,Table,Accordion, etc. all collide with openuiChatLibrary. Prefix with brand name or "Snappy" (e.g.SnappyDatePicker).
- All three legs or silent null-render:
- (a)
web/src/genui/<kebab>.tsx- TSX file with View + Component + Args - (b) DISPATCH_REGISTRY entry in
dispatch-card.tsx(before final};, NEVER inside another shape's parse/render) - (c) KNOWN_COMPONENTS string + intent regex + emitTriple in
server.ts(BOTH sub-steps)
- Both library registrations - add to
genuiLibrary.components[]ANDcanvasLibrary.components[]in genui-library.tsx. Shape in only one library renders in one surface mode and breaks in the other.state/lint/shape-both-library-registration.tscatches this.
- 3-line re-export stub only (isolatedModules):
export { NameView, NameComponent } from "./genui/<kebab>";
export type { NameArgs } from "./genui/<kebab>";
import { NameComponent } from "./genui/<kebab>";
Insertion point: after the last import { XyzComponent } from "./genui/xyz"; line, BEFORE export const genuiLibrary. Never inside the object literal.
- CSS vars only -
var(--text),var(--surface),var(--accent),var(--accent-soft),var(--border),var(--text-secondary), channel tokens (var(--channel-<brand>-accent)). No hex. styles.css class required orshape-styles-css-classlint fails.
- Brand design system - brand-row shapes use BRAND_DESIGN_SYSTEM from
state/lib/shape-manifest.ts. Height 52px, padding 10px/16px, 6px dot indicator (no side-stripe - impeccable absolute ban).
- No z.any() without guard -
z.any().optional()foractionprops is fine (universal exempt). Any otherz.any()needs// SHAPE-ALLOW-ANY: <reason>on the preceding line orshape-props-schema-strictfails.
- TypeScript gate before commit -
cd ~/projects/snappy-os-app/apps/snappy-os/web && npx tsc --noEmit 2>&1 | head -20. Fix all errors first.
- Build must exit 0 -
cd ~/projects/snappy-os-app/apps/snappy-os && bash scripts/build-app.sh --install 2>&1 | tail -10.
- server.ts doesn't hot-reload - after editing:
pkill -f "head-screen/server" 2>/dev/null; sleep 1; cd ~/projects/snappy-os && bash state/bin/head-screen/launch.sh &.
- Explicit pathspecs, two repos - never
git add -A. Stage: snappy-chat (web/src/genui/<file>.tsx web/src/genui-library.tsx web/src/dispatch-card.tsx web/src/styles.css), snappy-os (state/bin/head-screen/server.ts). Separate commits.
- Eval row mandatory - append to
state/log/evals.ndjson:skill: "shape-builder",score: 1.0(success) or0.0(build failed).
Manifest-driven flow
# 1. Write manifest (copy the template)
cp ~/projects/snappy-os/state/bin/shape-builder/manifest-template.ts /tmp/my-shape.ts
# edit /tmp/my-shape.ts
# 2. Validate
npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts check /tmp/my-shape.ts
# 3. Generate leg A (TSX) + leg B (genui-library stub)
npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts apply /tmp/my-shape.ts
# 4. Apply printed snippets for dispatch-card.tsx + server.ts + styles.css
# 5. Run 4 lint gates
npx tsx ~/projects/snappy-os/state/lint/shape-both-library-registration.ts
npx tsx ~/projects/snappy-os/state/lint/shape-name-collision.ts
npx tsx ~/projects/snappy-os/state/lint/shape-styles-css-class.ts
npx tsx ~/projects/snappy-os/state/lint/shape-props-schema-strict.ts
# 6. TypeScript gate
cd ~/projects/snappy-os-app/apps/snappy-os/web && npx tsc --noEmit 2>&1 | head -20
# 7. Build + deploy
cd ~/projects/snappy-os-app/apps/snappy-os && bash scripts/build-app.sh --install
# 8. Auto-verify
bash ~/projects/snappy-os/state/bin/shape-builder/verify.sh ShapeName
# 9. Commit (explicit pathspecs)
# 10. Eval row
Commands
| action | command | |
|---|---|---|
| check existing | grep -n "<Name>" ~/projects/snappy-os-app/apps/snappy-os/web/src/dispatch-card.tsx | |
| collision check | npx tsx ~/projects/snappy-os/state/lint/shape-name-collision.ts | |
| validate manifest | npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts check /tmp/my-shape.ts | |
| generate legs | npx tsx ~/projects/snappy-os/state/bin/shape-builder/apply.ts apply /tmp/my-shape.ts | |
| lint both-libraries | npx tsx ~/projects/snappy-os/state/lint/shape-both-library-registration.ts | |
| lint styles | npx tsx ~/projects/snappy-os/state/lint/shape-styles-css-class.ts | |
| lint collision | npx tsx ~/projects/snappy-os/state/lint/shape-name-collision.ts | |
| lint schema-strict | npx tsx ~/projects/snappy-os/state/lint/shape-props-schema-strict.ts | |
| tsc check | `cd ~/projects/snappy-os-app/apps/snappy-os/web && npx tsc --noEmit 2>&1 | head -20` |
| build + deploy | `cd ~/projects/snappy-os-app/apps/snappy-os && bash scripts/build-app.sh --install 2>&1 | tail -10` |
| auto-verify | bash ~/projects/snappy-os/state/bin/shape-builder/verify.sh <ShapeName> | |
| 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-os | git add web/src/genui/<file>.tsx web/src/genui-library.tsx web/src/dispatch-card.tsx web/src/styles.css && git commit -m "shape-builder: add <Name>" | |
| commit snappy-os | git add state/bin/head-screen/server.ts && git commit -m "shape-builder: register <Name>" | |
| eval row | append state/log/evals.ndjson: {"skill":"shape-builder","shape":"<Name>","score":1.0} |
Self-Test
An agent reading this should correctly:
- [ ] Check DISPATCH_REGISTRY before generating anything
- [ ] Run shape-name-collision lint BEFORE writing code
- [ ] Write only CSS vars (no hex/rgb/named colors)
- [ ] Use BRAND_DESIGN_SYSTEM for brand-row kind (dot indicator, not side stripe)
- [ ] Insert genui-library.tsx stub BEFORE
const genuiLibrary, not inside the object - [ ] Insert DISPATCH_REGISTRY entry before final
};, not inside another shape's function - [ ] Add BOTH server.ts sub-steps: KNOWN_COMPONENTS string AND intent regex + emitTriple
- [ ] Register in BOTH genuiLibrary.components[] AND canvasLibrary.components[]
- [ ] Add CSS class to styles.css
- [ ] Run all 4 lint gates before TypeScript check
- [ ] Run tsc --noEmit and fix all errors before committing
- [ ] Run build-app.sh --install and confirm exit 0
- [ ] Run verify.sh and confirm PASS
- [ ] Commit with explicit pathspecs in two separate repos
- [ ] Append eval row to evals.ndjson
Found a gap? Edit this file. <!-- footer-injection-point -->
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 - 10 inline code blocks live in SKILL.md above (no state/bin/ sidecar yet).
how we check it- the checks, plus the last 10 runs
no recent runs logged - the eval contract is declared but nothing has been graded yet