How to add the factory to an existing project.
# From your project root
git submodule add https://github.com/custodyzero/factory.git .factory
./.factory/setup.shThe setup script:
- Installs factory dependencies (isolated in
.factory/node_modules/) - Copies template files to your project root (no-clobber):
factory.config.json— project configurationCLAUDE.md— AI agent instructionsAGENTS.md— agent operating constraints
- Creates
factory/directory with artifact subdirectories:factory/intents/,factory/features/,factory/packets/,factory/completions/factory/memory/(durable host-project memory) andfactory/cache/(transient machine cache)
- Seeds
factory/memory/MEMORY.mdand category directories - Configures
git config core.hooksPath .factory/hooks
After setup, the normal workflow is:
- Author a spec at
specs/<spec-id>.md(see Authoring specs) - Run the full pipeline:
npx tsx .factory/tools/run.ts <spec-id> - The pipeline plans, develops, reviews, and verifies — autonomously to completion
- Re-run the same command to resume if anything failed (the pipeline is idempotent)
After setup, treat host-project memory as a small curated knowledge surface:
factory/memory/MEMORY.mdis the index the pipeline may always load- category directories hold durable memory
factory/memory/suggestions/holds candidate updates from pipeline runsfactory/cache/is transient machine state only
Use durable memory for things like:
- architectural invariants
- repeated failure modes
- stable team or repo conventions
- reusable local patterns
Do not use durable memory for:
- current packet/feature state
- run narration
- temporary debugging notes
- data already reconstructible from factory artifacts
Recommended host workflow:
- Keep
factory/memory/MEMORY.mdshort and link outward. - Add stable project knowledge to the category directories as it becomes worth reusing.
- Periodically review
factory/memory/suggestions/and promote only the durable items. - Treat
factory/cache/as disposable.
The factory exposes two ways to drive packets through their lifecycle:
- Autonomous mode —
run.ts <spec-id>. The pipeline calls the lifecycle library functions to advance state. Agents perform the work (write code, review it, verify it) but do not call the lifecycle CLIs themselves to signal start / request-review / complete. The reviewer agent is the one exception: it records its verdict viareview.ts --approveor--request-changesbecause that's how the pipeline learns the decision. - Manual mode — humans (or agents driving themselves) invoke the
lifecycle CLIs (
start.ts,request-review.ts,review.ts,complete.ts) directly to walk a packet through its states. This is the back-compat surface and the way to drive a stuck packet forward when the autonomous run has bailed out.
Both modes share the same lifecycle CLIs as the protocol surface; the difference is who invokes them. The full contract is in the Agent protocol appendix.
After setup, your project will have:
.factory/ # Tooling submodule (hidden)
specs/ # Human-authored specs (markdown + frontmatter)
factory/ # Factory artifacts (visible, one directory)
├── intents/ # Derived from specs (or hand-authored back-compat)
├── features/
├── packets/
├── completions/
├── memory/ # Durable host-project memory (tracked)
│ ├── MEMORY.md # Small always-loaded index
│ ├── architectural-facts/
│ ├── recurring-failures/
│ ├── project-conventions/
│ ├── code-patterns/
│ └── suggestions/ # Candidate updates; review before promotion
├── cache/ # Transient machine cache (gitignore)
├── events/ # Per-run event streams (JSONL)
├── cost/ # Per-invocation cost records
└── escalations/ # Structured failure records when recovery escalates
factory.config.json # Configuration
CLAUDE.md # AI instructions
AGENTS.md # Agent constraints
Tooling is hidden in .factory/. Artifacts are visible in factory/.
Specs live at the project root in specs/ so authors edit them next to
the code, not inside the tooling submodule.
The memory layer works best when the files stay small and specific:
- Put the broad summary in
factory/memory/MEMORY.md - Put one durable idea per file when possible
- Prefer concise statements over long narratives
- Link related notes from the index instead of copying them
- Promote from
suggestions/only after human review
Specs are the operator's interface to the factory. One spec describes one
unit of work; run.ts <spec-id> translates the spec into an intent and
drives the pipeline to completion. The full architectural rationale is in
docs/decisions/spec_artifact_model.md;
this section is the authoring guide.
specs/<spec-id>.md
At the project root, alongside factory.config.json. Tracked in git as
the source of truth for what the project intends to build. The directory
is intentionally distinct from any pre-existing docs/specs/ directory —
that one is project documentation; specs/ is factory-managed.
A spec is a markdown file with YAML frontmatter:
---
id: add-health-endpoint
title: Add /health endpoint
depends_on: [auth-baseline] # optional; default empty
---
# Spec body
The body is markdown. The planner reads it to derive an intent. Operators
author at the human-readable level; the factory translates to the locked
intent schema (1:1).| Field | Type | Required | Purpose |
|---|---|---|---|
id |
string | yes | Stable identifier; must match the filename (specs/<id>.md) |
title |
string | yes | One-line summary used in status output and audit logs |
depends_on |
array of strings | no, default [] |
Other spec IDs that must complete before this spec runs |
Frontmatter is intentionally minimal. Anything else (priority, owner, target release) goes in the markdown body or in human-managed tooling outside the factory — those fields are not consumed by the factory.
depends_on is the only sequencing primitive between specs:
- If spec
Adeclaresdepends_on: [B], the factory will not startAuntilBcompletes successfully (all packets in B's intent reachcompleted). - If
Bfails,Ais blocked and reported as such. - Cyclic dependencies are rejected at orchestrator entry.
- All transitive dependencies must be passed explicitly to
run.ts. Auto-resolution is out of scope; ifAdepends onBandBdepends onC, you must invokerun.ts C B A(in any order — topological sort computes the actual run order).
The markdown body is the spec content. The planner agent reads it to derive an intent. Operators author at the human-readable level (problem statement, acceptance, constraints); the planner pulls implementation detail into the intent during planning.
Recommended sections (not enforced):
- A short problem statement
- Acceptance criteria the change must satisfy
- Out-of-scope notes — what the change deliberately does not do
- Constraints the planner should respect
- Do not author intents directly. Hand-authored
factory/intents/<id>.jsonfiles still work for backward compatibility, but new work should be a spec. The factory derives the intent. - Do not put low-level implementation detail in the spec body. The planner translates the spec into an intent and decomposes the intent into packets — that is where implementation choices belong.
- Do not put a
status,priority, oracceptance_criteriafield in the frontmatter. Status lives on derived artifacts. Priority is a human concern outside the factory. Acceptance criteria are derived by the planner onto packets. - Do not split a single coherent change across multiple specs. One
spec maps to one intent (1:1). If you need cross-spec sequencing, use
depends_on.
Edit the template at your project root:
{
"$schema": "./.factory/schemas/factory-config.schema.json",
"project_name": "my-project",
"factory_dir": ".factory",
"artifact_dir": "factory",
"memory": {
"root_dir": "memory",
"cache_dir": "cache",
"suggestion_dir": "suggestions",
"max_additional_files": 4,
"max_file_bytes": 16384,
"max_cache_entries": 20
},
"verification": {
"build": "dotnet build",
"lint": "true",
"test": "dotnet test"
},
"validation": {
"command": "npx tsx .factory/tools/validate.ts"
},
"infrastructure_patterns": [
".factory/",
"factory/",
".github/",
"package.json",
".gitignore",
"CLAUDE.md",
"AGENTS.md",
"README.md",
"LICENSE"
],
"completed_by_default": {
"kind": "agent",
"id": "claude"
},
"personas": {
"planner": {
"description": "Decomposes intent into feature and packet artifacts",
"instructions": [],
"model": "high"
},
"developer": {
"description": "Implements the change",
"instructions": [],
"model": "high"
},
"code_reviewer": {
"description": "Reviews code changes for correctness, design, and contract adherence",
"instructions": [],
"model": "medium"
},
"qa": {
"description": "Verifies acceptance criteria are met",
"instructions": [],
"model": "medium"
}
},
"pipeline": {
"providers": {
"codex": {
"enabled": true,
"command": "codex",
"sandbox": "workspace-write"
},
"claude": {
"enabled": true,
"command": "claude",
"permission_mode": "bypassPermissions"
},
"copilot": {
"enabled": false,
"command": "gh",
"prefix_args": ["copilot", "--"],
"model_map": {
"high": "claude-opus-4-6",
"medium": "GPT-5.4",
"low": "claude-haiku-4-5"
}
}
},
"persona_providers": {
"planner": "claude",
"developer": "codex",
"code_reviewer": "claude",
"qa": "claude"
},
"completion_identities": {
"developer": "codex-dev",
"code_reviewer": "claude-cr",
"qa": "claude-qa"
},
"max_review_iterations": 3
}
}| Field | Type | Description |
|---|---|---|
project_name |
string | Used in status output headers |
factory_dir |
string | Path to factory tooling relative to project root (default: ".") |
artifact_dir |
string | Path to artifact directory relative to project root (default: ".") |
verification.build |
string | Shell command for build verification |
verification.lint |
string | Shell command for lint verification |
verification.test |
string | Shell command for test verification |
validation.command |
string | Shell command to run factory validation |
infrastructure_patterns |
string[] | File paths/prefixes that are not "implementation work" |
completed_by_default |
identity | Default identity written into completion records |
personas |
object | Planner, developer, code_reviewer, and qa persona defaults |
pipeline |
object | Provider mappings, completion identities, review iteration limits |
Each entry under pipeline.providers describes one CLI:
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Whether the provider is available for selection |
command |
string | The single executable token (bare name resolved against PATH, or absolute path) |
prefix_args |
string[] | Optional fixed leading argv elements prepended to every invocation |
sandbox |
string | (codex) sandbox mode: read-only, workspace-write, danger-full-access |
permission_mode |
string | (claude) permission gate: acceptEdits, bypassPermissions, ... |
model_map |
object | Per-tier (high/medium/low) model id mapping |
model_failover |
string[] | (abstraction providers) within-CLI failover model order |
command is one argv token. Under POSIX-style argv-mode spawn,
command is the executable path, not a shell-tokenized string.
Whitespace inside command is preserved as part of the path. If your
provider requires a sub-command (e.g. gh copilot --), put the
sub-command in prefix_args:
"copilot": {
"enabled": true,
"command": "gh",
"prefix_args": ["copilot", "--"]
}Operator migration note (DEP0190). If your factory.config.json
has a command string containing whitespace (the legacy shape, e.g.
"command": "gh copilot --"), you will see a deprecation warning on
load:
[factory] DEP0190: 'copilot' uses legacy shell-tokenized command "gh copilot --".
Migrate to command: "gh", prefix_args: ["copilot","--"]. See specs/dep0190-shell-removal.md.
Migrate by splitting the string: the first token becomes command,
the remaining tokens become prefix_args. The legacy shape continues
to load (kept for backward compatibility with hand-edited configs)
but will be removed in a future spec; the migration target above is
the supported shape going forward. See
specs/dep0190-shell-removal.md
for the rationale and the full migration story.
The factory operates under POSIX-style argv-mode spawn. Windows
operators run under WSL (or equivalent). Native-Windows .cmd/.bat
provider wrappers are not supported; install provider CLIs as
POSIX-spawnable binaries (e.g. via gh, claude, codex's native
installers under WSL).
Patterns ending in / match directory prefixes (e.g., factory/ matches
factory/packets/foo.json). Patterns without / match exact filenames
(e.g., package.json matches only the root package.json).
Adjust these patterns to match your project structure. Common additions:
- CI configuration directories (
.github/,.gitlab/) - Project-level config files (
.sln,Makefile,Cargo.toml) - Documentation directories
The pipeline (run.ts) is the single entry point for all factory work:
npx tsx .factory/tools/run.ts <spec-id> [<spec-id>...]
This runs to completion autonomously:
- Plan — Orchestrator translates each spec into an intent (1:1) and invokes the planner; the planner decomposes the intent into a feature with dev/qa packet pairs
- Develop — For each dev packet (in dependency order):
- Developer agent implements
- Code reviewer agent reviews (different identity)
- Feedback loop if changes requested (bounded by
max_review_iterations) - Completion recorded (build/lint/test verification)
- Verify — For each QA packet:
- QA agent verifies (different identity from dev)
- Completion recorded
- Done — Feature marked complete, summary printed (with total cost)
The gate depends on the run-input source:
- Spec-driven runs (
run.ts <spec-id>): exactly one human gate — authoring the spec. The intent is a derived artifact materialised by the orchestrator from the spec; itsstatusfield is a generator-set artifact, NOT a governance gate. The defaultproposedstatus is accepted automatically. - Intent-driven runs (
run.ts <intent-id>, backward-compat path): hand-authored intents are gated by theirstatusfield. The factory acceptsapproved,planned, anddelivered. The factory rejectsproposed,superseded, missing, and unknown values; the operator must edit the intent file and setstatus: "approved"to grant run authority. (plannedanddeliveredare accepted for idempotent reruns of intents that already progressed past plan.)
Completion IS acceptance — there is no separate "ready to ship" gate after the pipeline succeeds. See the end of this document for operator guidance on hand-authoring intents.
- Dev and QA agents use different identities (FI-7)
- Code reviewer uses a different identity from the developer
- Identities are configured in
pipeline.completion_identities
| Requirement | Version | Purpose |
|---|---|---|
| Node.js | >= 20 | Runtime for factory tooling |
| git | Any | Version control + hooks |
| Tool | Purpose |
|---|---|
| jq | Pre-commit hook reads config without Node.js for speed |
Factory's Node.js dependencies (tsx, vitest, typescript) are installed
inside .factory/node_modules/ and do not affect the host project.
The pre-commit hook runs two steps:
- Completion gate — FI-7 enforcement (blocks if implementation files are staged without completion records)
- Validate — full factory validation (schema + integrity + invariants)
If either step fails, the commit is blocked.
The hook resolves tool paths from factory_dir in the config, so it
works regardless of whether factory is at the project root or in a
subdirectory.
The pipeline runs autonomously from spec to completed feature:
-
Human authors
specs/<spec-id>.md(see Authoring specs). For backward compatibility, hand-authoredfactory/intents/<intent-id>.jsonfiles are still accepted — declaring eitherspec(inline body for short intents) orspec_path(path relative to the project root pointing at a Markdown file that holds the authoritative spec, e.g.docs/specs/016-platform-targets.md).spec_pathmust be relative, must not escape the project root, and must point at a non-empty file.validate.tsenforces the rules;plan.tsreads the file at plan time and hands its full contents to the planner.Hand-authored intents must declare
status: "approved"to grantrun.tsthe authority to run. This is the intent-driven approval gate (see Human Gates above).plannedanddeliveredare accepted for idempotent reruns;proposed,superseded, missing, and unknown values are rejected with an actionable error. Spec-driven runs (run.ts <spec-id>) are NOT subject to this check — the spec's authorship IS the approval, and the derived intent's generator-setstatus: "proposed"is accepted. -
Run
npx tsx .factory/tools/run.ts <spec-id> [<spec-id>...] -
Plan phase — orchestrator translates each spec into an intent (1:1); the planner agent then writes:
- one
factory/features/<feature-id>.jsonartifact withstatus: "planned" - matching dev/qa packet pairs in
factory/packets/ - packet dependencies, change classes, and acceptance criteria
feature.intent_idlinkage
- one
-
Develop phase — for each dev packet (in dependency order):
- Developer agent implements (via the configured
developerprovider) - Pipeline transitions packet status through the develop lifecycle
(
start→implementing→request-review); the developer prompt does NOT instruct the agent to call those CLIs - Code reviewer agent calls
review.ts --approveor--request-changesto record the verdict (the only lifecycle call the autonomous-mode prompts make) - On
--request-changes, the developer reworks; loop bounded bymax_review_iterations - On approval, the pipeline runs verification and records the completion with the developer's identity
- Developer agent implements (via the configured
-
Verify phase — for each QA packet:
- QA agent verifies (via the configured
qaprovider, distinct identity from dev) - The pipeline runs verification and records the completion with the
QA identity (FI-7 enforces distinct identities); the QA prompt
does NOT instruct the agent to call
complete.ts
- QA agent verifies (via the configured
-
Done — feature marked complete, summary printed (with total cost)
The lifecycle CLIs (start.ts, request-review.ts, review.ts,
complete.ts) are the protocol surface in both modes. In autonomous
mode the pipeline calls them as library functions; the agents perform
the work but only the reviewer records its verdict via the CLI. In
manual mode (debugging, back-compat, driving a stuck packet) humans or
agents may invoke the CLIs directly. See the
Agent protocol appendix for the
contract.
Pipeline properties:
- Idempotent — re-running resumes from artifact state on disk; lifecycle scripts are individually idempotent too
- Provider-agnostic — supports codex, claude, copilot (configure via
pipeline.providers) - Failover-aware —
persona_providersaccepts a list for cross-CLI failover; abstraction providers may declare within-CLImodel_failover(see Provider failover) - Recovery-aware — bounded auto-recovery for known scenarios; lint and test failures always escalate (see Recovery)
- Cost-visible — every run reports total cost; configurable caps abort on overage (see Cost visibility)
- Observable — typed events stream to
factory/events/<run-id>.jsonl(see Event observability) - Identity-separated — developer, code_reviewer, and qa identities are distinct (FI-7)
- Bounded review —
max_review_iterations(default 3) caps rework cycles - No human gates after spec authoring — completion IS acceptance
The pipeline emits typed events at every meaningful state transition. Each
event is one JSON line in <artifactRoot>/events/<runId>.jsonl (one file
per run). The stream is append-only during a run; rotation/archival is the
host's concern.
The taxonomy is closed — tools/pipeline/events.ts defines the full
EventType union. Grouped by category:
- Pipeline lifecycle:
pipeline.started,pipeline.spec_resolved,pipeline.finished,pipeline.failed - Spec lifecycle:
spec.started,spec.blocked,spec.completed - Phase lifecycle:
phase.started,phase.completed(the payload carriesoutcome: 'ok' | 'failed'— there is no separatephase.failedevent) - Packet lifecycle:
packet.started,packet.review_requested,packet.review_approved,packet.changes_requested,packet.completed,packet.failed - Verification:
verification.passed,verification.failed - Recovery:
recovery.attempt_started,recovery.succeeded,recovery.exhausted,recovery.escalated - Cost:
cost.cap_crossed
Every event has a stable shape:
{ event_type, timestamp, provenance, run_id, payload }.
Each event also carries a provenance label — live_run for normal
operator runs, test for events emitted by the test suite, plus
healthcheck, replay, and dry_run reserved for future tooling.
Consumers filter by provenance, so test-suite events do not pollute the
operator's stream.
To inspect a run:
tail -f factory/events/<run-id>.jsonl
jq 'select(.event_type == "recovery.escalated")' factory/events/<run-id>.jsonlFull design: docs/decisions/event_observability.md.
Every agent invocation is metered. The run summary emits zero, one, or many invocations per run; the summary line varies by what was metered:
- No invocations: the cost line is omitted entirely.
- All unknown-cost:
Cost: 3 unknown-cost invocation(s) (provider did not report tokens) - All known-cost:
Total cost: $0.4231 over 5 invocation(s) - Mixed:
Total cost: $0.4231 (3 unknown-cost invocation(s))
The unknown-cost count is invocations where the provider did not report
tokens (e.g., gh copilot); they are counted but contribute null
dollars rather than being silently zeroed.
Caps are configurable in factory.config.json. All three are optional and
default to disabled (no enforcement):
"pipeline": {
"cost_caps": {
"per_run": 5.00,
"per_packet": 1.00,
"per_day": 25.00
}
}| Cap | Behavior on cross |
|---|---|
per_run |
Aborts the entire pipeline run; emits cost.cap_crossed and pipeline.failed |
per_packet |
Fails just the affected packet; orchestrator continues to the next independent packet |
per_day |
Aborts the run AND records the date so subsequent same-day runs are blocked at orchestrator entry (LOCAL date) |
Caps use >= semantics: a running total at-or-above the cap triggers
escalation. The cap dollar values are USD.
Per-invocation cost records are written to factory/cost/. Operators
own the audit trail.
Full design: docs/decisions/cost_visibility.md.
The factory recognizes eight failure scenarios and applies a recipe per scenario. Five auto-recover with bounded retries; three always escalate.
| Scenario | Recipe | Per-packet retry budget |
|---|---|---|
ProviderTransient |
Wait, retry same provider/model | 2 |
AgentNonResponsive |
Treat as ProviderTransient |
2 |
BuildFailed |
Re-invoke developer with build error + guardrail prompt | 1 |
StaleBranch |
git fetch && git rebase origin/main; retry once |
1 |
ProviderUnavailable |
Cascade through within-CLI then cross-CLI failover (see Provider failover) | data-driven (= cascade length) |
LintFailed |
Escalate. Auto-recovery would invite agents to disable lint rules. | 0 |
TestFailed |
Escalate. Auto-recovery is the failure mode where agents mutilate tests to clear errors. | 0 |
CompletionGateBlocked |
Escalate. The pre-commit hook is an intentional human gate. | 0 |
When recovery exhausts its budget or hits an escalate-only scenario:
- The orchestrator writes a structured failure record at
factory/escalations/<spec-id>-<timestamp>.json - A
recovery.escalatedevent is appended to the run's event stream - The affected packet is marked
failed - Downstream packets and dependent specs are marked blocked
LintFailed and TestFailed are deliberately escalate-only. The factory
will not let an agent decide whether failing tests should be relaxed or
whether failing lint rules should be disabled — that is a human call.
Full design: docs/decisions/recovery_recipes_not_dsl.md.
Each persona maps to one or more provider CLIs. The factory supports two layers of failover.
Cross-CLI (persona_providers.<persona>) accepts either a single
provider name or an ordered list:
"persona_providers": {
"developer": ["codex", "claude", "copilot"],
"code_reviewer": ["claude", "copilot"],
"qa": "claude"
}The single-string form is the original shape and continues to work unchanged — no failover. The array form declares the failover order: the first entry is tried first, the next on failure, and so on. Existing configs do not need to migrate.
Within-CLI (pipeline.providers.<provider>.model_failover) applies
to abstraction providers — CLIs that route to multiple underlying
models (e.g., copilot). It is an optional ordered list of model IDs:
"copilot": {
"command": "gh",
"prefix_args": ["copilot", "--"],
"model_map": {
"high": "claude-opus-4-6",
"medium": "GPT-5.4",
"low": "claude-haiku-4-5"
},
"model_failover": ["claude-opus-4-6", "GPT-5.4", "claude-haiku-4-5"]
}Direct providers (codex, claude — one CLI maps to one upstream
provider) do not set model_failover; the field is reserved for
abstraction providers.
When ProviderUnavailable fires, the cascade walks each provider's
model_failover list (within-CLI) before falling through to the next
CLI in persona_providers (cross-CLI). When every entry is exhausted,
the scenario escalates.
Full design: docs/decisions/single_entry_pipeline.md
(see "Recovery" and "Provider failover").
cd .factory
npx vitest runThe factory tooling has its own test suite covering:
- Completion gate logic
- Status derivation
- Execute resolver
- Plan resolver
- Configuration utilities
- Terminal output formatting
All tests are pure function tests — no I/O, no mocking.
Factory artifacts (packets, completions, etc.) under factory/ are meant
to be committed alongside your code. They are the governance trail.
To update factory tooling from upstream:
cd .factory
git pull origin main
cd ..
git add .factory
git commit -m "Update factory tooling"Add to your project's .gitignore:
.factory/node_modules/
The setup script does not modify your .gitignore — add these entries
manually.
The lifecycle scripts below are the protocol surface for moving a packet through its lifecycle. They are the same scripts whether the caller is the orchestrator, an autonomous agent, or a human typing at a terminal — the difference is who invokes them, not what they do.
- Autonomous (
run.ts <spec-id>) — the orchestrator callsstart,request-review, andcompleteas library functions while driving the develop / verify phases. Agents under autonomous mode perform the underlying work but do NOT call those three CLIs themselves. The exception isreview.ts: the code reviewer agent calls it explicitly to record approve/request-changes — that is how the pipeline learns the verdict. - Manual (humans or self-driving agents) — anyone can invoke the lifecycle CLIs directly to drive a packet forward. This is the back-compat surface, the debugging surface, and the way to nudge a packet out of an in-between state when an autonomous run bailed out.
All four lifecycle scripts are idempotent: re-invocation on a state that already satisfies the request prints "already done" and exits 0. This means external callers can safely retry without producing duplicate state.
| Script | Purpose | Idempotent on |
|---|---|---|
start.ts <packet-id> |
Agent claims a packet; sets started_at, status → implementing |
started_at already set |
request-review.ts <packet-id> |
Developer signals dev work ready for code review; status → review_requested |
status already review_requested |
review.ts <packet-id> --approve / --request-changes |
Code reviewer records decision | status already matches the requested decision |
complete.ts <packet-id> |
Runs verification (build/lint/test), writes the completion record | completion record already exists (skips re-running verification) |
Two related scripts are part of the protocol but not state-mutating:
| Script | Purpose |
|---|---|
plan.ts <spec-or-intent-id> |
Resolves what the planner should do; reads spec/intent and returns a structured planner action |
execute.ts <feature-id> |
Resolves which packets are ready next, with persona and model assignments |
The flow looks different depending on which mode is driving the packet.
Autonomous mode (run.ts) — the orchestrator drives the lifecycle.
The agent prompts the factory ships in tools/pipeline/prompts.ts
explicitly tell each persona NOT to call start.ts,
request-review.ts, or complete.ts. The reviewer prompt is the
exception: it instructs the reviewer to call review.ts --approve /
--request-changes so the pipeline learns the decision.
Manual mode — the natural lifecycle for an agent (or human) driving a packet through its states by hand:
1. Run start.ts <packet-id> (mark in progress)
2. Implement the change
3. Run request-review.ts <packet-id> [dev only]
4. Wait for review.ts decision [dev only]
5. On --approve: run complete.ts <packet-id>
On --request-changes: rework, go to 3
The lifecycle scripts behave identically in both modes — they're the
same protocol. Choose the mode that matches your intent: run.ts for
the autonomous one-command pipeline; the lifecycle CLIs directly when
you need to drive a single packet by hand.