Skip to content

feat: ground contracts in facts before negotiating them#1

Merged
dasirra merged 3 commits into
mainfrom
feat/grounding-block
Jul 10, 2026
Merged

feat: ground contracts in facts before negotiating them#1
dasirra merged 3 commits into
mainfrom
feat/grounding-block

Conversation

@dasirra

@dasirra dasirra commented Jul 10, 2026

Copy link
Copy Markdown
Owner

The failure this fixes

A /forge:building run negotiated a contract whose fixtures bound the feature to a store the
generator invented
: a JSON file behind a new environment variable, modeled on a store the previous
issue in the same epic had legitimately created.

The real store already existed, and the capability the issue needed was missing from it. Adding
that capability was the actual work of the issue. Neither the spec, nor the epic, nor the issue, nor
the contract said so.

The contract then survived three adversarial rounds. The evaluator is forbidden to read source, so
the best it could do was record, in its residual risks, that the premise could not be checked from
where it sat. A human read that note at the --gate and asked where the store was. On the ungated
default it would have shipped green, with the next issue queued to build on top of the fiction.

Root cause

Forge treated a fact about an existing system as a deferrable decision.

Deferring a decision keeps options open, and it is this pipeline's best idea. Deferring a fact forces
every downstream agent to rediscover it, and each rediscovery is an unreviewed chance to invent.

The violated invariant is forge's own banner: "No agent grades its own work." The generator's
premise, "the store is a JSON file", is the one consequential output that nobody grades. The
evaluator audits contract-against-issue. The critic audits plan-against-spec. Nobody audits
issue-against-reality.

Every path that could have graded it was closed by the commands themselves:

  • interview.md: "Do NOT ask about schemas, file paths, libraries, or architecture."
  • planning.md, of the recon agent that had the answer in hand: "Its findings must never leak file
    paths or technical detail into issue bodies."
    The knowledge was gathered, then discarded by rule.
  • building.md: "There is no technical planning phase... technical decisions belong to the
    builders."
    But builders arrive in Phase 4, and the substrate was already fixed in the Phase 2
    fixtures, by the contract author, whose only reviewer may not read the code.

The decisive detail: for the previous issue, the identical generator move was correct, because
that store really was new. Inventing and discovering look identical from inside a contract. So the
discriminator cannot be better prompting. It has to be an existence check.

The change

Facts are exempt from the altitude ban. Decisions are not.

  • interview.md: a ## Grounding (facts, not decisions) section in the spec, recording every
    existing system the feature consumes. The command already explores the codebase; now it writes down
    what it found.
  • planning.md: the Light reality check becomes a Grounding phase, no longer optional outside
    greenfield. Its findings stop being discarded and become a grounding table, quoted into each child
    issue's ## Grounding block.
  • building.md:
    • the generator writes a grounding block before any criterion: every store, path, env var,
      collection, table, endpoint, package, or external system a fixture will name, each EXISTS (with
      file:line evidence) or NEW;
    • new step 1.5: the lead reproduces every EXISTS with git grep and refuses to relay a
      contract whose premises it could not reproduce. Any NEW persistent substrate escalates to the
      human with one question, gate or no gate;
    • the evaluator gains one BLOCKING objection: a fixture noun absent from the grounding block. It can
      raise this without reading code, because absence from a JSON block is visible in the artifact
      itself;
    • the gate is now on by default (--no-gate opts out), and it shows the grounding block and the
      evaluator's residual risks above the criteria. The human who caught the original defect did so
      by reading a paragraph the gate never required anyone to show him, on a run that happened to be
      gated. On the old default it would have shipped.

Why it works

The check is a grep plus a JSON-shape assertion, not an opinion, so it cannot drift the way a prompt
does. It is enforced twice by agents with opposed incentives: the lead checks EXISTS against the
repo, and the evaluator blocks ungrounded nouns from the artifact. For one to rot, the other must rot
in the same direction at the same time.

Replayed against the original failure, the invented environment variable greps to nothing, so it
cannot be EXISTS, so the human is asked one question before three adversarial rounds harden criteria
against a store that does not exist. Replayed against the previous issue, where the store genuinely
was new: one question, one yes, no friction. It discriminates precisely where the generator cannot.

Explicitly rejected

  • Give the evaluator code access. It would read the generator's own fixtures and the same
    precedent that misled the generator, and confirm the fabrication. Its isolation is what produced
    the residual-risks note that saved the run, and later a self-audit in which it reported two of its
    own earlier passes as unearned. That property is irreplaceable, and the grounding block buys the
    same coverage by handing it evidence as an artifact it can demand but not fetch.
  • Bring back PLAN.md, or add a technical design phase. The failure was not absent design, it was a
    false premise. A design phase consumes premises, it does not audit them.
  • Ask the human "what systems does this touch?" at Phase 0.5. Taxes every run with a question a
    grep answers exactly, and fails silently when the human's mental model is stale, which is the case
    grounding exists to catch. Invert it: the machine greps, and the human is interrupted only when the
    contract wants to invent.

Unchanged, deliberately

The adversarial negotiation, which in the run that exposed this caught seven blocking defects, three
of which would have failed a correct implementation. The evaluator's isolation. The amendment rule,
through which the recovery ran exactly as written. The altitude discipline itself: the conclusion is
not that PM artifacts should carry architecture, it is that a fact is not a decision, and the ban's
old wording could not tell them apart.

Also regenerates docs/pipeline.html and assets/pipeline.png: the page still described an
optional reality check, a building phase with no notion of what exists, and a run that goes unattended
end to end. Rendering it caught three claims the source changes had made false.

Bumps the plugin to 1.2.0, since the contract schema gains grounding and the gate default flips.

🤖 Generated with Claude Code

dasirra added 3 commits July 10, 2026 14:38
A building run negotiated a contract against a store the generator invented,
modeled on a store the previous issue had legitimately created. The real one
already existed, and the capability the issue needed was missing from it. That
was the actual work, and no artifact said so. The contract survived three
adversarial rounds, because the evaluator is forbidden to read source and could
only record, in its residual risks, that the premise could not be checked from
where it sat. A human caught it at the gate.

The pipeline treated a fact about an existing system as a deferrable decision.
Deferring a decision keeps options open, which is the pipeline's best idea.
Deferring a fact means somebody downstream invents it, and from inside a
contract, inventing and discovering look identical: for the previous issue the
same move had been correct, because that store really was new. The discriminator
has to be an existence check, not better prompting.

So: facts are exempt from the altitude ban, decisions are not.

- interview: a Grounding section in the spec records every existing system the
  feature consumes, found by the exploration the command already performs.
- planning: the reality-check agent's findings stop being discarded. They become
  a grounding table, quoted into each issue's Grounding block. The phase is no
  longer optional outside greenfield.
- building: the generator declares a `grounding` block before any criterion,
  each entry EXISTS with file:line evidence or NEW. The lead reproduces every
  EXISTS with a grep and refuses to relay a contract whose premises it could not
  reproduce. Any NEW persistent substrate escalates to the human before the
  evaluator sees the contract, gate or no gate. The evaluator gains one blocking
  objection, a fixture noun absent from the block, which it can raise without
  reading code.
- the gate shows grounding and residual risks above the criteria, because a
  human skims what he is shown first.

The evaluator keeps its isolation. It produced the note that saved the run, and
an evaluator that could check the generator's premise against the generator's
own fixtures would have confirmed the fabrication instead of flagging it.
The page still described a building phase with no notion of what already
exists, a reality check that was optional, and a run that goes unattended end
to end. None of that is true now.

- interview: the spec records what it finds, it does not only park it
- planning: grounding and reality check, skipped only on greenfield, and the
  altitude ban exempts facts
- building: the grounding block precedes the criteria, the lead reproduces
  every EXISTS with a grep, and a NEW store stops the run for one question
- the gate leads with grounding and residual risks, above the criteria
- human touchpoints gains that question, and the overview no longer promises
  an unattended run

PNG re-exported from the page at the same width.
The gate used to be opt-in, and the run that exposed the grounding gap was
saved only because it happened to be gated and the human happened to read a
paragraph the gate never required anyone to show him. On the default path that
defect would have shipped: green criteria, a passing contract, and the next
issue queued to build on the same fiction.

So the pause becomes the default. The contract is the last artifact a human can
correct cheaply. After it, every criterion, every builder and every sibling
issue inherits its premises, and the amendment rule turns changing one into an
event.

--no-gate keeps the old behavior for work whose premises you already trust.

The NEW-substrate question in phase 2 is unchanged and still fires either way:
it asks whether the contract may invent a store, while the gate asks whether the
contract is right.

Diagram and README regenerated: the build flow now shows the gate between the
contract and the builders, and the overview no longer promises an unattended run.
@dasirra
dasirra marked this pull request as ready for review July 10, 2026 13:58
Copilot AI review requested due to automatic review settings July 10, 2026 13:58
@dasirra
dasirra merged commit 39e2a7b into main Jul 10, 2026
1 check passed
@dasirra
dasirra deleted the feat/grounding-block branch July 10, 2026 13:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Forge’s PM/DEV pipeline docs to explicitly “ground” contracts in verifiable facts about existing systems before negotiating criteria, and flips /forge:building to gate-by-default (with --no-gate to opt out). It also documents the new grounding workflow across interview, planning, and building, and bumps the plugin version to reflect the contract schema change.

Changes:

  • Add a Grounding (facts-not-decisions) concept to /forge:interview and /forge:planning, and propagate it into child issues.
  • Extend /forge:building to require a grounding block prior to criteria, add a mechanical verification step, and make the human gate default (--no-gate to skip).
  • Regenerate pipeline reference docs and bump plugin version to 1.2.0.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Updates CLI flag naming and pipeline diagram to reflect gate-by-default + grounding-first contract flow.
docs/pipeline.html Regenerated pipeline reference reflecting grounding and default gating semantics.
commands/planning.md Adds Grounding as an explicit exception to the “no technical details” rule and mandates grounding/reality-check phase.
commands/interview.md Adds a Grounding section to the spec template and clarifies facts vs decisions in interview guidance.
commands/building.md Documents {surface, grounding, criteria} contract shape (now expanded) and introduces grounding verification + default gate behavior.
.claude-plugin/plugin.json Version bump to 1.2.0 for the contract schema and behavior change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md
@@ -55,7 +55,7 @@ The other two need:
|---------|----------|-------------|
| `/forge:interview [idea \| path/to/brief.md]` | PM | Relentless one-question-at-a-time grilling until you and Claude share an understanding of the idea, then synthesis into a PM-level spec. No code, no issues. |
| `/forge:planning [path/to/spec.md \| description]` | PM | A planner drafts an epic with user stories, a critic attacks it in a separate context, they iterate up to 3 rounds. Files the result as a GitHub epic with native sub-issues for async human review. No technical content: no files, no schemas, no architecture. |
Comment thread commands/building.md
Comment on lines +33 to +37
- `harness/contract.json`: `{surface, grounding, criteria}`. `surface` is the
evaluation surface resolved in Phase 0.5, recorded once so a resumed run never
re-derives it. `grounding` is the array of substrate the contract stands on,
each entry `{name, status: EXISTS|NEW, evidence, human_ack}`; see Phase 2.
Each criterion is
Comment thread commands/building.md
Comment on lines +135 to +137
criterion or fixture will name, each entry `{name, status: EXISTS|NEW,
evidence}`. EXISTS requires evidence: a `file:line` in the current worktree
that names the thing. NEW means this run creates it. Start from the issue's
Comment thread commands/building.md
states, errors, edge cases.
1.5 **Lead verifies grounding mechanically** (you, before relaying the
contract). This is a grep, not a judgment. For each EXISTS entry, run
`git grep -n <name>` (or `test -e` for paths) and confirm the cited
Comment thread commands/building.md
Comment on lines 188 to 189
Write objections into the contract file. If sound, mark all criteria
`agreed`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants