The afternoon session, "Claude Code, zero to expert", is three hours in six parts. Parts 3, 4 and 5 are hands on keyboard, and all three run in this repository. Everything you need is here: the set-up, the three exercises, the sample app, the tickets and the reference files.
- Set-up (before Part 3, five minutes):
workshop/SETUP.md. Four paths: local install, GitHub Codespaces (nothing to install), Claude Code on the web, or pair up. Ends with the three "are you green" checks. - Pre-work (the night before, 15 minutes):
docs/PRE-WORK.md. Install, clone, run the tests, and keep the brief and the definition of done handy.
| Part | Lab | Minutes | Badge | Ticket |
|---|---|---|---|---|
| 3 | Ship TODO-231: /init, plan and push back, build to green, /code-review, open the PR | 30 | Shipper | docs/tickets/TODO-231.md |
| 4 | Fit it to you: /usage, /security-review, a hook, /mcp on the database, the release-check skill | 25 | Toolmaker | |
| 5 | Put more agents on it: three subagents, a nightly routine, a remote session, a swap | 20 | Orchestrator | docs/tickets/TODO-232.md, TODO-233.md |
Each lab README has the copy-ready prompts, a done-when list, what to show a mentor for the badge, and what to do if you are stuck. Stuck five minutes: hand up.
| Path | Used in |
|---|---|
docs/tickets/ |
The three tickets |
.mcp.json |
Lab 4, the postgres MCP server that reads the local database |
docs/data/deliveries-last-30-days.csv |
Lab 4, the no-MCP fallback for the same question |
docs/examples/release-check.SKILL.md |
Lab 4, the skill template |
docs/examples/settings.hooks.json, docs/examples/hooks/pom-guard.sh |
Lab 4, the pom.xml hook |
docs/examples/agents/ |
Lab 5, the auditor, fixer and verifier subagents |
docs/examples/routine.md |
Lab 5, the nightly release check |
.devcontainer/ |
Codespaces: Java 17, Node 20, Claude Code and PostgreSQL 16, nothing to install |
docs/examples/ci.workflow.yml |
A GitHub Actions workflow that runs both suites on every pull request. Copy it to .github/workflows/ci.yml in your copy to switch CI on |
There is deliberately no CLAUDE.md and no .claude/ folder. You create them in Lab 3.
One badge per lab. A badge is one thing you show a mentor on your screen; they stamp your card. Three stamps put your card in the draw at show-and-tell, and a full card across the morning and afternoon sessions gets you named at the close.
Participants press Use this template on this repository to get their own copy (needed for Codespaces and Claude Code on the web). Do Lab 3 once yourself in a fresh Codespace before the day. The answer key for the database question is in docs/data/ANSWER-KEY.md.
The operations dashboard at Marlowe & Finch, a coffee-equipment company. A small Spring Boot REST API over a PostgreSQL database, plus a plain-JavaScript page that lives on a wall screen in the operations office: four KPI tiles, two charts, the late deliveries of the period, and the vendor contracts coming up for renewal.
Two sentences to explain it: the Java side reads orders, deliveries, tickets,
carriers and vendors with plain SQL (Spring JDBC, Flyway migrations, no JPA) and
serves read-only JSON under /api; the frontend in src/main/resources/static/
calls those endpoints with fetch for a date range and draws the tiles, two inline
SVG charts, the table and the list.
The data is fictional and generated by tools/make_seed.py: about 1,900 orders and
deliveries over the 90 days before 21 September 2026 across four carriers, 600
support tickets, and eight vendor contracts. One carrier, Kessler Logistics, has been
slipping all summer. The dashboard is meant to make that visible.
docker compose up -d db # PostgreSQL 16 on localhost:5432, database/user/password "ops"
./mvnw spring-boot:run # then open http://localhost:8080Flyway creates the schema and loads the seed on first start (V1__schema.sql,
V2__seed.sql). The database keeps its data between runs; docker compose down -v
wipes it. Connection details can be overridden with DATABASE_URL, DATABASE_USER
and DATABASE_PASSWORD.
SPRING_PROFILES_ACTIVE=demo ./mvnw spring-boot:runSame migrations, same seed, in an in-memory H2 database in PostgreSQL mode. Use this on a laptop without Docker and in Claude Code on the web. The tests always run on it.
The app's clock is pinned to 21 September 2026 (property ops.today, bean in
ClockConfig.java) so the default date range, the "days until renewal" figures and
the tests all agree with the seed data whatever the real date is. Every default range
is "the last 30 days ending today".
./mvnw test # Java: 25 tests (JUnit 5, H2, MockMvc)
npm install # once
npm test # Frontend: 45 tests (Jest + jsdom).mcp.json at the root registers a postgres MCP server that Claude Code starts on
demand with npx -y @modelcontextprotocol/server-postgres. It is read-only (every
query runs in a READ ONLY transaction) and points at the local compose database,
postgresql://ops:ops@localhost:5432/ops. In Codespaces the dev container shares the
database's network, so the same address works there. /mcp inside claude shows it
as postgres. Without Docker, the same question can be answered from
docs/data/deliveries-last-30-days.csv.
- Java 17, Spring Boot 3.2, Spring JDBC, Flyway, PostgreSQL driver, H2 for the demo profile and tests; Maven (wrapper included, no local Maven needed)
- Vanilla JavaScript, HTML and CSS; no build step, no framework; charts are inline SVG
- Jest with jsdom for the frontend tests; the harness lives in
src/test/javascript/setup/loadApp.js
ops-dashboard/
├── pom.xml Java build (dependencies are frozen, see comment at the top)
├── package.json Jest config and scripts
├── docker-compose.yml The PostgreSQL 16 service
├── .mcp.json The postgres MCP server for Lab 4
├── mvnw, mvnw.cmd, .mvn/ Maven wrapper
├── tools/make_seed.py Generates V2__seed.sql, the CSV and the answer key
├── src/main/java/com/marlowefinch/ops/
│ ├── OpsApplication.java Spring Boot entry point
│ ├── ClockConfig.java "Today" pinned to 2026-09-21
│ ├── DateRange.java from/to defaults (no validation yet, see TODO-232)
│ ├── DashboardRepository.java KPIs, on-time rate, late deliveries, tickets (SQL)
│ ├── VendorRepository.java Vendors with days until contract end
│ ├── KpiController.java /api/kpis
│ ├── DeliveryController.java /api/deliveries/on-time, /api/deliveries/late
│ ├── TicketController.java /api/tickets/by-category
│ ├── VendorController.java /api/vendors
│ ├── HealthController.java /api/health
│ └── Kpis, CarrierOnTime, LateDelivery, TicketCategoryCount, Vendor Response records
├── src/main/resources/
│ ├── application*.properties postgres (default) and demo profiles
│ ├── db/migration/ V1__schema.sql, V2__seed.sql (Flyway)
│ └── static/ index.html, app.js, style.css
├── src/test/java/... JUnit tests (migrations, repositories, controllers)
├── src/test/javascript/ Jest tests and the harness
├── docs/ Workshop material, tickets, data, examples
├── workshop/ Set-up and the three labs
├── .devcontainer/ GitHub Codespaces setup with PostgreSQL (no local install needed)
└── docs/examples/ci.workflow.yml CI workflow to copy into .github/workflows/
All endpoints are GET, return JSON and are read-only. from and to are ISO dates
(2026-09-14) and default to the last 30 days ending today. limit defaults to 20.
| Path | Returns |
|---|---|
/api/health |
{"status":"UP","today":"2026-09-21"} |
/api/kpis?from&to |
On-time delivery rate, open tickets, revenue and order count for the range |
/api/deliveries/on-time?from&to |
Delivered, on-time and rate per carrier (deliveries that arrived in the range) |
/api/deliveries/late?from&to&limit |
Late deliveries, worst first: order, carrier, promised and delivered dates, days late |
/api/tickets/by-category?from&to |
Tickets opened in the range per category: still open, and total |
/api/vendors |
Every vendor with annual spend, contract end, notice days, owner, days until contract end and whether that is inside the notice window |
There is no validation of from, to or limit: a malformed date is a 500, from
after to is an empty result, and limit can be anything. That is ticket TODO-232.
Workshop material is in workshop/ and docs/.