Full SDD and Fast SDD
Forge has two paths through the SDD spine. Full SDD is the complete path, with a written spec and a design before any code. Fast SDD is the short path for small, low-risk changes: it skips the spec and the design and goes straight from proposal to a short plan.
The path is called the track. In files, config, and JSON it is written track: full or track: fast. This page explains how to tell the paths apart, how Forge chooses one, and what happens when a fast change turns out not to be small.
The two paths at a glance
Full SDD (track: full) | Fast SDD (track: fast) | |
|---|---|---|
| Phases that run | propose → specify → plan → tasks → implement → verify → archive (and explore when warranted) | propose → tasks → implement → verify → archive |
| Phases skipped | None | specify and plan, shown as skipped |
| Artifacts | proposal.md, specs/**/spec.md, plan.md, tasks.md, verify-report.md | proposal.md, tasks.md (with a ## Fast Plan and its Review Workload Guard lines), verify-report.md |
| Tasks | As many as the work needs | 1 to 5 |
| Use it when | New behavior, a new capability, anything sensitive, or anything you are unsure about | A small, low-risk change: typo, doc fix, config tweak, a contained bug fix |
| Size limits | None | At most 150 changed lines, 5 files, 5 tasks |
| Cost and time | More ceremony, more tokens, a spec you can review before code exists | A fraction of the planning cost; no spec to review |
| Review gate | Yes | Yes. The track never relaxes it. |
| Verify evidence | Required | Required. A fast change with no passing test or build is FAIL. |
Fast is not "skip the checks"
Fast SDD skips planning documents, not verification. Strict TDD, the review gate, and verify all still run. What you lose is a spec and a design, which is why the path is only open to changes small enough not to need them.
The two paths as diagrams
Full SDD
Fast SDD
The fast path is always a subset of the same spine. It never invents a different one, and the phase order never changes.
How Forge picks the path
propose writes a ## Track block in proposal.md. It is a fenced YAML block, and it is where the agent argues for one path:
## Track
```yaml
track: fast # fast | full
proposed_by: agent # agent | human
complexity: low
risk: Low # fast requires Low
estimated_lines: 40
estimated_files: 2
new_capabilities: 0
touches: [docs]
reasons:
- "typo in one help string"
```The block is a proposal, not a decision. A deterministic resolver in Forge (the track field of forge sdd-status --json) has the last word, and it reports the track together with its source and reasons:
{ "track": { "value": "full", "source": "veto", "reasons": ["estimated_files 7 exceeds 5"] } }source is one of proposal, config, veto, sticky, or fallback.
Thresholds
A change can be fast only when all of these hold:
| Signal | Fast limit |
|---|---|
risk | Low |
estimated_lines | 150 or fewer |
estimated_files | 5 or fewer |
tasks in tasks.md | 5 or fewer |
new_capabilities | 0, and the proposal's "New Capabilities" section says "None" |
The limits are built into Forge. They are not config keys, so no repo can quietly raise them.
Vetoes
A veto forces Full SDD no matter what the proposal asked for. Any of these is one:
riskis notLow.estimated_lines,estimated_files, ornew_capabilitiesis missing, is not a plain non-negative integer, or is over its limit."many"or a range such as"20-40"counts as missing.touchesnames any of:security,auth,payments,permissions,pii,secrets,migration,schema,public-api.- The proposal declares a new capability.
tasks.mdholds more than 5 tasks.
Fail closed
Forge never guesses toward the short path. A missing proposal, a missing or malformed ## Track block, an unknown key, an unreadable config, or an unparseable number all resolve to Full SDD, with the reason listed in the status output.
Overrides
Two keys under workflow can steer the choice:
| Config | Effect |
|---|---|
adaptive_depth: false | Always Full SDD. Forge stops proposing the short path. |
force_depth: trivial | Request Fast SDD for every change. Vetoes still apply. |
force_depth: standard or full | Always Full SDD. |
The vetoes are not overridable. force_depth: trivial cannot push an auth change onto the fast path; the change resolves to full with source: veto.
The track is not the review tier
The track decides which planning phases run. The review risk tier decides how many adversarial lenses inspect the finished diff, and it is always derived from that diff. The two are independent (recorded as ADR-0002, "workflow track independent of review tier"). A fast change that touches executable code is still reviewed as a normal code change would be.
Escalation: fast to full, never back
Fast SDD is a bet that the change is small. sdd-apply checks the bet after it edits, using forge sdd-fast-check. If the diff has outgrown the limits, the change escalates:
sdd-applywrites two lines into the proposal's## Trackblock and stops:yamlescalated_from: fast escalation_reason: "forge sdd-fast-check exit 2: 212 changed lines exceeds 150"The marker is sticky. From then on the change resolves to Full SDD, with
source: sticky.The orchestrator runs the skipped phases (
specify,plan), then re-planstasks. Work already done is kept, not reverted.
Escalation also fires when sdd-tasks finds the work needs more than 5 tasks, and a change that already has a specs/ folder or a plan.md is sticky-full from the start. Nothing downgrades a full change to fast, and no config key does either. If a change grew, trust the escalation.
The skipped state
On Fast SDD, specify and plan are not "missing" and not "blocked". They are skipped by design, a state of their own:
$ forge status typo-fix
✓ propose
– specify
– plan
✓ tasks
⠇ implementskipped shows up in forge sdd-status (state: skipped), forge status, forge board, the spec viewer, and the generated spec site. A skipped phase is never the "next" phase and never counts as done. verify records spec and design coherence as skipped (fast track) rather than raising a warning.
Measuring the diff: forge sdd-fast-check
forge sdd-fast-check [change] [--cwd <path>] [--json] [--base-ref <rev>]The command measures how much a fast change has actually changed and tells sdd-apply whether it still fits. It counts <baseline>..HEAD plus staged, unstaged, and untracked work, and ignores the change's own .forge/changes/<name>/ folder.
The baseline is the first of these that resolves to an ancestor of HEAD:
| Order | Baseline | Where it comes from |
|---|---|---|
| 1 | override | --base-ref <rev>; if it fails, there is no fall-through |
| 2 | plan | base_commit pinned by sdd-tasks in ## Fast Plan |
| 3 | merge-base | merge-base of HEAD with origin/HEAD, main, or master |
The verdict is carried by the exit code:
| Verdict | Exit | Meaning | sdd-apply does |
|---|---|---|---|
within | 0 | At least one file changed and the diff is inside every limit | Carries on to review and verify |
escalate | 2 | Over a limit, a high-risk path, or no usable baseline | Writes the escalation marker and stops |
empty | 3 | Baseline resolved, but nothing changed | Reports apply as failed; no marker |
| usage error | 1 | Unknown flag or change | Treated as an escalation |
With --json the command prints the forge.sdd-fast-check/v1 payload for all three verdicts. It never changes refs, HEAD, your index, or your working tree. See the CLI reference.
Worked example: Fast SDD
You ask: "Fix the typo in the forge sync help text."
propose. The agent writes
proposal.mdwithtrack: fast,risk: Low,estimated_lines: 2,estimated_files: 1,new_capabilities: 0,touches: [docs].Resolve.
forge sdd-status --jsonreportstrack: { value: "fast", source: "proposal" }, andspecifyandplanshow asskipped.tasks.
sdd-taskspinsbase_commitand writes one task:markdown## Fast Plan ```yaml base_commit: 3f1c0a9e8b7d6c5b4a39281706f5e4d3c2b1a098 ``` - [ ] 1.1 Fix "sychronize" in the `sync` help string and its golden 400-line budget risk: Low Chained PRs recommended: No Decision needed before apply: Noimplement.
sdd-applyedits two lines, then runsforge sdd-fast-check typo-fix --json:verdict: within, exit0.verify. Tests and build pass; the report records them as evidence.
archive.
forge archivemoves the change aside and writesTrack: fast. There are no specs to merge.
Worked example: Full SDD
You ask: "Add rate limiting to the public API."
- propose. The agent writes
track: full. Thetoucheslist includespublic-apiand the change adds a capability, so even afastproposal would be vetoed. - specify. A delta spec describes the new requirements and scenarios.
- plan.
plan.mdrecords the design: where the limiter lives, the storage, and the contracts. - tasks.
tasks.mdlists the work with tiers and a review workload forecast. - implement.
sdd-applyworks task by task with strict TDD. There is no fast check on this path. - review and verify. The review gate inspects the diff;
verifychecks the result against the spec scenarios. - archive. The delta spec merges into
.forge/specs/, and the change moves to the archive withTrack: full.
Which model runs a Fast SDD change
Fast SDD: the proposal runs on its normal model because it is what classifies the change; once the track resolves to fast, run the remaining phases (tasks, apply, verify, archive) on sonnet when the change involves real logic, or on haiku for trivial edits. The default model table and how to override it are in Configuration: models.
Related
- The SDD Workflow: the spine both paths share.
- Configuration: models: the default model policy and overrides.
- Configuration:
adaptive_depthandforce_depth. - Commands:
forge sdd-statusandforge sdd-fast-check. - Adversarial Review: the risk tier, which is separate from the track.