# I shaped a feature then an agent implemented while I was AFK. Not vibe coding. Shaped coding + ralph. The difference? I spent some time with the problem before the agent wrote anything. Then it implemented 8 scopes autonomously, end to end. Here's the process. Most spec-driven approaches lock in the first solution that comes to mind. Shaping is different: you define requirements clearly, then explore multiple possible solutions before picking the best one. The agent gets goals, not tasks. Clear boundaries, explicit outcomes, and a wiring diagram between every piece. It figures out its own task list — it just needs to know *what done looks like*. ## The tools I used @rjs shaping-skills (github.com/rjs/shaping-skills) — a set of Claude Code skills that implement Shape Up methodology for AI-assisted development. Two skills: - `/shaping` — iterate on problem + solution before code - `/breadboarding` — map UI + Code affordances into a wiring diagram The process has 4 phases: ``` Frame → Shape → Breadboard → Execute (why) (what) (how) (ralph) ``` Phases 1-3 are collaborative sessions with Claude. Phase 4 is fully autonomous. ## Frame: define the problem For context: the app syncs data via webhook callbacks. New users need to configure their webhook endpoint and trigger an initial API sync to backfill historical data — but there was no UI for either. I started with a raw observation: > "There's no UI to trigger backfill or configure webhooks. To get it > working locally you need curl and .env. This is really bad." The `/shaping` skill helped me turn this into a crisp frame: **Problem:** Client logs in → empty dashboard → no guidance → needs manual curl + .env edits to get data flowing **Outcome:** Client sees exactly what to do. Webhook URL ready to copy. Backfill with one click. Secret per-account. ## Shape: explore solutions Still in `/shaping`, we explored multiple shapes and selected one. Not a feature list — a mechanism list: ``` A1: webhook_secret in accounts table (auto-gen on login) A2: GET /api/settings/status (single source of truth) A3: Empty state in Overview (disappears when data arrives) A4: SetupGuide component (2-step: webhook → sync) A5: /settings page (same info, persistent access) ``` Each part has a clear mechanism. No hand-waving. ## Shape: fit check Before moving on, we did a fit check — every requirement mapped to the shape: ``` R0 Empty dashboard shows guided setup ✅ A4 R1 Webhook URL with account_id + copy ✅ A5 R2 Auto-generated secret per-account ✅ A1 R9 Secret per-account in the database ✅ A1 R10 Signature verification per-account ✅ A1 ``` 12/12 requirements covered. No gaps. *Then* we moved to breadboarding. ## Breadboard: the wiring diagram `/breadboarding` maps every UI and Code affordance with explicit wiring: ``` UI Affordances: U2 | webhook URL (readonly + copy btn) → clipboard U6 | btn "Sync Now" → POST /api/backfill U7 | progress: billets ← N4 (sync-status-api) Code Affordances: N1 | auto-gen secret on login → accounts table N2 | verifySignature per-account → accounts table N5 | overview guard (count billets) → P1 empty or P1' dashboard ``` Every affordance has an ID. Every wire is explicit. This becomes the spec the agent implements. ## The ralph loop Here's where it gets interesting. I created "ralph" — a 80-line prompt + 30-line bash script that turns Claude Code into an autonomous scope-by-scope executor. The full prompt (`ralph/prompt.md`): ```markdown # CONTEXT Study the `docs/shaping/` directory to understand the objective: - `frame.md` — the problem and desired outcome - `shaping.md` — requirements, explored shapes, selected shape and its parts - `breadboard.md` — places, affordances (UI and Code), data stores and wiring - `slicing.md` — ordered scopes with dependencies and verify criteria Read `slicing.md` to get the list of scopes to implement. Run `git log --oneline -10` to understand what's already been done. # SLICING Each scope in `slicing.md` is already the smallest end-to-end verifiable slice. The scope is the unit of work — implement all affordances in a scope before committing. If a scope is large (many affordances), split into at most two natural sub-slices (e.g. data layer + UI layer). Each sub-slice becomes one commit. # SELECTION Pick **one scope** to work on. Prioritize in this order: 1. Critical bugfixes 2. Next scope on the critical path (`slicing.md` → Dependency Graph) Respect dependencies between scopes. Don't start a scope whose dependencies aren't complete. 3. Parallelizable scopes that are already unblocked 4. Polish and quick wins within already-delivered scopes If there's no more work, return <promise>NO MORE TASKS</promise>. **STOP after completing and committing the scope.** Don't pick the next one automatically. # EXPLORATION Explore the repository and fill your context window with information relevant to completing the work. Consult the breadboard to understand the wiring between affordances. # EXECUTION Complete the entire scope. If you realize the work is larger than expected (e.g. requires a refactor first), return "HOLD ON A SECOND". Then find a way to reduce scope — split into sub-slices and do only the first one. # VERIFICATION Before committing, run the scope's verify criteria (defined in `slicing.md`) and the feedback loops: - `npm run test` to run tests - `npm run typecheck` to run the type checker ## Visual Verification with Playwright MCP When working on UI affordances (scopes 5-8), use Playwright MCP to verify visually: 1. Start the dev server if not running (`npm run dev`) 2. Use Playwright MCP to navigate to the relevant place 3. Take screenshots to verify the UI is correct 4. Test that interactive affordances (filters, tables, cross-place navigation) work as expected # COMMIT Make a git commit. The commit message should: 1. Reference the scope and implemented affordances (e.g. `Scope 2: N50-N53 billet webhook handler`) 2. Key decisions made 3. Files changed 4. Blockers or notes for the next iteration Keep it concise. # FINAL RULES WORK ON ONE SCOPE AT A TIME. The scope is the atomic unit — don't stop in the middle. ``` ## The ralph scripts Two modes: `ralph/once.sh` — run one scope interactively: ```bash #!/bin/bash claude --dangerously-skip-permissions "@ralph/prompt.md" ``` `ralph/afk.sh` — AFK loop (run N scopes while you sleep): ```bash #!/bin/bash set -e if [ -z "$1" ]; then echo "Usage: $0 <iterations>" exit 1 fi stream_text='select(.type == "assistant").message.content[]? | select(.type == "text").text // empty | gsub("\n"; "\r\n") | . + "\r\n\n"' final_result='select(.type == "result").result // empty' for ((i=1; i<=$1; i++)); do tmpfile=$(mktemp) trap "rm -f $tmpfile" EXIT claude \ --verbose \ --print \ --dangerously-skip-permissions \ --output-format stream-json \ "@ralph/prompt.md" \ | grep --line-buffered '^{' \ | tee "$tmpfile" \ | jq --unbuffered -rj "$stream_text" result=$(jq -r "$final_result" "$tmpfile") if [[ "$result" == *"<promise>NO MORE TASKS</promise>"* ]]; then echo "Ralph complete after $i iterations." exit 0 fi done ``` Gists: [once.sh](https://gist.github.com/aquilarafa/ab1829a5d706336063a89bc1750331b1) | [afk.sh](https://gist.github.com/aquilarafa/0f9bc13cb52af50e6fc173c6daa91658) Run `./ralph/afk.sh 10`, go make dinner. Come back to commits. ## The results Ralph implemented 8 scopes autonomously. Every commit references the scope and affordances from the breadboard: ``` Scope 1: Get Connected — bootstrap + auth + webhook + queue Scope 2: Billet & Customer Sync — webhook handler + BullMQ Scope 3: Payment Enrich — fetch details on billet paid Scope 4: Backfill — historical import via API pagination Scope 5: Overview Dashboard — KPIs, charts, status distribution Scope 6: Payments List — filterable table with pagination Scope 7: Billets List — searchable table with status filter Scope 8: Customer & Billet Detail — drill-down pages ``` Each commit passes typecheck + tests. Each references the exact affordance IDs (N50-N53, U40-U53, etc). ## The meta-game Between scope 5 and 6, I tuned the ralph prompt three times: ``` 571d8dc ralph: stop after one task (was doing too many at once) 02bae31 ralph: scope is the atomic unit, not individual affordances f3598a8 ralph: add --dangerously-skip-permissions to both scripts ``` The prompt is code. You iterate on it like code. Small adjustments, big behavior changes. ## Why this works Shaping front-loads the hard decisions. The agent never has to guess intent. **Without shaping:** "add onboarding to the dashboard" → agent locks in the first solution → you course-correct 47 times **With shaping:** breadboard says U6 wires to POST /api/backfill, N5 guards overview with count query → agent implements exactly that Ambiguity is the enemy of autonomous agents. Resolve it in the shaping phase. ## The benefits 1. **Separation of concerns** — You think (shape). Agent codes (ralph). Clean boundary. 2. **Verifiable progress** — Each scope has verify criteria. Agent checks them before committing. 3. **Resumable** — Ralph reads the shaping docs + git log each run. Picks up where it left off. Stateless. 4. **Debuggable** — Commits reference affordance IDs. You can trace any line of code back to the breadboard. 5. **AFK-able** — `./ralph/afk.sh 10` and walk away. Seriously. --- The shaping-skills are open source: github.com/rjs/shaping-skills The ralph loop pattern is ~110 lines total (prompt + scripts). You can adapt it to any project. Shape the work. Let the agent execute.