Skip to main content
You proved the demand. Now turn it into a plan your coding agent can build from. build_spec maps the demand report you already ran to a feature list — every feature grounded (backed by a real quote) — and scaffold writes a project on disk for your agent (Claude Code, Cursor, etc.) to build inside. metalworks writes the spec and scaffolds the project. It does not write your product code. No React components, no API routes, no migrations. It carries the demand forward — as a spec and a frozen quote table — so the agent that does write the code can’t drift from what real users asked for.

What you give it / what you get back

You give it: a finished Research bundle (the report on .demand), your positioning, and a target surface ("auto" to let the spec choose). The report argument to the CLI is a stored report id or prefix (metalworks research list), a path to a report.json, or — omitted entirely — your latest run. You get back: a BuildSpec — each line tied to a real quote:
The grounding rules are what make the spec safe to build from:
  • Features. The model proposes features from the demand clusters; metalworks attaches each cluster’s real quotes as the feature’s evidence. A feature with no real cluster behind it is dropped — the build stays tied to real demand. What survives is only what people actually asked for (capped at 8 core features).
  • Build order is grounded, not arbitrary. Features come back ordered by the demand strength of the cluster behind them (source_cluster_rank, 1 = strongest). features[0] is the spine — the feature to build first — and SPEC.md renders them as a numbered build order. The sequence is a deterministic read of real demand, not a model’s guess at importance.
  • Surface. With surface="auto" the same model call that maps features also picks the surface (sdk / web / mobile / cli / …) and returns a one-line surface_rationale, grounded in who asked and how technical they are — no generic web-app default, no second model call. Pin a surface and the spec honors it and skips the pick (no rationale).
  • Screens. The UX skeleton is sketched after the features exist, so each screen maps to real feature_ids (the old standalone skeleton was blind to what got built). A screen inherits its feature’s evidence: it ships validated when a real voice backs it, an unvalidated hypothesis otherwise. Shell screens (auth/settings) are flagged scaffolding — needed by every product, not a demand bet.
  • Personas. Derived from the report’s audience segments, each tied to a real voice.
  • Pricing. Tiers are copied straight from the report’s price evidence (Starter at the low end of observed willingness to pay, Pro at the high end). No price signal → no tiers.
Then scaffold writes the project — pure deterministic templating, no model, idempotent:
EVIDENCE.md is the frozen verbatim quote + permalink table — every id SPEC.md cites appears here with its exact Reddit text and a source link. The agent must not edit it; to add a feature, go back to metalworks and re-run the research.

How your coding agent uses it

Once the project is written, metalworks is done. The hand-off:
  1. Open the scaffolded directory in your coding agent (the project’s CLAUDE.md and skills are now in scope).
  2. Run /scaffold-startup. The agent reads docs/SPEC.md (what to build) and docs/EVIDENCE.md (the proof), picks the stack hint’s starter, stands up the surface, and builds the features in order — top is the spine (strongest demand), each one a demand a real user voiced.
  3. The lint holds it to the evidence. As the agent writes user-facing copy, a hook runs cite_or_die.py on the edited files. A headline that cites an id not in EVIDENCE.md fails the build; the agent has to find the real supporting quote or drop the claim.

When the result is thin

When the report can’t back a buildable plan, the spec comes back partial with a caveat. A partial spec still writes (so you can read the evidence), but it opens with a “Partial spec” banner instead of pretending to be buildable. The CLI prints the caveat before scaffolding. One honesty guarantee: an infra error (a 404, an auth failure, a network blip during the model call) surfaces as a real failure — it’s never silently relabelled a thin-demand partial. scaffold() raises ValueError if the spec wasn’t built from the report you pass it — otherwise the frozen EVIDENCE.md would resolve the spec’s refs against the wrong report. Always scaffold a spec against the report it came from.
Next: plan where it gets distributed — distribution and GEO / LLM-citability — from the same report. Or read why you can trust the output — the rule that keeps every feature tied to a real quote.