#440 shipped spawnForge in src/actions/spawn.ts with tests and no caller. Intent: an owner (or Claude over the MCP) can spawn a child forge, and every caller sees the plan before anything is created.
Acceptance:
[ ] POST /api/v1/forges {name, apps[], crew[], dryRun}: rw token, admin only, validated at the boundary (name pattern, known apps, known crew). It returns the plan from src/spawn/plan.ts when dryRun is true (the default), and the Spawned result otherwise
[ ] GET /api/v1/forges lists this forge's children from the spawn ledger
[ ] an MCP spawn tool in src/http/mcp.ts with the same input, dry-run by default, and a description in the MCP's words. ro tokens can dry-run and cannot spawn
[ ] every SpawnError kind maps to one status and one sentence
[ ] tests (in the gate): dry-run, refusal for a non-admin, refusal for an ro token, a successful spawn with a fake runtime from SpawnDeps, and the MCP tool round-trip
#440 shipped
spawnForgeinsrc/actions/spawn.tswith tests and no caller. Intent: an owner (or Claude over the MCP) can spawn a child forge, and every caller sees the plan before anything is created.Acceptance:
POST /api/v1/forges {name, apps[], crew[], dryRun}: rw token, admin only, validated at the boundary (name pattern, known apps, known crew). It returns the plan fromsrc/spawn/plan.tswhendryRunis true (the default), and theSpawnedresult otherwiseGET /api/v1/forgeslists this forge's children from the spawn ledgerspawntool insrc/http/mcp.tswith the same input, dry-run by default, and a description in the MCP's words. ro tokens can dry-run and cannot spawnSpawnErrorkind maps to one status and one sentenceSpawnDeps, and the MCP tool round-tripFiles: src/http/api.ts, src/http/mcp.ts, src/actions/spawn.ts (export only), docs/MCP.md, test/spawn-api.test.ts. Fits: torque.