Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "forge",
"version": "1.1.1",
"version": "1.2.0",
"description": "Idea-to-PR pipeline with a human gate at each seam: grill an idea into a spec, plan it adversarially into GitHub issues, then implement it against a negotiated contract and ship a PR.",
"author": {
"name": "Daniel Asirra",
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
| `/forge:building <#issue ...> [--gate] [--max-rounds N] [--base <branch>] [--surface <name>]` | DEV | A generator and evaluator negotiate a granular contract of "done" against the live codebase, a team builds in an isolated worktree, then the evaluator black-box tests the running artifact against that contract until it passes: driving a browser for a web app, calling the public API for a library, running argv and reading exit codes for a CLI. Opens a PR. |
| `/forge:building <#issue ...> [--no-gate] [--max-rounds N] [--base <branch>] [--surface <name>]` | DEV | A generator and evaluator negotiate a granular contract of "done" against the live codebase, a team builds in an isolated worktree, then the evaluator black-box tests the running artifact against that contract until it passes: driving a browser for a web app, calling the public API for a library, running argv and reading exit codes for a CLI. Opens a PR. |

## Pipeline

Expand Down Expand Up @@ -92,7 +92,8 @@ flowchart TD
direction LR
b0["0.5 · Preflight<br/>gh + evaluation surface"] --> b1["1 · Isolated worktree"]
b1 --> b2["2 · generator ⚔ evaluator<br/>negotiate the contract<br/>max 3 exchanges"]
b2 --> b4["4 · sonnet builders,<br/>one per workstream"]
b2 --> b3{{"👤 3 · approve the contract<br/>grounding + residual risks first<br/>--no-gate skips"}}
b3 --> b4["4 · sonnet builders,<br/>one per workstream"]
b4 --> b5["5 · Static checks"]
b5 --> b6["6 · evaluator black-box tests<br/>the running artifact<br/>max 5 rounds"]
b6 -.->|"ROUND_FAILED<br/>fresh fixer, sees only the critique"| b4
Expand All @@ -106,6 +107,10 @@ flowchart TD

The contract negotiated in Phase 2 is the only plan `/forge:building` makes. There is no PLAN.md. Technical decisions belong to the builders, made against the code as they work, and Phase 6 judges the result against the contract rather than against the builders' account of it.

Facts are the exception, because a fact is not a decision. Before it writes a single criterion, the generator declares every store, path, env var, collection, and dependency its contract will name, each marked `EXISTS` with `file:line` evidence or `NEW`. The lead reproduces each `EXISTS` with a grep, and any `NEW` persistent substrate stops the run for one human question, gate or no gate. Deferring a decision keeps options open; deferring a fact just means somebody downstream invents it, and inventing and discovering look identical from inside a contract.

For the same reason, `/forge:building` pauses on the negotiated contract by default, showing you the grounding block and the evaluator's residual risks before the criteria. The contract is the last artifact you can correct cheaply: after it, every criterion, every builder and every sibling issue inherits its premises. `--no-gate` runs straight through when you already trust them.

Every phase, agent, artifact, and loop is laid out in the [full pipeline reference](https://htmlpreview.github.io/?https://github.com/dasirra/cc-forge/blob/main/docs/pipeline.html), which also documents the evaluation surfaces. Source: [`docs/pipeline.html`](docs/pipeline.html).

## Design
Expand Down
Binary file modified assets/pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
100 changes: 82 additions & 18 deletions commands/building.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Implement signed-off issues end to end with a team of agents in an isolated worktree. Before coding, a generator and an adversarial evaluator negotiate a granular contract of what "done" means; after coding, the evaluator black-box tests the running artifact against that contract until it passes. Ships as a PR.
argument-hint: <#issue | #issue #issue ... | "description of the work"> [--gate] [--max-rounds N] [--base <branch>]
argument-hint: <#issue | #issue #issue ... | "description of the work"> [--no-gate] [--max-rounds N] [--base <branch>]
---

# /forge:building
Expand Down Expand Up @@ -30,9 +30,11 @@ rubber-stamp evaluations.

All shared state lives in `harness/` inside the worktree (gitignored):

- `harness/contract.json`: `{surface, criteria}`. `surface` is the evaluation
surface resolved in Phase 0.5, recorded once so a resumed run never
re-derives it. Each criterion is
- `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 on lines +33 to +37
`{id, issue, criterion, verify_how, status: proposed|agreed|pass|fail}`.
`issue` is the issue number the criterion belongs to, or `"integration"`
for cross-issue behavior. Single-issue and free-form runs use one issue
Expand Down Expand Up @@ -60,10 +62,11 @@ number does not resolve, stop and report it rather than guessing. If the
request is free-form, restate it as a problem statement plus acceptance
criteria before proceeding.

Flags: `--gate` pauses for human approval of the negotiated contract before
building (default is fully autonomous). `--max-rounds N` caps evaluation
rounds (default 5). `--base <branch>` forces the base branch.
`--surface <name>` forces the evaluation surface.
Flags: the run pauses for human approval of the negotiated contract before
building. `--no-gate` skips that pause and runs autonomously, for work whose
premises you already trust. `--max-rounds N` caps evaluation rounds
(default 5). `--base <branch>` forces the base branch. `--surface <name>`
forces the evaluation surface.

## Phase 0.5: Preflight

Expand Down Expand Up @@ -125,7 +128,21 @@ granular, testable contract. Two subagents, separate contexts, artifacts
only.

1. **Generator proposes** (opus): given the issue bodies and read access to
the codebase, write `harness/contract.json` with 10-20 granular criteria
the codebase, write `harness/contract.json`.

Before any criterion, write the `grounding` block: every store, file path,
env var, collection, table, endpoint, package, or external system that any
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 on lines +135 to +137
Grounding block if it has one; contradicting that block is allowed only by
surfacing the contradiction, never by silently overriding it. Inventing a
substrate is sometimes right, and the generator cannot tell the difference
from the inside: an issue that needs a store the project lacks and an issue
whose store it simply failed to find look identical while writing criteria.
That is what `status` is for. Declare, do not decide.

Then write 10-20 granular criteria
PER ISSUE, each tagged with its `issue`, plus 3-8 `integration` criteria
covering behavior that spans issues (only for multi-issue runs). Never
dilute: three issues means roughly 40-60 criteria, not 30 split three
Expand All @@ -139,11 +156,35 @@ only.
"movement works", never "handles empty input gracefully". Cover the
unhappy paths the issue's Proposed behavior section describes: empty
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
evidence. An EXISTS entry with no hit goes back to the generator: do not
relay a contract whose premises you could not reproduce. Then scan the
fixtures and every `verify_how` for substrate-shaped tokens (ALL_CAPS
identifiers, slash paths, collection and table names, URLs, package names)
and confirm each appears in `grounding`. A token with no entry blocks the
relay.

Any NEW entry naming persistent substrate (a store, schema, collection, env
var, or external dependency) is escalated to the human with AskUserQuestion
before the evaluator sees the contract, **gate or no gate**: "this contract
invents `<X>`; confirm nothing existing should serve instead." Record the
grep output and the human's answer in `harness/progress.json`, and set
`human_ack` on the entry. This is the run's one mandatory pause besides the
Phase 0.5 surface question, and it exists for the same reason: a wrong
answer here poisons everything downstream, and it costs a minute to ask.

2. **Evaluator attacks** (opus, sees ONLY the issue body and the proposed
contract): find missing edge cases, criteria too vague to verify, scope
beyond the issue, criteria tagged to the wrong issue or integration
behavior missing entirely, and tests that would pass while the feature
is broken.
behavior missing entirely, tests that would pass while the feature
is broken, and **ungrounded substrate**: any store, path, env var,
collection, endpoint, or dependency named in a criterion or fixture that is
absent from the `grounding` block, or marked EXISTS without evidence, is a
BLOCKING objection. You cannot read the code, and you do not need to: the
grounding block's quoted evidence is an artifact, and a noun with no entry
is visible from where you sit.
Write objections into the contract file. If sound, mark all criteria
`agreed`.
Comment on lines 188 to 189
3. Iterate: hand objections to the generator, revise, re-attack. Max 3
Expand All @@ -161,20 +202,37 @@ reason, evaluator must accept, the issue comment gets updated with an
"Amended" note. Never silently edit a criterion because it turned out to be
hard.

## Phase 3: Human gate on the contract (opt-in)
## Phase 3: Human gate on the contract (default)

Default: pause. Show the user the agreed contract, in this order: the
grounding block (NEW entries first), the evaluator's residual risks, then the
criteria list and any disputes. Wait for approval or adjustments before
building. A human skims what he is shown first; show him the premises, not the
paperwork.

The pause is the default because 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 makes changing one an
event. A minute here is worth a rebuild there.

Default: no pause. The agreed contract is already posted to the issue
(Phase 2), so the human can inspect it asynchronously, and the run continues
autonomously into execution.
If `--no-gate` was passed: no pause. The agreed contract is already posted to
the issue (Phase 2), leading with the same grounding block and residual risks
above the criteria, so the human can inspect it asynchronously and the run
continues into execution. Use this when you already trust the premises, not to
save a minute.

If `--gate` was passed: show the user the agreed contract (criteria list
plus any disputes) and wait for approval or adjustments before building.
Either way, the NEW-substrate escalation in Phase 2 step 1.5 is not part of
this gate and fires regardless. It asks whether the contract may invent a
store; this gate asks whether the contract is right.

## Phase 4: Execution (sonnet team, straight from the contract)

There is no technical planning phase and no PLAN.md: the contract is the
plan, and technical decisions belong to the builders, made against the code
as they work. As lead, do the split inline before spawning anyone:
as they work. The `grounding` block is the one exception: which existing
system a criterion reads or extends is a verified fact, fixed in Phase 2, and
not a builder's to re-decide. As lead, do the split inline before spawning
anyone:

- Group the contract criteria into workstreams (often just one for
issue-sized work) and give each workstream a file territory, so parallel
Expand Down Expand Up @@ -281,6 +339,12 @@ auto-fixable, link to the updated PR.
- The evaluator never reads generator transcripts, and no agent other than
the generator-evaluator pair (via the amendment rule) may modify agreed
contract criteria. You do not weaken a criterion to make a round pass.
- **Never relay a contract whose grounding you have not reproduced.** An EXISTS
entry is a claim about the world; you check it with a command, not with
trust. A criterion can be re-attacked in a later round, but a false premise
is silently inherited by every criterion written on top of it, and by every
sibling issue that builds on the same substrate afterwards. A fact does not
go stale the way a design does; it becomes false and fails its grep.
- **The evaluator never runs the builder's test suite and never reads the
implementation.** Builder-written tests encode the builder's understanding,
so a green suite certifies whatever misunderstanding produced the bug. That
Expand Down
29 changes: 22 additions & 7 deletions commands/interview.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ Before the first question:

- If you are inside a codebase the idea touches, explore it first (use an
Explore subagent if the repo is non-trivial) so your questions and
recommendations are grounded in what already exists.
recommendations are grounded in what already exists. Record what you
find: every existing system, store, or dataset the idea would consume
or extend goes into the spec's Grounding section as a fact, whether or
not any question depends on it.
- Scope check: if the idea spans several independent features, say so and
help the user split it. Interview one feature at a time.

Expand All @@ -40,10 +43,12 @@ Interview rules:
yourself and move on instead of asking.
- **Stay at product altitude**: users and actors, behavior, empty/error/edge
states, scope boundaries, success criteria, priorities, rollout. Do NOT ask
about schemas, file paths, libraries, or architecture; /forge:planning bans that
content and /forge:building negotiates it against the codebase later. If a
technical unknown genuinely blocks a product decision, record it as a
deferred question and continue.
the user to *decide* schemas, libraries, or architecture; /forge:planning bans
those decisions and /forge:building negotiates them against the codebase later.
Do ask, or better, go and read, where an existing system the feature will
consume already lives: that is a fact, not a decision, and it belongs in
Grounding. If a technical unknown genuinely blocks a product decision, record
it as a deferred question and continue.
- When a decision has 2-3 genuinely different viable shapes, present them as
options with trade-offs before asking, recommendation first.
- The user can end the interview at any time ("wrap it up", "answer the rest
Expand Down Expand Up @@ -81,6 +86,13 @@ error, and edge states>
<every decision made during the grilling, including recommendations the
user accepted; mark auto-answered ones with "(auto)" >

## Grounding (facts, not decisions)
<every existing system the feature consumes or extends: what it is, where
it lives, and whether the capability the feature needs exists there today
or must be added. Anything the feature needs that does not exist anywhere
yet is listed here marked NEW. This section records the world as found; it
decides nothing>

## Out of scope
<explicit rejections and deferrals, each with its one-line reason; this is
what gives /forge:planning a definition of done>
Expand All @@ -90,8 +102,11 @@ what gives /forge:planning a definition of done>
/forge:planning critic and the /forge:building negotiation to pick up>
````

No technical content beyond the deferred notes in Open questions: no file
paths, schemas, function names, or architecture anywhere else.
No technical decisions anywhere: no schemas, no architecture, no chosen
libraries. The Grounding section is the one exemption, and it may name
files, stores, and functions because it records what already exists, not
what to build. Outside Grounding and the deferred notes in Open questions,
no technical content at all.

Self-review before saving, fix inline, no re-review:

Expand Down
Loading