# Full SDD and Fast SDD

Forge has **two paths** through the [SDD spine](/guide/workflow). **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`. |

::: tip Fast is not "skip the checks"
Fast SDD skips planning documents, not verification. Strict TDD, the
[review gate](/features/review), 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**

```mermaid
flowchart LR
  P[propose] --> S[specify] --> PL[plan] --> T[tasks]
  T --> I[implement] --> V[verify] --> A[archive]
```

**Fast SDD**

```mermaid
flowchart LR
  P[propose] --> T[tasks + Fast Plan]
  T --> I[implement] --> C{sdd-fast-check}
  C -->|within| V[verify] --> A[archive]
  C -->|escalate| F[Full SDD: specify, plan, tasks]
```

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:

````markdown
## 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:

```json
{ "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:

- `risk` is not `Low`.
- `estimated_lines`, `estimated_files`, or `new_capabilities` is missing, is not
  a plain non-negative integer, or is over its limit. `"many"` or a range such as
  `"20-40"` counts as missing.
- `touches` names any of: `security`, `auth`, `payments`, `permissions`, `pii`,
  `secrets`, `migration`, `schema`, `public-api`.
- The proposal declares a new capability.
- `tasks.md` holds more than 5 tasks.

::: warning 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`](/guide/configuration#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](/features/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`](#measuring-the-diff-forge-sdd-fast-check).
If the diff has outgrown the limits, the change escalates:

1. `sdd-apply` writes two lines into the proposal's `## Track` block and stops:

   ```yaml
   escalated_from: fast
   escalation_reason: "forge sdd-fast-check exit 2: 212 changed lines exceeds 150"
   ```

2. The marker is **sticky**. From then on the change resolves to Full SDD, with
   `source: sticky`.
3. The orchestrator runs the skipped phases (`specify`, `plan`), then re-plans
   `tasks`. 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:

```text
$ forge status typo-fix
✓ propose
– specify
– plan
✓ tasks
⠇ implement
```

`skipped` 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`

```text
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](/reference/cli#sdd-fast-check).

## Worked example: Fast SDD

You ask: *"Fix the typo in the `forge sync` help text."*

1. **propose.** The agent writes `proposal.md` with `track: fast`,
   `risk: Low`, `estimated_lines: 2`, `estimated_files: 1`, `new_capabilities: 0`,
   `touches: [docs]`.
2. **Resolve.** `forge sdd-status --json` reports
   `track: { value: "fast", source: "proposal" }`, and `specify` and `plan` show
   as `skipped`.
3. **tasks.** `sdd-tasks` pins `base_commit` and 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: No
   ````
4. **implement.** `sdd-apply` edits two lines, then runs
   `forge sdd-fast-check typo-fix --json`: `verdict: within`, exit `0`.
5. **verify.** Tests and build pass; the report records them as evidence.
6. **archive.** `forge archive` moves the change aside and writes `Track: fast`.
   There are no specs to merge.

## Worked example: Full SDD

You ask: *"Add rate limiting to the public API."*

1. **propose.** The agent writes `track: full`. The `touches` list includes
   `public-api` and the change adds a capability, so even a `fast` proposal would
   be vetoed.
2. **specify.** A delta spec describes the new requirements and scenarios.
3. **plan.** `plan.md` records the design: where the limiter lives, the storage,
   and the contracts.
4. **tasks.** `tasks.md` lists the work with tiers and a review workload forecast.
5. **implement.** `sdd-apply` works task by task with strict TDD. There is no
   fast check on this path.
6. **review and verify.** The review gate inspects the diff; `verify` checks the
   result against the spec scenarios.
7. **archive.** The delta spec merges into `.forge/specs/`, and the change moves
   to the archive with `Track: 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](/guide/configuration#models).

## Related

- [The SDD Workflow](/guide/workflow): the spine both paths share.
- [Configuration: models](/guide/configuration#models): the default model policy and overrides.
- [Configuration](/guide/configuration#workflow): `adaptive_depth` and `force_depth`.
- [Commands](/guide/commands): `forge sdd-status` and `forge sdd-fast-check`.
- [Adversarial Review](/features/review): the risk tier, which is separate from the track.
