{"slug": "spec-forge-beyond-vibes-to-behaviorally-complete-design-specs", "title": "Spec Forge: Beyond Vibes to Behaviorally Complete Design Specs", "summary": "Spec Forge, a Claude Code skill by blentz, adversarially certifies design specifications as behaviorally complete before code is written, defining completeness as no observable residual freedom under a declared observation alphabet. The skill runs adversarial phases to produce distinguishing implementation pairs, requires human ratification, and converts answers into acceptance tests, with a certificate that names the agent class and toolchain. It is available via git clone and requires the Workflow tool, which is not delegable to subagents.", "body_md": "A Claude Code skill that adversarially certifies a specification as **behaviorally\ncomplete** — before any code is written.\n\nThe goal is not a longer spec. It is a spec with no observable residual freedom:\n\nSpec`S`\n\nis behaviorally complete iff every implementation satisfying`S`\n\nis observationally equivalent to every other, under a declared observation alphabet`Σ`\n\n.\n\nNames, formatting, and internal structure are not in `Σ`\n\n, so a complete spec does not\nhave to mention them. What it must not do is leave a coding agent guessing about\nbehavior that `Σ`\n\ncan see.\n\nIf you hand a spec to a coding agent and it stalls — asking questions, or worse, silently inventing unstated behavior — the spec was underdetermined. That is measurable without rendering the application:\n\n**A spec is underdetermined exactly where an adversary can exhibit two implementations\nthat both satisfy it and diverge on Σ.**\n\nThat divergence is the gap, it is localized, and it is already a test case. spec-forge\nruns adversaries to produce those distinguishing pairs, has a human ratify each one, and\nturns the ratified answers into clauses *and* into the acceptance suite. You never author\nthe acceptance suite; you harvest it from the ambiguity resolution.\n\nTwo limits are stated up front, because pretending otherwise would be a lie:\n\n**The limit is Rice, not Gödel.**\"Is`S`\n\ncomplete?\" is undecidable in general, so the process is adversarial and statistical and issues a**certificate**, never a proof. The certificate has a mandatory RESIDUALS section.** Completeness is relative to**`(agent class A, toolchain T)`\n\n.`spec_bits + prior_bits + toolchain_bits ≥ behavioral_entropy`\n\n. A non-zero integer type discharges \"quantity is always ≥ 1\" totally and unspoofably; a language without that type needs prose plus a runtime validator plus a property test to get the same teeth. The same spec can be complete in Rust and incomplete in Python, so the certificate names`A`\n\nand`T`\n\n.\n\nSpec completeness = the coding agent's question count + its uncited-decision count. Both zero, or you are not done.\n\nNot \"a human read it and liked it.\" That is not a gate, it is a chore, and it is the first thing anyone skips.\n\nThe repo root *is* the skill. Put it where Claude Code looks for skills:\n\n```\ngit clone https://github.com/blentz/spec-forge ~/.claude/skills/spec-forge\n```\n\nThen ask Claude something like *\"is this spec complete?\"* or *\"certify this spec\"* and the\nskill triggers. Python assets are stdlib-only; `spec_gate.sh`\n\nneeds bash and `python3`\n\n.\n\n**Prerequisite: the Workflow tool.** Every phase from 1 on is a\n\n`Workflow({ scriptPath })`\n\ncall, and it is **not delegable**— subagents do not get the tool, so you cannot hand this skill to a\n\n`general-purpose`\n\nagent and walk away. Drive it from the main loop.The `workflows/*.js`\n\nfiles are Workflow scripts, not Node programs. `node workflows/ontology.js`\n\nwill fail: `agent()`\n\n, `parallel()`\n\n, and `phase()`\n\nare injected by the Workflow runtime.\n\n```\nPhase 0    OBSERVATION.md     declare Σ — what counts as observable\nPhase 0.5  SPEC.md            CLAUSIFY: prose → S-NNN clauses\nPhase 1    ONTOLOGY.md        subject × stimulus grid; the adversary work-list\nPhase 2    WITNESSES.md       underdetermination loop — run until DRY        ←──────┐\nPhase 2b   contradiction      contradiction hunter — run until CLEAN                │\nPhase 2c   ratification       did the answer actually ANSWER? earns `seen`          │\nPhase 3    spec-mutation      anti-vacuity: every clause must carry bits            │\nPhase 4    CERTIFICATE.md     Σ, A, T, dry rounds, RESIDUALS                        │\nPhase 5    DISCHARGE.md       route each clause to its cheapest enforcement rung    │\nPhase 6    build loop         harness first, implementation last  ─────────────────┘\n                                                    uncited decision = new witness\n```\n\nPhase 1 is 3% of the spend and finds the worst defects — including entities the author never\nnamed, which cannot show up as a `MISSING`\n\ncell because they have no cell. Never skip it.\n\nUse the driver. Do not drive it by hand.\n\n```\nassets/forge.py init   spec/                      # scaffold EMPTY templates\nassets/forge.py status spec/                      # loop state, Σ lint, void counters\nassets/forge.py next   spec/                      # what to run now, and why\nassets/forge.py next   spec/ --sigma              # the `sigma` arg every workflow needs\n\nassets/forge.py redact spec/ underdetermination \\\n  --cells cells.json --round 0 --out canary/SPEC.md  # the canary's spec + its receipt\n\nassets/forge.py record spec/ underdetermination out.json --transcripts .forge-transcripts/under-0/\nassets/forge.py ratify spec/ decisions.json       # → ledger + clauses + PENDING verification\nassets/forge.py verify spec/ ratification.json    # → and only NOW is `seen` earned\n\nassets/spec_gate.sh spec/                         # every mechanical check, one command\n```\n\nTwo rules the driver enforces mechanically, because leaving them to an agent's good intentions is \"human review is not a guardrail\" applied to the agent:\n\nA`--transcripts`\n\nis not optional.`record`\n\nwithout it VOIDS the round, deliberately. Blindness is not a promise an agent makes; it is a property of a run, checked against that run's own transcripts. A round you cannot prove was blind is a round you cannot use.**A round whose canary failed is VOID, not DRY.**`forge record`\n\nreads the workflow's own`void: true`\n\nand refuses to advance the dry counter. You cannot forget it or talk yourself out of it.**The canary must be aimed where the workflow aimed it.** Canary liveness is tested by giving a probe a spec with specific clauses physically stripped out.`forge redact`\n\npicks those clauses the same way the workflow does and leaves a receipt;`forge record`\n\nand`forge verify`\n\ncheck the receipt against what the workflow says it targeted, and VOID on disagreement. A misaimed canary does not fail — it finds some*other*gap and passes.\n\n`ratify`\n\nexists because a resolution must land in three places or the pipeline lies: the\n**ledger** (`WITNESSES.md`\n\n, which *is* the acceptance suite Phase 6 harvests), the **clause**\nit forced into existence, and the ** seen key** so the next round does not re-surface it. A\n\n`DON'T-CARE`\n\nis the easiest to lose, because it produces no clause and so looks like nothing\nhappened — it is a carve-out, it goes in the ledger and in RESIDUALS, and the harness must\nnot assert either branch.| tool | when | what it refuses |\n|---|---|---|\n`assets/forge.py` |\nevery phase | the driver: void/dry accounting, `seen` earning, phase gating |\n`assets/blindness_lint.py` |\nevery `forge record --transcripts` |\na round whose agents read source, an oracle, or a reference — greps the run's own transcripts, including `Bash` , not just `Read` /`Grep` /`Glob` |\n`assets/grid_lint.py` |\nafter Phase 1 | an incomplete cross-product, a dangling or orphan citation, an unjustified `IGNORE` , weasel text; also emits `--fanout` , the Phase 2 work-list |\n`assets/citation_lint.py` |\nduring Phase 6 | code→clause and clause→code; a clause nothing cites is a silently-dropped requirement |\n`assets/cert_lint.py` |\nbefore Phase 4 ships | a stale hash pin, a missing `(A,T)` , too few non-void dry rounds, unattested blindness, pending verification, surviving zero-bit clauses, unresolved contradictions, a missing discharge table, missing RESIDUALS. Warns on discarded rounds — blindness failures and misaimed canaries |\n`assets/redact_spec.py` |\nPhase 2 / 2c canaries | a canary that only asks an agent to ignore clauses it can still read |\n`assets/spec_gate.sh` |\nCI, and before trusting a certificate | runs all of the above: Σ, structure, certificate, consistency, blindness, provenance — plus a syntax check on `workflows/*.js` , because a phase that dies on a `SyntaxError` never ran |\n\nMeasured on a real 9-clause pass, not estimated.\n\n| phase | agents | tokens | what it bought |\n|---|---|---|---|\n| Phase 2 underdetermination | 30 | 727k | 4 root causes |\n| Phase 3 spec-mutation | 20 | 476k | 9 clauses |\n| Phase 5 discharge | 10 | 401k | the discharge table |\n| Phase 2b contradiction | 17 | 387k | 2 contradictions |\n| Phase 2c ratification | 11 | 258k | 3 answers checked |\n| Phase 6 build | 2 | 169k | harness + implementation |\n| Phase 1 ontology | 2 | 66k | 19 absent entities, including a shopping cart with no price |\n\nThe one lever that mattered: Phase 2c has already proved each clause load-bearing, so Phase 3\ndoes not need to re-prove it. Passing `provenClosed`\n\nto `spec-mutation.js`\n\nmade those clauses\ncost **zero agents** while carrying stronger evidence — a killing input a human actually\nratified. Full pass went from **127 agents / 3574k to 92 agents / 2482k, identical verdicts.**\n\nOn a small spec (one function, one module), set adversary replication to 1 per cell and\nrecover replication *across* rounds instead of within them. If `grid_lint --fanout`\n\nasks for\nmore adversaries than clauses × 3, the subject axis has degenerated — that is a Phase 1\nfinding, not a budget problem.\n\n- Skipping Phase 0. The loop drowns in naming bikeshed and the user learns to ignore witnesses.\n- Shipping Phase 2 without Phase 3. You have built a machine that teaches users to pad.\n- One judge. Judges are agreeable by construction.\n- Counting a dry round with no canary. That is fatigue, not convergence.\n- Letting the agent that produced a witness also resolve it — it now authors both the question and the answer.\n- An\n`IGNORE`\n\ncell with no written justification. - A certificate with no RESIDUALS.\n- Treating \"complete\" as absolute rather than relative to\n`(A, T)`\n\n.\n\n```\nSKILL.md                              the skill itself — the full method\nassets/*.template                     OBSERVATION / SPEC / ONTOLOGY / WITNESSES / CERTIFICATE\nassets/forge.py                       the driver\nassets/*_lint.py, spec_gate.sh        the mechanical gates\nworkflows/*.js                        Workflow scripts, one per phase\nreferences/lessons.md                 the scars: how an agent games each gate. Read first.\nreferences/observation-alphabets.md   Σ: twelve dimensions, class templates, five mistakes\nreferences/ontology-grid.md           Phase 1: subject × stimulus, verdicts, fan-out policy\nreferences/toolchain-profiles/        Python (highest spec bar) and Rust (lowest), side by side\n```\n\nGNU Affero General Public License v3.0. See [LICENSE](/blentz/spec-forge/blob/main/LICENSE).", "url": "https://wpnews.pro/news/spec-forge-beyond-vibes-to-behaviorally-complete-design-specs", "canonical_source": "https://github.com/blentz/spec-forge", "published_at": "2026-08-13 14:28:45+00:00", "updated_at": "2026-08-13 14:43:46.993453+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents"], "entities": ["Spec Forge", "Claude Code", "blentz", "Workflow"], "alternates": {"html": "https://wpnews.pro/news/spec-forge-beyond-vibes-to-behaviorally-complete-design-specs", "markdown": "https://wpnews.pro/news/spec-forge-beyond-vibes-to-behaviorally-complete-design-specs.md", "text": "https://wpnews.pro/news/spec-forge-beyond-vibes-to-behaviorally-complete-design-specs.txt", "jsonld": "https://wpnews.pro/news/spec-forge-beyond-vibes-to-behaviorally-complete-design-specs.jsonld"}}