Programs: large requirements as parallel workstreams¶
One bounded run cannot absorb a requirement that spans many components. A program splits it into workstreams with explicit dependencies and literal ownership. The program manifest is the program's agreement: you approve it by exact token before any workstream starts, and you approve every later revision of it the same way. Each workstream runs as an ordinary AutoCode build run in its own Git worktree, with its own requirements, plan review, exact human approval, Builders, independent validation and completion gate, and completed workstreams are merged onto one integration branch in dependency order. The walking skeleton is merged and verified before any other workstream starts; every merge re-runs the cumulative checks of everything merged so far; the final check follows the agreement's user journeys by name.
The program controller schedules, briefs each child with the agreement, checks each child plan against it, and integrates. It never approves a plan on your behalf, never merges into your default branch, and never runs a deployment workstream without explicit authorization.
Big request
|
v
autocode program plan -> ordinary planning units (Requirements, Planner, Plan Reviewer)
| you answer questions and approve the exact displayed plan
v
autocode program derive -> program.json: one workstream per approved milestone, the first
| one the walking skeleton, plus the final check (integration)
| you review or edit it: interfaces, journeys, checks
v
autocode program show -> the agreement and the token that approves it, a1:<digest>
autocode program approve --token a1:<digest>
| nothing starts before this; every later revision is approved too
v
autocode program run -> integration branch autocode/program-<key>/integration
walking skeleton first: merged, cumulative checks pass
then the workstreams that extend it, one worktree each,
branched from the merged integration head
each merge --no-ff re-runs the cumulative checks;
a failing merge is undone
final check on the integration branch itself:
verifies every user journey by name
deployment workstreams wait for --authorize-deployment
Plan it¶
autocode program plan "Build an order system: catalog, cart, checkout, gateway, ..." \
--workspace /path/to/project
This launches the normal planning-only unit (--unit autoplanner, read-only, in place)
with a program preamble: milestones must be independently deliverable workstreams
with disjoint affected_paths, explicit depends_on, and acceptance criteria that can
be verified on that workstream's own merged result. The first milestone is the walking
skeleton: the thinnest version that works from start to finish across every layer,
walking the main user journey, with the shared interface contracts it needs. Every
other milestone depends on it and extends it. Deployment, credentials and external
systems stay outside every milestone. Answer the questions and approve the displayed
plan exactly as for any run:
autocode --workspace /path/to/project --run-dir RUN --status # needs.questions and needs.resolver_token
autocode --workspace /path/to/project --run-dir RUN --answer 'Q1=...' --resolver-token 'TOKEN'
autocode --workspace /path/to/project --run-dir RUN --show-goal
autocode --workspace /path/to/project --run-dir RUN --approve-goal 'r3:<hash>'
--answer needs the current resolver token from the status view, and a question that offers options
takes one of their exact labels (or its proposed default); --delegate Q1 with the same token accepts
the proposed default (CLI).
When the plan run ends, program plan prints the next commands (derive, show, approve,
run).
Derive the manifest¶
autocode program derive --run-dir RUN --output program.json
derive reads the plan run's approved_contract status-view field (see
Task-run interface), never its state.json, and refuses a
plan that is not approved. --workspace names the plan run's project when it is not
the directory that holds .autocode/runs/RUN; --name sets the program name (default:
the approved outcome, cut to 60 characters); without --output the manifest is printed.
It turns each approved milestone into a code workstream: objective plus its acceptance
criteria as the brief, affected_paths as ownership, depends_on as dependencies,
and the milestone's criterion ids as acceptance_criteria. Then:
- The first milestone without dependencies is the walking skeleton (
skeleton: true). Any other milestone without dependencies is made to depend on it, andderivation_notessays so. - One
integrationworkstream is appended. It depends on every sink milestone, validates the approved end-to-end flow, and lists every acceptance criterion. If a milestone is already namedintegration, the generated one gets a noncolliding id. journeysgetsJ1"Main user journey", whose steps are the approvedend_to_end_flow(or the outcome when there is none), plusskeleton-journey"Walking skeleton journey" from the first workstream's brief. Ids avoid criterion collisions. The skeleton selects only its thin journey; integration inherits both.- Shared constraints, permission boundaries, technical approach, end-to-end flow and
deliverables are copied into
shared;shared.interfacesis left empty. contractholds the whole approved contract body with itstask_id,revisionandhash: required behaviors, failure cases, exclusions, assumptions and human-review obligations are not discarded.source_runrecords the plan run; it is provenance, not part of the agreement's digest.- No
checksare derived.
Before approving, add what derivation cannot know: the interfaces between workstreams
(with a producer and consumers), better journey names and steps, and program checks
that walk the journey. Without a runnable check the skeleton cannot be verified (see
Walking skeleton and cumulative checks).
Derivation does not approve a child plan. Each child still plans and asks for its own exact approval; a seven-workstream program can require seven additional plan approvals and additional clarification. The benefit is isolation and parallelism, not fewer human decisions. The parent contract is supplied as child planning context, not a replacement for reviewing the child plan or for its human-review criteria.
You can also write a manifest by hand. tools/task_scenarios.py carries a complete
example (PROGRAM_MANIFEST) for a four-service order system: a walking skeleton, a
journey, a program check and a versioned interface with a producer and consumers.
Approve the agreement¶
autocode program show program.json --workspace /path/to/project
autocode program approve program.json --workspace /path/to/project --token a1:<digest>
show prints the agreement as you approve it and saves nothing:
- the revision and
Approve with token: a<revision>:<digest>, where the digest covers the whole manifest exceptsource_run; - for a revision, the changes since the approved revision and the workstreams that lose
their approval. The changes name the parts that differ, the parent contract's
milestones and its
revisionincluded, and order-only changes too (order of requirements,workstream X owns order). The list is never empty for a revision: one that changes only parts no line names showsthe manifest changed in a way no workstream is built from: <keys>; - the outcome, the shared lists and the requirements;
- the user journeys, with what each simulated one does not prove;
- the workstreams: the walking skeleton marked, ownership, dependencies, any
skeleton_exemptreason, the inherited requirement ids, the first brief line and the workstream's own checks; - the interfaces with version, producer, consumers, paths, schema and behavior;
- the program checks;
- a closing line: approving the agreement approves no workstream's plan.
When nothing is pending it prints the approved revision instead.
approve accepts only the token of the manifest file as it is now; any other token is
refused, including one shown for a manifest that has since been edited. It prints
{approved, affected, next}; the first approval lists every workstream as affected.
Approval does not check that you ran show: the token also appears in the summary of
program run and program status (agreement.pending.token and next). Read the
agreement before you approve it.
Until the first approval, program run saves the program's state, prints
WAITING_AGREEMENT_APPROVAL and exits 2. It creates no integration branch and no
worktree.
Manifest rules (version 1)¶
| Field | Rule |
|---|---|
version, name, brief |
version is 1; name and brief are nonempty. The name keys the program's state (.autocode/programs/<key>): another name is another program |
workstreams[] fields |
Only id, kind, brief, owns, depends_on, acceptance_criteria, engine, skeleton, skeleton_exempt, journeys, checks |
workstreams[].id |
Starts with a letter or digit, then letters, digits, ., _, -; at most 64 characters; unique |
kind |
code, content, integration, deployment. content is non-runtime work such as documentation. Program-level ui is explicitly deferred until UI checkpoint recovery is supported; use autocode ui separately |
owns |
Literal repository-relative paths (no globs, .., .git, or .autocode). Non-integration workstreams must declare at least one. Two workstreams that could run at the same time may not own the same or nested paths; workstreams ordered by a dependency may |
depends_on |
Known ids, no self, no duplicates, acyclic |
| integration | Exactly one: the final check of the whole product on the merged result. It must (transitively) depend on every non-deployment workstream |
| deployment | Must (transitively) depend on the integration workstream; nothing except another deployment may depend on it |
skeleton |
Exactly one workstream, a code workstream with no dependencies. Every other workstream must (transitively) depend on it unless it is skeleton_exempt |
skeleton_exempt |
Only a content workstream may declare this nonempty reason for not waiting for the skeleton. It cannot produce or consume a declared runtime interface |
workstreams[].journeys |
Only the skeleton may select a nonempty list of distinct agreement journey ids. Without it, the skeleton inherits every journey; integration always inherits all journeys |
contract or requirements |
Where the requirements come from: the parent contract's acceptance criteria (derived manifests), or a top-level requirements list of {id, criterion, verification_method, human_review} with unique ids. Never both; both may be absent. contract is an object and its body an object; each of the body's acceptance_criteria needs an id and a criterion, and the ids are unique |
workstreams[].acceptance_criteria |
Requirement ids the workstream inherits. When the agreement has requirements, each must be one of them, and every requirement must be listed by some workstream. When it has none (no parent contract acceptance criteria and no requirements), the ids are listed in the workstream's brief but neither inherited nor checked |
journeys |
Required and nonempty: {id, name, steps, simulated, does_not_prove} with nonempty steps. A simulated: true journey must say what it does not prove. Ids are unique and differ from requirement ids |
checks |
Top level: program checks, commands re-run on the integration branch after every merge. Per workstream: commands re-run once it is merged and after every later merge |
engine |
codex or opencode for that workstream's run (overrides --engine) |
shared |
Optional lists (constraints, permission_boundaries, end_to_end_flow, technical_approach, deliverables) passed into every child brief, and interfaces |
shared.interfaces[] |
A list. Each entry: id (unique), summary, paths (a list, same rules as owns), version (positive integer, default 1), producer (a workstream id; that workstream owns every interface path), consumers (a list of distinct workstream ids other than the producer, each depending on it transitively; consumers need a producer), schema and behavior (text or JSON) |
derivation_notes, source_run |
Written by derive. source_run is not part of the agreement's digest |
autocode program run program.json --dry-run validates and previews without touching Git.
Writing deployment descriptors is ordinary code, not deployment. Put that work
before integration so it is included in final verification. Reserve deployment for
actual deployment work, which requires --authorize-deployment on every invocation
that starts or resumes it, as well as permission in its own approved child plan.
Run it¶
autocode program run program.json --workspace /path/to/project --max-parallel 2
Each invocation does one pass:
- Reads the manifest. If it differs from the approved agreement, it records a pending
revision and stops at
WAITING_AGREEMENT_APPROVAL(exit 2): nothing starts, resumes or merges until you approve it. A manifest that is not a revision of the approved agreement is refused (see Revisions and STALE). - Creates the integration branch and worktree on first use (from the project's
committed
HEAD, under.autocode/worktrees/program-<key>-integration).<key>is the program name as a slug plus a short hash of it. Takes back a merge an interrupted pass made and never verified (see below). - Refreshes every launched workstream from its child run's status view
(
autocode --status, read through the task-run interface). - Marks stale every workstream whose part of the agreement an approved revision changed, and retires its run.
- Checks each child's plan against the requirements it inherits (see Inherited requirements). From a completed child it keeps the checks its run replayed and passed and, for the final check, its journey results.
- Merges, one at a time, each completed workstream that nothing holds (step 7), unless
the last plan the program saw from its run drops an inherited requirement. A
workstream of any kind merges only under its current run's approved plan, read from
the run's status view and checked (
approved_plan): a run that completes without showing one pauses atPAUSED_INHERITANCE, naming the run, with nothing merged. For a non-integration workstream it first checks the delivered changes againstowns, including committed changes, deletions, rename source/destination paths and new files; out-of-scope changes pause without merging. Runner metadata under.autocode/is excluded; pre-staged metadata must be unstaged. A delivery that changes a shared interface without an approved change pauses too. Then it commits, merges (--no-ff) and re-runs the cumulative checks, undoing its merge when they fail. Any pause raised while merging (no checked plan, failing cumulative checks, ownership, metadata, an interface change, a conflict, a dirty integration worktree, an unverified journey) ends the whole pass: no later workstream is merged and nothing starts. A delivery that fails again unchanged raises the same pause on every rerun, before any workstream after it is merged and before anything starts, so one such workstream holds up the rest of the program until it is fixed or retired. Only a conflict is set aside: the workstream staysCONFLICTand later passes merge and start the others. - Starts or resumes every workstream whose dependencies are all merged and that
nothing holds, up to
--max-parallelat once, each new one in a fresh worktree branched from the current integration head. A workstream is held while the walking skeleton is not yet verified; while a change request is open on an interface the agreement binds it to (see Interfaces and change requests); while its plan holds the program atPAUSED_INHERITANCE, until a person sends its run back to plan again (see Inherited requirements); and, for deployment, without--authorize-deployment. The final check is held until every other non-deployment workstream, includingcontent, is merged. The summary gives the reason asblocked_reason. - Repeats steps 3 to 7 until nothing more can start. Each child is invoked at most once per pass.
- Prints a JSON summary and exits
0only when the program isCOMPLETE, otherwise2.
Child runs are ordinary autocode <brief> --in-place --no-chat --workflow build runs:
a workstream is a build job by construction, never left to the recognizer to route.
--engine (or the workstream's engine) and any unrecognized flags are passed through
to them when they start; a resumed child keeps its saved settings.
The summary has the program status and next, state_file, integration_branch and
integration_workspace, agreement (revision, approved, token and the pending
revision with its token, affected workstreams and changes), skeleton (the
verified skeleton's commit and check count), journeys, change_requests, one row per
workstream and, once complete, final_check. A workstream row carries its status, run
directory and worktree, what its child needs, and the program's own records: pin (the
agreement revision and scope fingerprint its run was built from), approved_plan,
plan_check, stale_reason, retired_runs, checks, verification,
integration_check and blocked_reason.
A child run pauses at its own human gates. The summary lists each waiting run
directory with what the child needs (needs: its kind; for a plan approval, the
token to approve; for a question, its request_kind, resolver_scope,
resolver_request_id and each question's id and text, never an answer token) and
its one-line progress; answer or approve there with the
normal CLI, then rerun program run. A run you have acted on (its status is back to
RUNNING) is resumed by the program; a run still at a gate or at any PAUSED_*
status is left alone.
An operational failure requires an explicit retry after inspecting its logs:
autocode program run program.json --workspace /path/to/project --retry-workstream catalog
The retry reuses the worktree and existing child checkpoint, if one was created. It does not approve a plan or resume a child-level pause. The controller saves the worktree, and the runs already in it, before starting a child; after a controller interruption, a later invocation reattaches to the one child run created in that worktree since. A run that was already there, such as your own run in the integration worktree, is never adopted. An interruption without a child checkpoint is marked failed and requires the same explicit retry. A checkpoint whose status cannot be read (including one whose worktree was removed), or a worktree holding several new runs, is refused rather than replaced.
A pass interrupted after merging a workstream but before verifying the merge leaves a
merge nobody verified. The next pass first takes it back (git reset --hard to the
integration head before it, recorded as the event unverified_merge_undone), then
merges and verifies the workstream again. Only a merge still at the integration head is
taken back. When the integration worktree has uncommitted tracked changes at that
point, the pass pauses at PAUSED_INTEGRATION_DIRTY instead.
| Program status | Meaning | Your next action |
|---|---|---|
NOT_STARTED |
Shown only by run --dry-run and status before the program has saved any state |
Read the agreement with show, approve it, then program run |
WAITING_AGREEMENT_APPROVAL |
The manifest is an agreement revision nobody has approved yet, the first one included. Nothing starts, resumes or merges | program show, then program approve --token with the token it prints, then rerun |
RUNNING |
Workstreams are under way and none waits for you | Rerun to continue |
WAITING |
A child run needs a question answered, a plan approved, or an explicit resume, or waits only for its next invocation | Act in the listed run directory, rerun |
WAITING_CHANGE_REQUEST |
An interface change request is open, even if every workstream is merged. The workstreams the agreement binds to its interface (its producer and consumers, every workstream when it has no producer) and the final check neither start, resume nor merge; other workstreams go on | Accept it (publish the interface's next version and approve that revision), reject it with program resolve-change, or approve a revision that removes the interface, which withdraws it; serve any listed child runs; rerun |
AUTHORIZATION_REQUIRED |
Everything else is merged; deployment workstreams need authorization to start or resume | Rerun with --authorize-deployment after deciding deployment is wanted |
BLOCKED |
A child invocation failed, with or without a saved checkpoint | Inspect its logs, then use --retry-workstream ID; missing checkpoints must be restored |
PAUSED_MERGE_CONFLICT |
A completed workstream conflicts with the integration branch; the merge was aborted, both branches are intact. That pass ends; later passes merge and start the other workstreams | Merge it by hand in the integration worktree, commit, rerun (the program adopts the manual merge once the cumulative checks pass, and only under the run's checked plan, as any merge; then work the run left uncommitted in its worktree after the conflict, such as a follow-up's, is committed and merged on top of it as an ordinary merge. Until that merge lands, the checks your merge passed run on every other merge, and its interfaces and the skeleton count as delivered only once it lands. Work committed on the workstream branch after your merge needs another merge by hand) |
PAUSED_INTEGRATION_DIRTY |
The active integration worktree is dirty or on the wrong branch, or a retired final check cannot be isolated from an exact accepted frontier (missing receipts, inconsistent ancestry or a guarded manual resolution) | Preserve the worktrees and reconcile the named branch, original receipts or manual resolution. When legacy acceptance cannot be established, start a new program from an explicitly reviewed checkout; no evidence is reconstructed |
PAUSED_OWNERSHIP |
A completed workstream changed files outside owns, or switched branches. Nothing was merged; every rerun repeats it, and nothing starts until it is fixed |
Correct the workstream delivery or restore its recorded branch, then rerun |
PAUSED_METADATA |
Runner metadata was staged or committed. Every rerun repeats it, and nothing starts until it is fixed | Unstage .autocode without deleting it, or remove the metadata diff, then rerun |
PAUSED_INTERFACE_CHANGE |
A delivery changes a shared interface without an approved change. Nothing was merged; every rerun repeats it, and nothing starts until it is fixed | Remove that change, or raise it with program request-change and approve the new interface version |
PAUSED_INHERITANCE |
A workstream's plan still drops an inherited requirement after the automatic rejections, or its run refused the program's feedback (plan_check.feedback_error), or its run completed without showing an approved plan the program could check (nothing was merged; every rerun repeats it) |
Give its run feedback yourself (the next rerun resumes it to plan again, and checks that plan), or revise the agreement, then rerun. For a run completed without an approved plan, follow it up so it plans again and approve that plan, then rerun |
PAUSED_SKELETON_UNVERIFIED |
The walking skeleton has no runnable check on the integration branch. The program undid its own merge of it (a merge you made by hand stays, but is not accepted), and nothing else starts | Add program checks that walk the journey, approve that revision, rerun |
PAUSED_INTEGRATION_CHECK |
After a merge, the integrated product fails its cumulative checks. The program undid its own merge of a code workstream; the final check's own commits stay, and so does a conflict resolution you merged by hand (the message says it is still on the integration branch). An unchanged rerun repeats it, and nothing starts until it is fixed | The message names who left the failing check. Its own check: fix the workstream (for example --follow-up on its run). An earlier workstream's (or the program's) check: fix the merging workstream if it broke that behavior, or, if the check held only for the product before this merge, revise the agreement so that workstream is re-checked (its checks are replaced) or change the program checks. Or undo or repair your own merge. Then rerun; the receipts are listed |
PAUSED_JOURNEY_UNVERIFIED |
The final check completed without verifying every user journey, and was not merged. Every rerun repeats it until the run is followed up | Follow up its run so each journey is verified, then rerun |
COMPLETE |
Every workstream is merged on the integration branch, every journey is verified and no change request is open | Review the branch and merge it into your default branch yourself |
When several apply, the summary shows the first of: a pending agreement revision;
everything merged with no change request open (COMPLETE or
PAUSED_JOURNEY_UNVERIFIED); a conflict; BLOCKED; PAUSED_INHERITANCE;
WAITING_CHANGE_REQUEST; AUTHORIZATION_REQUIRED; WAITING; RUNNING. So a change
request raised after every workstream merged keeps the program at
WAITING_CHANGE_REQUEST until it is accepted, rejected or withdrawn. A pause raised
during the pass replaces the status, and its message becomes next. program status
keeps showing that pause; the next program run clears it and looks again.
Workstream statuses:
| Workstream status | Meaning |
|---|---|
PENDING |
Not started |
RUNNING |
Being invoked in this pass |
WAITING |
Its run waits at a question or plan approval, or waits only for its next invocation |
PAUSED |
Its run stopped at any other status, such as a PAUSED_* of its own; a person decides |
COMPLETE |
Its run completed and it is not merged yet: held (blocked_reason), not reached before a pause ended the pass, its merge failed the cumulative checks, or (the final check) a journey is unverified |
CONFLICT |
Its merge conflicted; see PAUSED_MERGE_CONFLICT |
MERGED |
Merged on the integration branch, and the cumulative checks did not fail |
FAILED |
An invocation failed or its status could not be read; see BLOCKED |
STALE |
Its run was retired, by an agreement revision or a dropped inherited requirement; a fresh run starts when its dependencies are merged and nothing holds it |
autocode program status program.json --workspace ... prints the same summary without
launching anything: it reads each unfinished child's status view and saves nothing.
Revisions and STALE¶
The manifest file is the agreement. When it differs from the approved agreement, the
next show, run, status or approve sees it as revision N+1, pending. show names
what changed and which workstreams lose their approval; nothing starts, resumes or
merges until you approve it. Editing the file again before approving replaces the
pending revision (same number, new token); restoring the approved file withdraws it.
The program state keeps the approved agreement in full (agreement.approved) and, for
each approved revision, its number, digest, approval time, affected workstreams and
changes (agreement.history); earlier revisions are not stored in full.
A revision may not change:
- the workstream graph: ids,
kind,owns,depends_on, which workstream is theskeletonand which areskeleton_exempt; - an interface's definition without advancing its version by exactly one. An unchanged definition must keep its version: no version-only bump or skipped version is allowed. Removing an interface does not erase its approved version history, even if it was never delivered; reintroducing it requires exactly the last approved version plus one.
Such a manifest is refused with the reason by show, approve, run and status;
restore it, or start a new program with a new name (the name keys the program's state,
so a renamed manifest is a new
program). The graph and interface definitions are compared ignoring order and
spelled-out defaults: reordering owns, depends_on, or an interface's paths or
consumers, writing out skeleton: false, consumers: [] or producer: null, or
rewording a skeleton_exempt reason is neither a new program nor an interface change
that needs a new version. It still changes the agreement's digest, so it is a revision
to approve, but no workstream's scope (below) counts these differences, so no
workstream loses its approval.
Briefs, requirements, journeys, checks, shared lists, the parent contract and
interfaces at a new version can all change. Adding an interface, or removing one, is an
ordinary revision. Program state saved before agreements existed is refused the same
way: use a new program name.
An older inheritance-policy approval also requires renewed human approval, even when
the manifest is unchanged. This uses the same agreement revision and token, records a
policy_upgrade approval-history entry, and requires fresh conforming child plans.
It is not a fabricated manifest revision or proof of legacy acceptance. Runs, receipts
and worktrees remain available for reconciliation.
A revision affects a workstream when it changes that workstream's part of the agreement (its scope fingerprint):
- the program outcome, the shared lists and the parent contract other than its acceptance criteria and milestones (these affect every workstream);
- its own manifest row, except
engine, the order of itsownsanddepends_on, a spelled-outskeleton: falseand the wording of askeleton_exemptreason; - the definitions of the requirements it inherits;
- the interfaces it produces or uses, apart from the order of their
pathsandconsumersand spelled-out empty defaults (an interface with neither producer nor consumers binds every workstream; the final check is bound by all of them); - the journeys, for the walking skeleton and the final check.
The program checks, engine, derivation_notes, the parent contract's milestones and
its task_id, revision and hash, and the order of the workstreams, requirements and
interfaces are in no workstream's scope: a revision that changes only them needs
approval but takes no workstream's approval away. A changed program check applies from
the next merge on.
Every workstream records, when its run starts, the revision and the scope fingerprint
it is built from (pin). After a revision is approved, the next program run marks
STALE each started workstream whose fingerprint changed, whatever it was doing, merged
or waiting at plan approval included:
- its run is retired: it is listed in
retired_runs(with its run directory, status, worktree, branch, merged commit, pin andapproved_plan), and its plan's approval no longer counts: the workstream has noapproved_planuntil its fresh run's plan is approved. The retired run and its worktree stay on disk; stale_reasonnames the revision;- a fresh run starts when its dependencies are merged and nothing holds it. Its brief
carries a
RE-CHECK:line that asks it to plan against the agreement, keep what still conforms, change what does not, and verify again. It asks for plan approval like any run; - a workstream that was merged, or whose conflict you resolved by hand while its later
work still waits to merge (see
PAUSED_MERGE_CONFLICT), or that depends on a workstream re-checked by the same revision, restarts in a fresh worktree from the current integration head. Any other plans again in its own worktree; - when the walking skeleton is stale, everything that waits for it is held again until its re-check is merged and verified.
STALE never reverts code. A merged workstream's earlier merge stays on the integration
branch; its re-check starts from there and is merged and verified again, even when it
changes nothing. Workstreams the revision does not affect keep their runs and their
approval.
A re-check may leave source unchanged, including an implementation task whose files
already conform. Exact accepted-source snapshots and matching prior approval and
verification receipts admit that source only as a candidate for fresh review. The
fresh run still invokes its Builder and independent review against the current source;
old PASS receipts give no completion credit. guard: proof can verify behavior already
present, while test: still requires discriminating change proof
(named test proof).
When a retired final check leaves edits or extra commits, the controller can create a fresh integration worktree at the exact accepted integration frontier. It validates approval, check and merge-history receipts and ancestry rather than trusting HEAD. The old branch, HEAD, index, untracked files and run evidence stay intact; retired checks are removed from the active cumulative set until their workstream lands again. Unknown legacy receipts or a guarded manual resolution pause for reconciliation, not deletion or automatic acceptance. A curated, explicitly reviewed checkout may instead seed a new program with fresh approvals.
Inherited requirements¶
A workstream inherits the requirement ids in its acceptance_criteria (only when the
agreement has requirements); the skeleton also inherits its selected journey ids
(all journeys by default). The final check inherits every journey id and every
requirement id that a workstream other than a deployment workstream lists. A
requirement only deployment workstreams list is theirs: the final check, which runs
before them and may not deploy, does not inherit it, and a change to it does not
affect the final check. A workstream's brief lists its inherited ids, and its plan must
keep each one exactly once, with the same id and criterion text (only outer whitespace
may differ), preserving human_review: true. Every pass, the program
reads each child's plan from its status view: the draft shown for approval
(displayed_plan) or the plan in force (approved_contract):
- A draft that loses an inherited definition, human-review obligation, constraint or
permission boundary gets the program's guidance naming the loss before anyone
approves it (
--program-guidance TEXT --review-token -, bound to that draft's token), and plans again at its next invocation. The child saves it as the program's guidance, never as a person's feedback: it is no requirement the next plan must quote, no basis for a decision, and starts no clarification episode (#866). This guidance is the only input the program ever gives a child; it never approves anything. - An approved plan that drops one is never resumed or merged. Its run is retired
(
STALE) and a fresh run plans again in the same worktree, or, while a conflict you resolved by hand waits for the run's later work, in a new worktree from the integration head. - Nothing merges while the last plan the program saw from a workstream's run drops an inherited id, even when that run completes.
- The program rejects a workstream's dropping plans twice at most (
plan_rejections, counted across its runs). A third dropping plan is held, and the program pauses atPAUSED_INHERITANCE. So does a child that refuses the program's guidance; the error is inplan_check.feedback_error. - To go on from
PAUSED_INHERITANCE, give the run feedback yourself (autocode --workspace WORKTREE --run-dir RUN --feedback "..."), or revise the agreement. Once the run is back atRUNNINGwith no plan shown, the nextprogram runresumes it to plan again. The program checks that plan like any other but sends no more guidance of its own: a plan that still loses an inherited obligation pauses again, and nothing merges until a plan keeps all inherited definitions and boundaries.
plan_check records the plan token checked, the ids it dropped and whether it was
approved. A conforming plan is recorded in approved_plan once it
is approved, and nothing merges without it, a conflict resolved by hand included. It
follows the plan in force: it is dropped when the completed run shows no approved plan,
or an approved plan that drops an inherited id; a retired run takes its approved_plan
into its retired_runs entry, so a fresh run merges only under its own. Ids and
definitions are compared exactly: a renamed, duplicated or weakened criterion,
or a removed human-review obligation, counts as dropped. plan_check.boundaries
also records lost parent/shared constraints and permission boundaries; approved plans
must retain parent scope exclusions. Verification methods may adapt to the proof base.
Keeping an id alone is insufficient. Case, punctuation and inner whitespace in its
criterion remain part of the agreement. Put worked examples and test: or guard:
proof in verification details without weakening the inherited definition. Exact
human plan approval and independent validation remain separate requirements.
Interfaces and change requests¶
An interface in shared.interfaces is how two workstreams agree to connect: a version,
the paths it lives in, the producer that owns them, the consumers that use it, and its
schema and behavior. Every child brief lists the interfaces with their versions and
says that an interface changes only through the program: a child that finds one wrong
or insufficient must not change it or work around it, but stop and ask the user, who
raises a change request:
autocode program request-change program.json --workspace /path/to/project \
--interface store --by search --reason "load() does not promise an order" \
--proposal "load() returns the notes in the order they were added"
A change request needs an approved agreement. It is numbered CR-1, CR-2, ... and
records the interface version it was raised against. While it is open, every workstream
the agreement binds to the interface neither starts, resumes nor merges: its producer
and consumers, every workstream when the interface has no producer, and always the final
check. The program shows WAITING_CHANGE_REQUEST once nothing outranks it, even when
every workstream is already merged; other workstreams go on. You decide it:
- Accept: publish the interface anew in the manifest, with the next version and the
new definition, and approve that revision. Open requests on that interface become
accepted(accepted_in_revision,version,resolved_at). Every workstream bound to the interface that had started (the producer and consumers, the final check, and every workstream for an interface without a producer) becomesSTALE, and is planned, approved and checked again against the new version; a consumer re-checked with its producer restarts from the integration head that will hold the new version. - Reject:
program resolve-change, which records your reason (rejected, withresolutionandresolved_at). - Withdraw: approve a revision that removes the interface. Its open requests become
withdrawn(withdrawn_in_revision,resolved_at).
autocode program resolve-change program.json --workspace /path/to/project \
--request CR-1 --reject --reason "order is not part of the interface"
Only a person accepts an interface change, by approving the revision; no model does.
Quiet changes are refused twice. A revision that changes an interface's definition
without a new version is not a revision of the agreement. A delivery that touches an
interface's paths pauses at PAUSED_INTERFACE_CHANGE with nothing merged when the
workstream is not the interface's producer, when the producer edits a version it has
already delivered, or when the final check edits an interface that has no producer. The
program state records each delivered version (interfaces: version, commit and
by, the producer that delivered it), and keeps that record after the interface is
removed.
Walking skeleton and cumulative checks¶
Exactly one workstream is the walking skeleton. Every other workstream, apart from a
skeleton_exempt content workstream, is held until the skeleton is merged and its
cumulative checks pass on the integration branch (skeleton in the summary). The
skeleton's brief lists its selected journeys' steps (and what a simulated one does not prove)
and asks for runnable checks that prove the journey end to end; every other brief,
apart from a skeleton_exempt workstream's, says to extend the skeleton, not rebuild
or bypass it.
Each merge, the program re-runs the cumulative check set:
- the program
checks; - for every merged workstream and the one being merged, its manifest
checksand the checks its own run replayed and passed (evidence.check_replayin its status view), with that run's worktree path rewritten to the integration worktree.
The checks run in a scratch copy of the integration worktree, never in it, each limited
to --check-timeout seconds (default 900). Receipts are kept under
.autocode/programs/<key>/verify/NNN-<workstream>/, numbered by a counter over every
verification of the program; the state file keeps the latest 50 (verifications).
A hand-resolved conflict with later uncommitted child work is checked first against
the frozen conflict-time checks. Until the later delivery lands, those checks guard
every other merge; the later delivery then uses its own current checks. If the hand
resolution contains the conflict-time tip but lacks later branch commits,
PAUSED_MERGE_CONFLICT names their exact commit ids (#666). Merge those commits by
hand and rerun; the controller
does not automatically merge that late committed work or count it as delivered.
- Pass: the workstream is
MERGED, withverification(verdict, head, receipts) and the pin it was merged under. A skeleton is recorded inskeleton; a producer's interface version is recorded as delivered. - Fail: the program undoes its own merge of a code workstream (
git reset --hardto the head before the merge) and pauses atPAUSED_INTEGRATION_CHECK. The workstream staysCOMPLETEwithintegration_check(verdict, heads, receipts, and the message naming the failing command and who left it: the merging workstream, an earlier one, or the program). An earlier workstream's check can fail because the merge broke what it checks, or because it held only for the product before the merge (a live skeleton checked thatexportwas an unknown command; the export workstream then made it valid). In the second case nothing the merging workstream does can pass it: revise the agreement so the check's workstream is re-checked, which retires its run and replaces its checks. Two things are not undone: the final check's own commits on the integration branch, and a conflict resolution you merged by hand (the message says your merge is still on the integration branch: undo or repair it there). "The merge was undone" appears only when the program reset its own merge. Rerunning with the same workstream branch, integration head and check set repeats the pause without merging again: follow up the workstream's run, or change the checks through an approved revision. - No checks at all: a skeleton is not accepted as merged
(
PAUSED_SKELETON_UNVERIFIED; the program undoes its own merge of it), and nothing else starts. A derived manifest has no program checks, so the skeleton then depends on the checks its own run replays; add program checks before approving to be sure.
Checks are commands you approved in the agreement or that a child run replayed. They run with your permissions in a scratch copy; that is not a sandbox.
Final check: journeys¶
The integration workstream is the final check. It starts only once every other non-deployment workstream (including content) is merged, on the integration branch itself. It inherits, by id, every journey and every requirement a workstream other than a deployment workstream lists (see Inherited requirements), and its brief lists each journey's steps; for a simulated journey, the brief asks its evidence to say what the journey does not prove.
Each new run of the final check is proven against the integration head it starts from:
before it starts, the program writes that head as the base_commit of the integration
worktree's .autocode/task-workspace.json, which the run reads once
(bug note). The program's own delivery checks
keep the head the first run on its current record started from; a new integration
worktree record renews it, including isolation of a retired final check at the accepted
frontier. Without that replacement, they still cover commits made on the branch since.
Since the merged product already delivers the criteria the final check inherits,
its brief asks the plan to mark those guard:, naming
the tests the merged workstreams already have, and test: only for an integration defect
it repairs (named test proof).
When its run completes, the program reads each journey's result from the run's status
view (evidence.acceptance, by journey id). If any journey is not verified, the final
check is not committed or merged: the program pauses at PAUSED_JOURNEY_UNVERIFIED and
the workstream stays COMPLETE. Follow up its run:
autocode --workspace INTEGRATION_WORKTREE --run-dir RUN --follow-up "Verify journey J1 ..."
The follow-up reopens the run, and its next completion is checked again. Once every journey is verified, its changes are committed on the integration branch, the cumulative checks run, and it is merged.
The summary's journeys list each journey by id and name with its steps, its status
(pending until the final check merges, then verified), the run that verified it,
its evidence and, for a simulated journey, does_not_prove. The program is COMPLETE
only when every workstream is merged, every journey verified and no change request
open; final_check then names the final-check workstream, the journeys by name, and
under not_proven what each simulated journey does not prove. The program reports
that statement from the agreement; it does not check what the child's evidence says.
What each child sees¶
The composed brief contains, in order:
PROGRAM WORKSTREAM <id> (<kind>)and the program name; for a re-check, theRE-CHECK:line with the stale reason, which asks for a validation-only task where the files already conform and, when the run starts in a fresh worktree from the integration head, forguard:marks on criteria the branch already satisfies (a run retired in place is proven against its worktree's original commit, without its earlier work);- the program outcome and the approved agreement revision;
- the shared constraints, permission boundaries and technical approach, and the whole product's end-to-end flow and deliverables, labelled as context: later workstreams build the rest, and a workstream's own flow is the part its objective delivers;
- for a derived manifest, the complete parent contract, with the instruction to preserve its requirements, exclusions, permission boundaries and human-review obligations; approving the parent approves neither the child plan nor any human-review obligation in it;
- the shared interfaces with version, producer, consumers, paths, schema and behavior, and the instruction to ask the user for any interface change;
- the workstreams already merged on the integration branch when the brief is composed (every merged workstream, not only its prerequisites), each with its brief's first line;
- for the skeleton, that it is built and verified first and must leave runnable checks
for its own part of each journey (later workstreams add the other steps, and the final
check verifies each whole journey), with each journey's steps (and what a simulated
journey does not prove); for every
other workstream except a
skeleton_exemptone, that it extends the verified skeleton; - for every code workstream, that its checks are re-run after every later merge, so they check only what the finished product keeps, never that something a later workstream adds is missing, unknown or refused;
- the workstream's own objective and its acceptance criteria, with their full definitions when the agreement has requirements; the final check's list also holds every requirement it inherits, whether or not its own row lists it;
- for the final check, each user journey's steps (and what a simulated journey does not prove);
- the inherited requirement ids it must keep; for the final check, that the merged
product already delivers them, so its plan marks them
guard:andtest:only for an integration defect it repairs; - the exact paths it owns and the paths owned by others (the final check's cross-component repair exception instead), and that it must not deploy or reach external systems (deployment: only with permission in its own approved plan) and must not merge branches.
The brief is composed when the workstream's run starts and saved as
.autocode/programs/<key>/<workstream>/brief.md. Resuming the run leaves that file
alone, so it is always the brief that started the current run; a fresh run (a re-check,
or a retry without a run) writes it anew.
Ownership is checked against the actual diff before a non-integration branch is committed/merged or an existing conflict resolution is adopted. This is an integration gate, not a filesystem sandbox: it cannot prevent a child from writing an unowned path during execution. The integration workstream has an explicit cross-component repair exception and may commit its own tracked-file repairs; it is not rejected merely because those repairs make its worktree dirty. Its approved scope still applies, and it may not change a shared interface.
Boundaries¶
- No automatic merge into
main/master. The integration branch is yours to review. - No conflict resolution by the tool. A conflict pauses with both branches preserved.
- No deployment without
--authorize-deployment, and even then the deployment workstream is a normal reviewed run that must not reach external systems unless its own approved plan says so. - Approving the agreement approves no child plan; each workstream asks for its own exact approval. No model approves an agreement, a revision or an interface change.
- The child's status view (
autocode --status, viaautocode_taskrun) is the only source of a workstream's status and plan; the program never reads a child'sstate.json. An exit code, elapsed time or a Builder's report never marks a workstream complete. STALEnever reverts merged code, and a failing cumulative check undoes only the program's own merge of a code workstream. The only other merge the program takes back is one an interrupted pass made and never verified.- Evidence stays per run: each child run's validation and completion records remain in its own run directory; the integration workstream is where the whole flow is independently validated on the merged code.
- Cumulative checks are not a sandbox, and ownership is checked on the diff, not while the child runs.
--max-parallellimits concurrent workstreams, not total model spend. Scheduling re-evaluates readiness after each batch finishes. There is no program-wide budget, cancellation/eviction API or automatic worktree cleanup in this version.- A single job never sees any of this: an ordinary run writes nothing under
.autocode/programsand creates no program branch or worktree.
Testing: .venv/bin/python -m unittest tests.test_program tests.test_program_children
tests.test_program_agreement tests.test_program_agreement_runs covers manifest and
agreement rules, derivation from an approved contract, approval by exact token,
revisions and STALE, inherited requirements, change requests and quiet interface
changes, the walking skeleton, cumulative checks and undone merges, journeys, wave
order, worktree bases, merges, conflict pause and manual resolution, ownership
enforcement, tracked integration repairs, explicit retries, recovery from an
interrupted checkpoint or an unverified merge, deployment gates on start/resume, the
mapping from a child's status view to the workstream status, and a real CLI first wave
with the fake Codex provider.
tests.test_taskrun shows that an ordinary job creates no program files, and
tests.test_live_trial covers live_trial.py's program mode.
End to end, the program-notes-cli scenario drives program plan, derive, show,
approve and run with real child runs under the scripted model: a walking skeleton,
two parallel workstreams, an accepted interface change request that re-checks the
producer and both consumers, and the final check by journey
(scenarios/README.md, Programs):
.venv/bin/python scenarios/run.py run program-notes-cli --fake
program-adaptive-learning adds shared exact hint-rule inheritance in engine and
backend, named simulated journeys, SQLite persistence across separate processes,
interface reapproval and an unchanged-source implementation re-check with fresh review:
.venv/bin/python scenarios/run.py run program-adaptive-learning --fake
.venv/bin/python examples/adaptive-learning/run.py
The example runs the reference product, not AutoCode. Hint-assisted and independent answers have different recorded outcomes; these simulations do not establish real learning efficacy. These commands describe coverage, not a current test or live pass.
The PROGRAM-01 scenario in scenarios provides the end-to-end oracle
for a live trial (--mode program); a live profile stops there at the agreement for a
person. Any trial also stops at the first program-level PAUSED_* status, without
rerunning program run or serving a child's gate, and reports it as a pause for a
person rather than a stage-budget error.
See also: Task lanes · Task-run interface · Execution · Workflow