I Tested Matt Pocock's Agent Skills — This Was the Result
I tried Matt Pocock's engineering skills harness on a Java CRUD refactor. First run skipped architecture; second run asked 17 questions—and this is what came out.
I tested Matt Pocock’s agent skills on a real refactor. This is what came out—not a polished demo, but a conversation with an LLM and the code it produced afterward.
The short version: first attempt, I asked for a CRUD and the agent skipped architecture. Second attempt, I said “ask everything again” and got 17 explicit decisions before a single line changed.
Why the repo is called faruk-base2
faruk-base (the first repo) was me picking skills one by one—install this, try that, wire my own workflow by hand. It worked well enough to learn the mechanics, but there was no coherent harness: no router, no grilling protocol, no spec/ticket chain when things got big.
faruk-base2 is the opposite bet. I cloned a template built around Matt Pocock’s engineering skills—someone with more experience than me curating which skills exist and how they chain. Install with npx skills add mattpocock/skills, restore from skills-lock.json, read HELP-SKILLS.md.
I renamed the entry router locally from /ask-matt to /ask-fmz. Same skill content, different invocation name in .agents/skills/, .claude/skills/, and agent/skills/. When you see /ask-fmz below, read mattpocock/skills.
The repo is a skills testbed. This session focused on examples/java-crud—a Product REST API on Java 21, Spring Boot 4.1, and PostgreSQL in Docker:
| Item | Value |
|---|---|
| API | http://localhost:8090/api/products |
| Stack | Java 21, Spring Boot 4.1, PostgreSQL 17 |
| SQL editor (host) | localhost:5433 |
Each example has CONTEXT.md and ADRs under docs/adr/. The workspace rule: technical decisions only after my confirmation, one at a time.
Attempt one: “make a CRUD” — and the agent skipped architecture
Before the session documented here, I asked the agent—using Matt’s skills—to build a CRUD. It ran. Code appeared. Docker came up.
What it did not do: walk me through architecture. No layered design question. No schema strategy. No “where does the database live?” moment. It assumed defaults and moved on.
That is the failure mode I care about. An LLM with skills is still an LLM: if the prompt is vague and the skill chain does not force an interview, you get a CRUD, not your CRUD.
So I opened a new thread and attached /ask-fmz with a clearer instruction:
Rebuild the Java module and ask everything again.
Attempt two: the flow we actually used
flowchart LR
A["/ask-fmz"] --> B["/grill-with-docs<br/>17 questions, one at a time"]
B --> C["Shared understanding<br/>summary + my confirmation"]
C --> D["/implement<br/>TDD + tests + commit"]
We did not use /to-spec, /to-tickets, /handoff, /prototype, or a formal /code-review with parallel sub-agents. Scope was already closed after grilling—same session, straight to /implement.
Matt’s full harness supports a longer path (/to-spec → /to-tickets → per-ticket /implement). That is for multi-session builds. This was a revalidation of an existing example, not a greenfield feature.
The conversation, turn by turn
Below is the session arc—not every word, but the decisions as they happened.
Turns 1–4 — Stack baseline
Me: /ask-fmz rebuild the Java module and ask everything again
Agent: Read the current java-crud (Spring Boot 4.1, Java 21, PostgreSQL, H2 in tests, ddl-auto, Swagger always on). Started /grill-with-docs.
| Turn | Question | My answer |
|---|---|---|
| 1 | Refactor goal | A — same Product CRUD, revalidate stack and decisions |
| 2 | Java version | A — Java 21 |
| 3 | Spring Boot | A — 4.x |
| 4 | Runtime database | (see turn 5) |
Turn 5 — “Where will this database live?”
Question 4 was picking a runtime database. Before choosing, I interrupted:
Tell me where this database will live?
I did not want a checkbox labeled “PostgreSQL.” I wanted a picture of what lands on my machine when I run docker compose up.
Agent: Instead of assuming, it read docker-compose.yml and answered with a diagram—then re-asked question 4 with a table mapping each option to host ports and containers:
flowchart TB
subgraph host["Your machine"]
Editor["SQL editor"]
Client["Browser / curl"]
end
subgraph docker["Docker Compose"]
App["java-crud"]
DB[("PostgreSQL<br/>container: db")]
Vol[("volume<br/>product_pg_data")]
end
Editor -->|"localhost:5433"| DB
Client -->|"localhost:8090/api/products"| App
App -->|"jdbc:postgresql://db:5432/products"| DB
DB --- Vol
That is the moment grilling clicked for me. The agent did not decide—I still picked PostgreSQL in Docker (option A). But I could see why: nothing installs Postgres natively on Windows; data survives restarts in a named volume; my editor hits 5433, the API hits 8090, and the app talks to db:5432 on the internal network.
Turn 6 — A requirement born in chat
Once I understood the layout, I answered—and added something the checklist never asked:
A, but I always need to know how to access this database from my SQL editor.
That sentence became a README section—“Database access (SQL editor)” (Acesso ao banco (editor SQL) in the repo; docs stay pt-BR) with copy-paste host, port, user, password. Not code. Still part of the deliverable.
| Turn | Topic | My answer |
|---|---|---|
| 5 | Test database | A — H2 in memory (test profile) |
| 6 | Schema tooling | C — Liquibase (replacing ddl-auto: update) |
| 7 | Swagger | B — dev profile only |
| 8 | Host ports | A — API 8090, Postgres 5433 |
Turns 11–17 — Architecture, tests, language
| Turn | Topic | My answer |
|---|---|---|
| 9 | Validation rules | A — keep current (name max 120, price ≥ 0.01, etc.) |
| 10 | Layers | A — Controller → Service → Repository + DTOs + exception handler |
| 11 | Docker build | A — mvn test then mvn package in Dockerfile |
| 12 | Default compose profile | A — dev (Swagger on at docker compose up) |
| 13 | Liquibase in H2 tests | A — same changelog as production |
| 14 | HTTP error format | B — { "error": "..." } in English |
| 15 | Integration test scope | B — “always test more things so we don’t have bugs” |
| 16 | List ordering | B — createdAt DESC |
| 17 | Docs vs code language | Docs pt-BR; code, tests, HTTP messages, Swagger labels, commits English |
After turn 17, the agent showed a consolidated table of all 17 decisions plus a before/after diagram. I replied with /implement.
What changed in the module
| Before | After |
|---|---|
ddl-auto: update | Liquibase + ddl-auto: validate |
| Swagger always on | Swagger only in dev; off in prod |
Errors in Portuguese (Produto não encontrado) | English (Product not found) |
| List order unspecified | findAllByOrderByCreatedAtDesc() |
| 4 integration tests | 9 integration tests |
| README without SQL access block | Dedicated SQL editor section |
After /implement
The agent committed 90526d2 — refactor(java-crud): revalidate example with Liquibase and expanded tests. The diff matches the table above: schema tooling, Swagger profiles, English errors, list ordering, tests, and README.
Worth showing only one snippet—the Liquibase precondition for legacy Docker volumes. The old setup used ddl-auto: update; my volume still had a products table but no DATABASECHANGELOG row. First boot after the refactor failed with relation "products" already exists. This fixed it:
# db.changelog-master.yaml
preConditions:
- onFail: MARK_RAN
- not:
tableExists:
tableName: products
Everything else was structural: ProductRepository.findAllByOrderByCreatedAtDesc(), @Profile("dev") on OpenAPI, nine cases in ProductCrudIntegrationTest, and three new ADRs under docs/adr/.
How we checked it
cd examples/java-crud
docker compose --profile test run --rm test # 9/9
docker compose up --build -d
curl http://localhost:8090/actuator/health
curl http://localhost:8090/api/products/9999 # {"error":"Product not found"}
Swagger UI returned 200 under the default dev profile. SQL editor: jdbc:postgresql://localhost:5433/products (user/pass products).
What I take from this
The prompt is the product. Same skills, two sessions, opposite outcomes. “Make a CRUD” let the agent skip architecture. “Ask everything again” forced /grill-with-docs to run as designed—17 questions, one per turn, each with options and a recommendation. Skills route behavior; they do not fix a vague ask.
Conversation is a requirements channel. The SQL editor requirement was not in any ADR until I typed it mid-grill. It became a README section with copy-paste JDBC settings. The best specs sometimes arrive as a side comment, not a ticket.
Facts and decisions stay separate. When I asked where the database would live, the agent read docker-compose.yml and explained containers, volumes, and ports. I still had to pick PostgreSQL over H2. That is the rhythm I want: the agent surfaces what is true in the repo; I own the choice.
Versioning exposes real debt. Moving from ddl-auto to Liquibase broke startup on an old Docker volume—a table existed without a DATABASECHANGELOG row. Demo-friendly defaults hide that until you commit to migrations. The precondition in the changelog was not theoretical; it unblocked the deploy.
One session was enough here. We skipped /to-spec and /to-tickets because grilling closed the scope. For a multi-day build with blocking edges between tickets, I would take the longer path. The harness gives you both; picking the short path is a decision too.
Repo: faruk-base2. Skills upstream: mattpocock/skills. Local router: /ask-fmz. Full session handoff: resumo.md in the repo.
Comments
Comments powered by GitHub — sign in to join the discussion.