Vibe Coding for Production
Scaling automationRalph Loops

Practicing Ralph loops

Hands-on exercise, multi-phase Snippet Vault API using Ralph, with Node or .NET 10+ paths, specs, and verification gates.

This page is a full workshop exercise: you (or your students) run a real Ralph loop on a small but non-trivial build. You will choose Node or .NET, drop in numbered specs, and let ralph-loop.sh drive iterations until every phase is green and marked complete.

Prerequisites: read What is Ralph Wiggum? and complete Implementing Ralph loops so scripts/ralph-loop.sh, specs/, RALPH.md, and verification are already understood.

Scenario: Snippet Vault

You are building Snippet Vault, a tiny backend that stores code snippets in memory (no database). Clients can create a snippet and list or fetch it by id. The point is not novelty; it is multi-phase delivery, clear acceptance criteria, and tests that must pass before the model may claim a phase done.

Pick one stack for the whole exercise (do not mix in one repo):

PathYou will use
NodeTypeScript or JavaScript, npm, a minimal HTTP server (Express or Fastify is fine), tests with Vitest or Jest, optional supertest for HTTP integration tests.
.NET.NET 10+ (SDK and runtime), Minimal APIs, xUnit (or NUnit) for tests, WebApplicationFactory for integration-style tests if you want one bootstrapped test.

Work in a fresh git repo on a feature branch (for example ralph/snippet-vault).

Setup (once)

Create the layout from Implementing Ralph loops (specs/, scripts/, logs/, ralph-loop.sh).

Initialize the project (npm init + TypeScript tooling or dotnet new web).

Use the stack toggle below (TypeScript / Node vs .NET 10+); copy exactly one RALPH.md block into the repo root as RALPH.md, matching the stack you chose. The same toggle filters the spec copy-paste blocks.

Copy the four spec files for that stack into specs/ using the filenames shown (e.g. 001-bootstrap-and-health.md).

Each spec is incomplete until you add a line exactly ## Status: COMPLETE at the bottom after work and verification are green. The copy-paste bodies below do not include that line, append it yourself when the phase is done.

RALPH.md, pick one (save as RALPH.md)

Fumadocs Tabs use groupId="workshop-ralph-stack" and persist so this heading and Spec files stay on the same stack, and your choice is remembered (including on Implementing Ralph loops).

# Ralph loop, Snippet Vault (TypeScript / Node)

## Product
Snippet Vault: in-memory HTTP API for code snippets (`title`, `language`, `code`). Workshop exercise; **TypeScript** + **npm** + **Express** or **Fastify** (your choice).

## Required verification (before any done token)
- **`npm install`** succeeds.
- **`npm run build`** passes if the project uses TypeScript compilation.
- **`npm test`** passes (Vitest or Jest, configure in phase 001 if needed).
- Add **`npm run lint`** only if you introduce ESLint; then it must pass too before `<promise>DONE</promise>`.

Do not print `<promise>DONE</promise>` or `<promise>ALL_DONE</promise>` until every command above that applies has **exited successfully** for the current phase.

## While in the loop
- Implement **one** spec at a time; add `## Status: COMPLETE` to that spec file only when its criteria and verification are green.
- Commit **locally** in logical chunks; **never `git push`** (no remote push, no force-push); a human publishes after review.

## Out of scope
- No database, auth, or deployment. No unrelated refactors or framework swaps.
# Ralph loop, Snippet Vault (.NET)

## Product
Snippet Vault: in-memory HTTP API for code snippets (`title`, `language`, `code`). Workshop exercise; **.NET 10+** **Minimal APIs**.

## Required verification (before any done token)
- **SDK:** **.NET 10** or later on `PATH` (`dotnet --version`).
- **`dotnet build`** passes for the solution or project.
- **`dotnet test`** passes once a test project exists (phase 001 may add an empty test project, or you add tests in later phases, still run `dotnet test` every iteration).

Do not print `<promise>DONE</promise>` or `<promise>ALL_DONE</promise>` until every command above that applies has **exited successfully** for the current phase.

## While in the loop
- Implement **one** spec at a time; add `## Status: COMPLETE` to that spec file only when its criteria and verification are green.
- Commit **locally** in logical chunks; **never `git push`** (no remote push, no force-push); a human publishes after review.

## Out of scope
- No database, auth, or deployment. No unrelated refactors.

Spec files

Copy each block into specs/<filename>. The .NET track uses the same filenames as the Node track so the loop can compare stacks consistently.

specs/001-bootstrap-and-health.md

# Phase 001, Bootstrap and health (TypeScript / Node)

## Goal
Runnable server skeleton with a health endpoint and a short README.

## Acceptance criteria
- [ ] `package.json` exists; **npm** scripts include **`start`** or **`dev`** to run the server on a **documented port** (default **3000** unless you document another).
- [ ] **GET** `http://localhost:<port>/health` returns **200** and JSON body `{"status":"ok"}` (or equivalent keys you document, must include `status: ok`).
- [ ] **README.md** at repo root: how to **`npm install`**, **`npm run build`** (if TypeScript), and start the server, in **≤ 15 lines**.
- [ ] **Vitest** or **Jest** is configured so **`npm test`** runs (may be a single placeholder test that passes).

## Verification commands (run before COMPLETE)
npm install
npm run build
npm test
# Then start the server and curl /health, or document the exact command you used.

specs/002-snippet-crud-core.md

# Phase 002, Snippet CRUD core (TypeScript / Node)

## Goal
In-memory storage and JSON API for snippets (no database).

## Acceptance criteria
- [ ] **POST** `/snippets` with JSON `{ "title", "language", "code" }` returns **201** and JSON including **`id`** (string) plus stored fields.
- [ ] **GET** `/snippets` returns **200** and a JSON **array** of all snippets in **creation order**.
- [ ] **GET** `/snippets/:id` returns **200** for a valid id; **404** for unknown id.
- [ ] Storage lives **in memory** in the running process only.

## Verification commands (run before COMPLETE)
npm run build
npm test

specs/003-validation-and-unit-tests.md

# Phase 003, Validation and unit tests (TypeScript / Node)

## Goal
Reject invalid input; prove behavior with **unit tests** (≥ 3 tests, not all happy path).

## Acceptance criteria
- [ ] **POST** `/snippets` returns **400** if `title` is missing, empty, or whitespace-only.
- [ ] **POST** `/snippets` returns **400** if `language` or `code` is missing.
- [ ] **At least three unit tests** cover validation or mapping logic.
- [ ] **`npm run build`** and **`npm test`** both pass.

## Verification commands (run before COMPLETE)
npm run build
npm test

specs/004-integration-smoke.md

# Phase 004, Integration smoke (TypeScript / Node)

## Goal
One **HTTP-level** test: POST a snippet then GET by id (no manual curl required to trust the flow).

## Acceptance criteria
- [ ] One test using **supertest** (or equivalent) against the app: **POST** `/snippets` with valid body, then **GET** `/snippets/:id`, assert response matches.
- [ ] **`npm run build`** and **`npm test`** pass.

## Verification commands (run before COMPLETE)
npm run build
npm test

specs/001-bootstrap-and-health.md

# Phase 001, Bootstrap and health (.NET)

## Goal
Runnable **.NET 10+** Minimal API with `/health` and a short README.

## Acceptance criteria
- [ ] **`dotnet run`** starts the app; launch URL and port are documented in **README.md** (≤ 15 lines total for install/run).
- [ ] **GET** `/health` returns **200** and JSON `{"status":"ok"}`.
- [ ] **xUnit** (or NUnit) test project exists; **`dotnet test`** runs (may be one empty passing test).

## Verification commands (run before COMPLETE)
dotnet build
dotnet test
# Optionally: curl /health after dotnet run, document if you use it.

specs/002-snippet-crud-core.md

# Phase 002, Snippet CRUD core (.NET)

## Goal
In-memory snippet store exposed as Minimal API routes.

## Acceptance criteria
- [ ] **POST** `/snippets` accepts `{ "title", "language", "code" }`, returns **201** with JSON including **`id`** (GUID string) and stored fields.
- [ ] **GET** `/snippets` returns **200** with JSON array in **creation order**.
- [ ] **GET** `/snippets/{id}` returns **200** for valid id, **404** for unknown id.
- [ ] Storage is **in-memory** only.

## Verification commands (run before COMPLETE)
dotnet build
dotnet test

specs/003-validation-and-unit-tests.md

# Phase 003, Validation and unit tests (.NET)

## Goal
Return **400** for bad input; **≥ 3 unit tests** (not only happy path).

## Acceptance criteria
- [ ] **POST** `/snippets` returns **400** if `title` is missing, empty, or whitespace-only.
- [ ] **POST** `/snippets` returns **400** if `language` or `code` is missing.
- [ ] **At least three unit tests** cover validation or mapping.
- [ ] **`dotnet build`** and **`dotnet test`** pass.

## Verification commands (run before COMPLETE)
dotnet build
dotnet test

specs/004-integration-smoke.md

# Phase 004, Integration smoke (.NET)

## Goal
One integration test: **POST** then **GET** by id using **`WebApplicationFactory`** (or equivalent host test).

## Acceptance criteria
- [ ] One test performs **POST** `/snippets` with valid JSON, then **GET** `/snippets/{id}`, asserts body matches.
- [ ] **`dotnet build`** and **`dotnet test`** pass.

## Verification commands (run before COMPLETE)
dotnet build
dotnet test

How to run the exercise

Ensure only specs without ## Status: COMPLETE are "active"; the loop picks one incomplete spec (lowest number first if filenames sort that way, use 001-…004- prefixes).

Start from Auto mode if you can; otherwise set PERM_MODE per Implementing Ralph loops.

Run the loop:

./scripts/ralph-loop.sh 25
./scripts/ralph-loop.sh 25

Git Bash / WSL:

bash scripts/ralph-loop.sh 25

After each iteration, read git diff and the log under logs/. Confirm the model actually ran your verification commands.

When 004 is complete, every required check passes independently, and the final-result log contains <promise>ALL_DONE</promise> as an exact standalone line, you are done.

If the model marks a spec complete without green tests, remove ## Status: COMPLETE from that spec, tighten RALPH.md, and run another iteration.

On this page