Skip to content

Latest commit

 

History

History
202 lines (159 loc) · 8.25 KB

File metadata and controls

202 lines (159 loc) · 8.25 KB

The workflow

Forge has one artifact: the spec. It is born cheap and grows.

The states and the legal transitions between them live in the binary: forge workflow prints them in lifecycle order, with what each state means and who has to act next. This page explains the process around them and keeps no second copy of the machine.

The backlog is not a folder: it is the specs in proposed and accepted. An epic is not a type: it is a spec that has children. Collapsing those into one artifact removes the copying, and the drift that copying causes.

Only forge writes status, and every move appends a line to the spec's History section saying who did it and why.

No authorization model

forge accept SPEC-004      # into the queue
forge approve SPEC-004     # the contract is right

Anyone can run either. --by defaults to git config user.name, so nobody has to name themselves to move their own work forward. Forge does not have a maintainer list to check a handle against, the same way git does not check whether you were allowed to author a commit.

The scrutiny a team wants happens in the pull request review it already does, not in a second Forge-specific approval. forge validate still catches what is objectively wrong regardless of who touched what: a missing contract, an uncovered promise, a dependency cycle, a contract that drifted after it was approved.

Open questions

A contract may raise questions instead of answering them. They live in the spec's ## Open questions section, one per line, or None. when there are none:

## Open questions

- OQ1: does the retry window close on the first success or on the last?

forge approve refuses while any question remains and forge validate warns, so a contract is never approved around an open decision. Settle each question, or write None..

Acceptance criteria

Criteria live in the spec as a list:

## Acceptance criteria

- AC1: a saved card can be reused without retyping it
- AC2: charging twice with the same request id charges once

They must be verifiable by someone who did not write them: a command, a test, a request and its response. forge approve refuses while a criterion names none of those, so a vague criterion is settled before code starts. The reviewer marks each one with evidence, and forge check reports every criterion with no task in tasks.md or no evidence line in review.md; forge validate raises the same coverage as a warning, and as an error at done when the evidence is missing.

Ids across branches

forge new picks the next free number from the spec folders committed under .forge/specs/ on every remote-tracking ref, not just main, so a number a parallel branch already pushed is skipped. The read is best-effort, not a reservation: a branch whose refs are not present locally, or two branches that mint the same number before either pushes, can still collide, and two specs with the same title share a folder and surface only as a git conflict at merge. forge new and forge accept never fetch, so in a repository with more than one person run git fetch before forge new to read the latest branches. forge accept keeps the id when the only match is the spec's own published branch, and forge validate still fails on a real duplicate once it lands.

Planning from what exists

A spec does not start from an empty repository. The architect records what already exists in the spec's ## Existing state section (.forge/specs/<id>/spec.md): what this builds on, what it reuses, the conventions that apply, and the duplication it avoids. Planning reads that one survey instead of filling a second one. Before splitting the spec into phases, the orchestrator checks it against the delivered work: first forge capabilities, which derives the current contracts grouped by capability and marks what each one supersedes, then the contracts it points at, forge status for what is open, and the code that already does part of the job. The architect names the modules it builds on in the contract too, so the reuse is written where it survives archiving. forge validate warns when a live spec has not surveyed the existing state.

Hierarchy and coverage

A spec that spans several deliverables becomes a parent:

forge new "Card payments"                                  # SPEC-002
forge new "Saved cards" --parent SPEC-002 --covers AC1,AC3 # SPEC-004
forge new "Reconciliation" --parent SPEC-002 --covers AC4  # SPEC-005

Coverage is declared by the child and computed by the tool. The parent file is never edited when a child advances, which is why two branches closing different children never conflict.

$ forge status SPEC-002
coverage
  AC1   SPEC-004 (implementing)
  AC3   SPEC-004 (implementing)
  AC4   SPEC-005 (accepted)
  AC2   NOT COVERED

NOT COVERED is a warning while everything is open and an error once any child is closed: that is the moment where a promise silently disappears.

Dependencies

depends_on: [SPEC-003, SPEC-011@contract]
needs:
  - "a retry engine"
blocked_by_external:
  - what: "production credentials"
    who: ana
  • depends_on: [SPEC-003] waits for that spec to be done.
  • depends_on: [SPEC-011@contract] waits only for its contract to be approved. This is how a front end and a back end are built in parallel against the same agreed shape.
  • needs is a dependency on something that has no spec yet. The only way out is to create it and replace the text with its id, which is what stops "we will look at it later" from meaning "never".
  • blocked_by_external is out of Forge's hands, so it makes it visible with an owner.

forge start refuses while any of these is open, lists what is ready instead, and accepts --force when you decide otherwise. The exception is written into the spec, where the reviewer will see it.

Contract drift

When forge approve runs, it stores a fingerprint of the contract. Every spec that starts against it with @contract records which fingerprint it agreed to.

If the contract changes afterwards, forge validate fails and names the specs building against a version that no longer exists. It also fails if a contract is edited after approval without being approved again. This is the classic back-and-front integration failure, caught in the pull request instead of in staging.

Checkpoints

By default the work reaches origin only at forge submit, so a spec in flight lives on one machine. forge push <id> closes that gap: it commits the pending tree with the subject chore(<id>): checkpoint <state> and pushes the spec branch with its upstream set. Run it after each phase and before a session ends, so another machine can fetch the branch and resume.

With push: on in .forge/project.md's frontmatter, forge advance runs the same checkpoint at every state boundary; without that scalar forge advance never touches git or the network. It refuses main and master, so the checkpoint only ever publishes a spec branch.

Files

.forge/specs/SPEC-004-saved-cards/
├── spec.md     the spec, its contract and the existing-state survey
├── plan.md     the phases
├── tasks.md    the phases as checkboxes, ticked as they land
└── review.md   verdict and evidence per criterion

Every spec owns a folder. The plan, tasks and review are written as the work moves and stay in the tree when the spec is done; forge archive no longer deletes anything, because the folder is the durable record. The spec's spec.md holds the contract, the existing-state survey, the history and the state.

That is the durable record a later spec reads to know what exists: the contract of every done spec (with the interfaces and the modules it builds on), the decisions that outlive a spec, and the conventions. The plan, tasks and review of a closed spec stay beside its contract, so a future task can read what was surveyed, what each phase did and what the review found; the contract is still what a future task is expected to read first.

A spec ends as a pull request. forge submit <id> pushes the branch and opens it through gh, recording the number and URL on the spec; when gh is missing it prints the git push and gh pr create commands. Nothing in Forge merges a pull request: a person reviews and merges on the forge host.