Symphony Forge is a dual-runtime harness plus doc-driven factory for building agent-ready software. Claude Code coordinates; Codex executes. This page is the one blessed path from empty directory to first feature PR.
You drive it with sentences, not commands. Every step below leads with
what you SAY to Claude Code (or Codex); the command underneath is what the
agent runs for you — the deterministic contract, and your fallback in
degraded mode. The only commands a human ever types personally are the
one-time clone and decision accept (confirming decisions is deliberately
human-only).
Lost at any point, in any phase? Say "what's next?" — the agent runs
./forge next and walks you through the exact next actions.
You never memorize these — the command you're about to run refuses and tells you what's missing. But this is the shape of the whole system:
prototype ▶ spec grills ▶ CONFIRMED SPECS ▶ derived roadmap ▶ sign-off grill
▶ SIGN-OFF ▶ roadmap+team ▶ per task:
PLAN MODE (hook-forced) ▶ grill ▶ plan saved (incl. Surface Impact) ▶
decompose (creates the stage tracker) ▶ per stage: implement (`forge delegate`)
▶ LOCAL autoreview until clean ▶ commit ▶ stage done ▶ … ▶ verify ▶
ONE branch autoreview ▶ functional (if user-facing) ▶ assumptions guided ▶
pr_ready (stages done + refactor ratchet) ▶ archive
- Grills are adversarial gaps/contradictions passes: each spec
confirmation, sign-off, and every plan approval (
/grill-me) require the relevant fresh pass. - One write gate is always armed: product edits are denied until the plan
is saved, unless a bounded
forge quickfix startwindow is open.forge delegateis the only implementation boundary;/codex:rescueremains the sanctioned read-only planning explorer. Rawcodex execis always denied. - Every artifact is attested:
generated_byon the allowlist,skills_usedmandatory on user-facing work, commit-stamped and fresh. - The ship gate additionally demands orchestrator guidance on every recorded assumption and (for user-facing tasks) a functional check.
- Hygiene runs continuously: secrets/oversize refused at the context inbox, repo budgets in CI, decision lifecycle + prototype isolation linted.
- The repo learns: recurring review-finding classes surface via
forge findings patterns(3+ hits ⇒ a refactor story, not a fourth patch); lessons ledger inplans/lessons.jsonl(forge lesson relevantbefore work,forge lesson addafter repeated failures); deferred scope keeps a revisit trigger (forge defer).
Clone it wherever you keep repos — ./setup records the location for the
bootstrap skills:
git clone git@github.com:knacklabs/symphony-forge.git
cd symphony-forge
./setupSay: "Set up my machine for KnackLabs projects."
./forge doctor --fix--fix auto-installs everything installable (Codex CLI via npm,
codex-plugin-cc and ponytail via the claude plugin CLI, gstack and
autoreview from their GitHub repos, mattpocock/skills, emilkowalski/skills,
and Anthropic's frontend-design via the skills CLI). On Windows it also
installs Git for Windows and Python 3.10+ through winget in user scope; Forge
does not elevate itself, though an installer may show its own Windows prompt.
See Windows support for the native flow and WSL2 escape hatch.
Only logins (codex login) stay manual.
Re-run until it says ready.
Say: "Set up a new KnackLabs project called my-app." (the knacklabs-new-project
skill installed by ./setup)
./forge init --name my-app --target ../my-app
cd ../my-appThis scaffolds a complete, git-initialized repo: dual-runtime adapters
(.claude/, .codex/), shared agent assets (factory/, including the
artifact schemas under factory/schemas/), the vendored engineering
constitution, the phase manifest + skill allowlist (harness.yaml), doc
contracts, and an armed sign-off gate. It refuses a target containing files
it would overwrite (listing them); a non-empty target with no collisions is fine.
It also creates .factory/record-origin.json, which records the date, current
commit, and number of commits that existed before Forge began keeping the
project record. The file is written once and is not moved forward later.
The new repo has ZERO git relation to the harness (the machinery is a vendored copy — see "Template, Not Fork" in the README). Give it its own home and build the application inside it:
gh repo create knacklabs/my-app --private --source . --push
# or: git remote add origin git@github.com:<org>/my-app.git && git push -u origin mainDo NOT fork the harness and do NOT use
gh repo create --templatefor client projects. A fork makes every future harness upgrade a merge into your app code; a template copy has no upgrade path at all — and both drag along the harness's own plans, run state, and history.forge init+forge upgradeis the only supported pairing: init creates the repo, upgrade refreshes machinery-only, app code is never touched.
Once per machine per repo: run
direnv allowinside the project. That activates.envrc, which pinsGSTACK_HOMEto the repo's.gstack/— every gstack output (office-hours design docs, decisions, learnings) lands IN the repo, committed and shared, instead of a personal~/.gstack. Multiple devs never conflict: JSONL stores union-merge (.gitattributesjsonl-appenddriver, auto-registered per clone). Old history on your machine? Say "migrate my gstack history" (./forge gstack migrate).
Say: "Let's run office hours on this idea." (gstack /office-hours) and
fill docs/product/DISCOVERY.md and docs/product/BRIEF.md from it.
Prototype freely — no .factory ceremony before sign-off; the prototype is
preserved under prototype/ afterwards as the forever UX reference.
As capabilities emerge, save each one as a draft spec, grill the exact file, then confirm it:
./forge spec save billing --from /tmp/billing.md
python3 factory/scripts/record_grill_from_json.py --gate spec \
--input /tmp/spec-grill.json --input-digest docs/specs/billing.md
./forge spec confirm billingCapture every client decision as you go — say: "Record that as a decision."
./forge decision new <slug>After every spec is confirmed, say "Build the project roadmap." The docs-decomposer derives spec-linked epics and stories:
./forge roadmap derive --input /tmp/roadmap.jsonFirst say: "Grill the handover." The agent interrogates DISCOVERY, BRIEF,
confirmed specs, the derived roadmap, decisions, and prototype notes — one question at
a time, findings resolved into doc edits or decision records — and records
the verdict (record_grill_from_json.py --gate signoff).
record_signoff.py refuses without a fresh, passing grill (fresh =
product docs unchanged since it ran).
Then say: "The client signed off." The agent drafts the record; the accept is the human's decision, not their keystroke — an explicit chat confirmation ("accepted") lets the agent run it with their name:
./forge decision new client-signoff
./forge decision accept client-signoff --by "<human name>" # human-confirmed
python3 factory/scripts/record_signoff.pyEvery phase from planning onward is refused until this is recorded.
Say: "Scaffold the workspace." The agent hands
harness/nestjs-react/SCAFFOLD_PROMPT.md to Codex to generate the nx
workspace per harness/nestjs-react/conventions/ and constitution/.
The PM reviews coverage and the EM distributes the already-derived stories. Optionally define the team first so assignment is checked and skill-matched:
./forge team set alice --role dev --skills frontend
./forge team set bob --role dev --skills fullstack
./forge roadmap assign ENG-101 --to alice
./forge roadmap list # grouped by epic, shows @assignee
./forge board # read-only live lifecycle viewThe board opens on Overview, which answers what the project is, what can start now, what each epic delivers, and where each story sits. When the repo has a record-origin marker, Overview also says where the Forge record begins and how many commits precede it; an older repo with no marker makes no claim. To inspect a different initialized example repo, point the same board command at it:
./forge board --repo <example>The checked-in source at factory/board/example/ is the smallest readable
brief, confirmed spec, and roadmap that the production capture validators
accept. It is intentionally a data-only validation example. The current board
server reads its page from the --repo target, so pass a full initialized repo
to the command above; direct serving of the data-only bundled tree is deferred
until the page resolver changes.
plans/roadmap.json is the durable backlog: intake marks items active,
pr_ready.py marks them done with history links, assignments survive
re-imports, and "what's next?" always knows the next story (and nags the EM
about unassigned ones). Refine it by PR as planning teaches you more.
To read the committed project record by story, event type, or date, say
"Show me the project history." The agent runs ./forge history; events
without a story are shown as unattributed instead of being hidden.
Start each feature with: "Start the next story on the roadmap."
git worktree add ../ENG-123 -b feat/ENG-123-build-billing-dashboard
cd ../ENG-123
python3 factory/scripts/intake.py --issue ENG-123 --title "Build billing dashboard"Each story lives in its own isolated worktree and branch with its own committed
.factory/ state. Tasks inside that story run sequentially. Stories whose
roadmap dependencies are done may run in parallel worktrees; pr_ready.py
archives evidence before merge.
- Plan (mandatory — enforced) — say: "Plan this task." and switch to
PLAN MODE (shift+tab). While the task is unplanned, the hook blocks
product-code edits and writing Codex delegation, so there is no way to
"just start coding". Plan per
factory/prompts/planner.md; exploration is delegated, never done by Claude Code itself:/codex:rescue --model gpt-5.6-terra --effort high(read-only by default; rawcodex execis hook-blocked, no exceptions).planner-highin Codex is the sanctioned alternate. New decisions get records. Before approval, grilling the plan is mandatory — say: "Grill me on this plan" (/grill-me); the verdict is recorded (record_grill_from_json.py --gate plan) andplan saverefuses without it. Then say: "Save the plan."
./forge plan save --from <approved-plan-file>- Decompose — say: "Decompose it." (
docs-decomposer; the recorded JSON must matchfactory/schemas/decomposition.json, incl. theuser_facingflag):
python3 factory/scripts/record_decomposition_from_json.py --input /tmp/decomposition.json
python3 factory/scripts/update_run.py --phase implementing --plan-status approved --decomposition-status recorded- Implement — say: "Implement it." The orchestrator runs
./forge stage start <id>and then./forge delegate <id>for one bounded task at a time. Write delegations stay in the foreground so stage close cannot race a worker that is still editing. Feature type routes the design skills, ENFORCED at record time:user_facing: truetasks MUST loademil-design-eng+frontend-designand attest them in the artifact'sskills_used— the recorder refuses otherwise (apple-design/animation-vocabularyadvisory for motion work); backend tasks skip them all. The implementer writes and runs the tests and records the artifact itself:
python3 factory/scripts/record_test_from_json.py --kind automated --input /tmp/automated-test.json
python3 factory/scripts/verify.py- Review — say: "Review it." ONE autoreview run in Codex, three
lenses (
factory/prompts/reviewer.md), three recorded artifacts:
python3 factory/scripts/record_review_from_json.py --aspect quality --input /tmp/quality-review.json
python3 factory/scripts/record_review_from_json.py --aspect performance --input /tmp/performance-review.json
python3 factory/scripts/record_review_from_json.py --aspect security --input /tmp/security-review.json- Functional check — only when the decomposition says
user_facing: true; then: "Is this PR ready?"
python3 factory/scripts/record_test_from_json.py --kind functional --input /tmp/functional-test.json
python3 factory/scripts/pr_ready.pypr_ready.py exits non-zero if any required artifact is missing, unstamped,
or stale. Merge stays manual.
Client emails, meeting transcripts, voice-note summaries, stray docs — drop
them in docs/context/ the moment you get them. Dumping is free; tracking is
automatic. Then say: "Process the context dump."
./forge context scan # register files in docs/context/ledger.json
# agent harvests per factory/prompts/harvester.md:
# pending file -> proposed decision records + DISCOVERY/BRIEF/architecture edits
./forge context mark <file> --harvested --outputs <paths> # or --ignored --notes "why"
./forge context list --pendingYou will not miss pending context — four surfaces make sure:
- every agent session opens with the unharvested count (SessionStart hook)
./forge nextputs "harvest first" as step 1 in any phase- the daily
gardenerworkflow opens a GitHub issue while anything is pending, and closes it at zero ./forge plan saverefuses while context is pending — you cannot approve a plan over an unread client email
Decisions proposed by a harvest still need a HUMAN accept
(./forge decision accept <slug> --by "Name").
Recorders refuse any artifact that does not match its schema in
factory/schemas/ — wrong shape, wrong types, or a generated_by outside
the harness.yaml allowlist. Adopting a new tool is a harness PR, never a
local choice (see WORKFLOW.md "Determinism Contract").
CI runs these on every PR (and you can run them any time):
python3 factory/scripts/check_dual_runtime.py # reference-not-duplicate + schema/allowlist parity
python3 factory/scripts/check_agents_hygiene.py # AGENTS.md size + links
python3 factory/scripts/check_factory_scaffold.pyIf codex-plugin-cc is unavailable, see docs/degraded-mode.md — same phase
prompts, same artifacts, direct codex exec.
Already built a prototype (or an early project) with agents, outside the
harness? Say: "Migrate this repo into the harness." (the
knacklabs-migrate-project skill installed by ./setup)
The deterministic core is forge adopt, run from the harness clone against a
CLEAN target tree:
./forge adopt --target ../legacy-repo --name my-appAdopt writes the same create-if-absent .factory/record-origin.json boundary
before future work is recorded. Running adoption again can never rewrite that
starting point or make the historical record look more complete than it is.
It vendors the machinery, preserves any pre-existing AGENTS.md/CLAUDE.md
into docs/context/migrated-* (the harvester picks them up), creates
project-owned files only where missing, and never deletes existing work —
every overwrite is a reviewable git diff. The skill then walks the judgment
part: sorting code into prototype/, dumping notes into docs/context/,
harvesting DISCOVERY/BRIEF and decision records, formalizing a historical
sign-off, and handing off to ./forge next. Repos that already carry the
harness are routed to forge upgrade instead.
Say: "Upgrade this repo to the latest harness." (the
knacklabs-upgrade-project skill installed by ./setup)
The skill locates the setup-pinned harness clone, verifies its origin, branch,
and clean state, and stops if its fast-forward-only pull fails. Every periodic
upgrade cycle also requires a clean, committed project baseline. It audits the
project, refreshes machinery with forge upgrade, repairs tooling with
forge doctor --fix, and pauses for review of the diff.
Harness-owned machinery (factory/ incl. schemas, adapters, constitution/,
contracts) is replaced; project-owned content (harness.yaml, AGENTS.md,
plans incl. the roadmap, decisions, context, prototype, .factory/) is never
touched, and factory/skills/proposed/ survives the swap. After the review,
the skill backfills deterministic project-level gaps and guides you through
re-authoring incomplete stories one at a time with forge roadmap fill.
Completed stories are never rewritten and roadmap data is never bulk-loaded.
It then re-runs verification and audit, reports forge next, and leaves the
reviewed upgrade for you to commit as the next cycle's clean baseline.