Project Structure
Forge keeps all of its per-project state under a single directory: .forge/. Everything there is plain files you can read, diff, and commit — which is why Forge's state survives context compaction, agent restarts, and team handoffs.
Commit .forge/
Commit the .forge/ directory so your team shares the same specs, changes, and memory. See Best Practices.
specs/ vs changes/ — the core distinction
Two directories carry most of the meaning, and they are easy to confuse. The one-line version:
specs/is what the system is. The canonical, settled truth — the current agreed-upon behavior of your project. The deterministic gates read from here:forge driftcross-references these specs against your code, andforge analyzescores these. You don't hand-edit them mid-work; they are the record of decisions already made.changes/is what you are doing to it right now. One directory per in-flight change, each a self-contained workspace for a single unit of work (proposal, exploration, design, tasks, gate state — and a delta spec).
The word that ties it together is delta. The spec inside a change is not the whole truth — it describes only the slice that this change adds or alters. That is deliberate: it keeps every unit of work small, reviewable, and reversible. You reason about one change, not the entire system.
The two connect through promotion, and only in one direction. When every task in a change is Done, forge archive merges that change's delta spec into specs/ (so the canonical truth now reflects the change) and moves the change folder to changes/archive/. Work always flows changes/ → specs/, never the reverse.
Asking "what does this system do?" → read
specs/. Asking "what's being worked on?" → look inchanges/. A change is a renovation work order scoped to one room;specs/is the as-built blueprint that gets amended once the work is signed off.
The .forge/ layout
.forge/
├── config.yaml # optional per-project overrides
├── config.local.yaml # gitignored, per-machine
├── memory/ # constitution, discovery memory, refinement notes
├── specs/{domain}/spec.md # canonical capability specs (promoted from changes/)
├── changes/
│ ├── archive/YYYY-MM-DD-<name>/ # immutable audit trail
│ └── <change-name>/
│ ├── state.yaml # DAG state (survives compaction)
│ ├── exploration.md proposal.md plan.md tasks.md verify-report.md
│ ├── data-model.md # (optional) entities, written by design phase
│ ├── contracts/ # (optional) {interface-name}.md per interface
│ ├── roadmap.md # (optional) delivery sequencing, written by forge.roadmap
│ ├── test-coverage.md # (optional) scenario -> test map, written by sdd-tests
│ ├── swarm-log.md # (optional) wave dispatch record, written by sdd-swarm
│ ├── specs/{domain}/spec.md # delta spec
│ ├── verify-attempts.yaml # circuit-breaker state
│ ├── budget.yaml # spend ledger
│ └── session-log.jsonl # attributed timeline
├── templates/ skills/ extensions/ backups/ design/
├── site/ # generated dashboard (forge docs)
└── _imported/ # lossless catch-all from `forge import`What each part is
Top-level files
config.yaml— the single optional per-project override file. Committed with the repo so the whole team shares it. See Configuration.config.local.yaml— per-machine overrides. Gitignored so machine-specific settings never leak into the shared config.
memory/
forge init creates this directory empty. It fills up as you run the foundation and discovery commands: .forge/memory/constitution.md from /forge.constitution, project-discovery.md / current-architecture.md / current-capabilities.md / current-gaps.md from /forge.discover, and refinement-suggestions.md / knowledge-index.json as /forge.refine and other commands accumulate context over time. When memory.team_sync is enabled, it doubles as the Engram team-sync export — either way, it is committable, so project memory travels with the repository and every teammate benefits from the same accumulated context.
specs/{domain}/spec.md
The canonical capability specifications, organized by domain. These are the project's source of truth for behavior. Specs land here by being promoted from a change's delta spec when that change is archived.
changes/
Home for changes — both in-flight and archived.
archive/YYYY-MM-DD-<name>/— the immutable audit trail. Once a change is archived it is frozen here, dated and named, as a permanent record.<change-name>/— an in-flight change directory. Its contents:state.yaml— the change's DAG state. It survives context compaction, so an interrupted change can always be resumed.exploration.md,proposal.md,plan.md,tasks.md,verify-report.md— the phase artifacts (see the phase-to-artifact map).data-model.md(optional) — entities and data structures, written by the design phase only when the change introduces or modifies any.contracts/{interface-name}.md(optional) — one file per new or modified external-facing interface, written by the design phase when applicable. Both companions are skipped for purely internal changes.roadmap.md(optional) — the task execution graph sequenced into a value-first delivery order of vertical slices, written by/forge.roadmaponcetasks.md's execution graph exists.test-coverage.md(optional) — a scenario -> REQ ID -> test file map for the RED-phase tests it generates, written bysdd-tests.swarm-log.md(optional) — a wave-by-wave dispatch, specialist classification, and conflict record, written bysdd-swarmwhen it implements a change's tasks in parallel instead ofsdd-apply's sequential path.specs/{domain}/spec.md— the delta spec: the change's proposed additions/edits, merged into canonicalspecs/on archive.verify-attempts.yaml— the circuit-breaker state that bounds verify loops. See circuit-breaker.budget.yaml— the spend ledger for this change.session-log.jsonl— the attributed timeline of what happened, line by line.
Treat the archive as read-only
changes/archive/ is an audit trail, not a working directory. Forge writes it on archive and never rewrites it — hand-editing an archived change breaks the record it exists to preserve. Reopen work as a new change instead.
Supporting directories
templates/— not created byforge init. It only appears whenforge import specifybrings over an existing Spec-Kit project's.specify/templates/; the foundation and spec commands draft their artifacts directly and never require it to exist.skills/— project skills.extensions/— installed extensions.backups/— automatic snapshots taken before mutations (used byforge restore).design/— design-system artifacts from thedesignfoundation phase.
site/
The generated dashboard, produced by forge docs. Serve it with forge docs serve to browse your specs, changes, tasks, budget, and timeline. See the dashboard overview.
_imported/
A lossless catch-all populated by forge import. When you migrate an existing project, anything that does not yet map cleanly onto Forge's model lands here rather than being dropped. See migration.
Agent files live outside .forge/
Not everything Forge writes lives under .forge/. Agent-specific directories are written wherever each agent expects them — for example .claude/commands/ and .claude/agents/ for Claude Code. This keeps Forge's own state cleanly separated from each agent's native configuration, and it is how the same unified command set appears natively inside every configured agent.
See Agents for how per-agent adapters map commands into each agent's native format.
Next steps
- The Workflow — how artifacts are produced and promoted.
- Configuration — the
config.yaml/config.local.yamlfiles. - Agents — where per-agent files are written.
- Dashboard — the
site/output.