What We Are Building
A guided walk through speccing a small paint program in Psy, one step at a time.
Everything up to here has been explained in pieces. This section does one small thing end to end.
We are going to spec out a paint program. An MS Paint clone — a bitmap, three drawing tools, and the two file operations you cannot do without:
- Pencil — hard single-pixel line
- Brush — soft, sized, anti-aliased
- Eraser — paints the background colour back
- New Drawing — start again
- Save Drawing — write a PNG
Not much of a product. Exactly enough to need every definition kind, and small enough that you can hold all of it in your head while we go.
By the end you will have a directory of .psy files that compiles, a set of
generated Claude Code artifacts, and an agent that knows what it is building.
What you end up with
paint/
psy.config.ts
src/
concepts/ Paint, StrokeModel
domains/ Canvas, Tools, Files
features/ Pencil, Brush, Eraser, NewDrawing, SaveDrawing
components/ CanvasSurface, ToolPalette, ColorPicker, FileStore
references/ Voice, Naming
skills/ CanvasRules, ImplementTool, WriteInterfaceCopy
agents/ BuildPaint
index.psy
.claude/ generated
agents/build-paint.md
skills/*/SKILL.mdSixteen declarations. About three hundred lines of Psy. And this on the other side, one file of nineteen:
---
name: pencil
description: "Feature: Draw a hard single-pixel line."
---
# Pencil
## Behaviour
Dragging draws a one-pixel line in the foreground colour, following the
pointer. The line is hard-edged: no anti-aliasing, no opacity ramp, every
touched pixel is set to exactly the foreground colour.
## Edge Cases
- A click without a drag paints exactly one pixel.
- Dragging outside the canvas and back continues the same stroke; the pixels
outside are discarded rather than clamped to the edge.
## Authority
This describes the product as it is meant to work. Where the implementation
disagrees, treat this as correct unless the task is explicitly to change it.Set up the project
mkdir paint && cd paint
psy init . --template morphic --yespsy init: created 4 files from template `morphic`
psy.config.ts
src/skills/ExampleSkill.psy
src/agents/ExampleAgent.psy
src/index.psyDelete the two example declarations — we are writing our own:
rm src/skills/ExampleSkill.psy src/agents/ExampleAgent.psyAnd make psy.config.ts this:
import { defineConfig } from "@psy/config";
import { morphic } from "@psy/morphic";
import { claude } from "@psy/adapter-claude";
export default defineConfig({
source: ["./src"],
frameworks: [morphic()],
targets: [claude({ output: ".claude" })],
artifacts: { manifest: true },
});Three lines of that matter:
frameworks: [morphic()]gives us the eight declaration keywords and the rules that go with them.targets: [claude(...)]turns declarations into Claude Code artifacts.artifacts: { manifest: true }letspsy buildclean up after itself when we rename something, which we will. See pruning.
Empty out src/index.psy for now:
// src/index.psyAnd check that everything is wired up:
psy checkcheck: no problems found in 1 file(s)One file, no problems, nothing declared. That is the starting line.
How this goes
Seven steps, outside in. We describe the program before we describe its parts, and we describe its parts before we describe how to work on it.
- The Concept — what a drawing is.
- The Domains — the three areas of the problem.
- The Features — all five, and what makes each one decided.
- The Components — what owns the bitmap, and what must stay true.
- The References — the rules that apply everywhere.
- Skills And An Agent — wiring knowledge to work.
- Build It — and see what an agent actually receives.
Every step shows the .psy you write and the file it produces. Nothing is
hand-waved.
Next: The Concept