Case study 04

A local-first planning workspace for the stage before code exists.

Role
Solo engineer
Year
2026, open source
Stack
Next 16, React 19, TypeScript, Tailwind 4, zod
Links
703unit tests passing1
44test files2
8planned phases shipped, plus a three-phase repository track3
46commits on main4
  1. groundwork README test badge
  2. git ls-tree -r --name-only main, tests/**/*.test.ts
  3. README.md, "Status and roadmap"
  4. git rev-list --count main in the groundwork repo

The problem

Projects get started faster than they get thought through. The useful thinking at the start lands in a one-off plan file with no shared shape. Generic AI planners make it worse: they turn a vague brief into a confident, complete-looking plan built from template filler, and they overwrite your file to do it.

Groundwork is built so that failure cannot happen.

What I built

A local web app over a folder of plain markdown. You write the brief badly on purpose. Groundwork asks Claude to turn it into phases, task cards, risks and the questions it could not answer, as a proposal you review as a diff.

  • Proposes, never writes. Each run produces a proposal. You accept or reject each block, a snapshot is taken first, and one click reverts.
  • Asks instead of inventing. Gaps become entries in an open-questions queue, and your answers feed every later run.
  • Shows its source. Each card carries a verbatim quote from your brief, or is flagged as inferred or ungrounded.
  • Hands off. An export writes a CLAUDE.md and a task list for Claude Code to pick up.

Architecture

Web appNext 16, local onlylib/vault.tsthe only disk gateClaude Code CLIstream-json, denylistMarkdown vaultyour filesRun directoryproposal.jsonDiffaccept / reject

AI design

  • The model proposes, code disposes. The spawned CLI can only write to its run directory. A denylist keeps it out of the vault, and the app validates the proposal with zod before showing it.
  • Grounding is checked by string match, not by another model. A card either quotes your brief verbatim, or it is labelled inferred or ungrounded.
  • Progress is real. The app parses the CLI’s stream-json output so you see actual steps, and a run survives closing the tab.
  • Planning against real code. A connected repository is indexed with a local embedding model, and the model gets excerpts, never the repository path.

How it was built

The architecture and data model were written down in docs/ before the code, and a hard-rules file in the repo records each exception to the disk-access rule with the argument for it. Two live-model verification passes and two fresh-context reviews ran against a real repository, and the defects they found are written up as lessons.

Tradeoffs

  • It needs the Claude Code CLI. There is no Anthropic API engine yet. The engine interface exists for one, the implementation does not.
  • Prompt quality is judged by hand. Probe briefs bait specific failures, but a human reads the output against expectations. Nothing automates the judging.
  • A run takes minutes. One run at a time, enforced by a lock.
  • Grounding proves traceability, not quality. A real quote can still be stretched to justify an unrelated card.
WorkHire me