System reference
Configuration
Every setting the coding planner reads, where it lives, who writes it, its default and which program reads it: manifest keys, bead metadata, environment variables and labels.
This section lists every setting the coding planner reads and where each one lives. Without it, the only way to answer "where is X configured?" is to read the scripts, and a value written where no reader looks is silently ignored: the run carries on with the default and nothing reports the mistake. The settings live in seven homes, each owned by a different file and changed by a different hand, so the list is one table split by home, with a short account of each home before its rows.
Every row has the same six columns. Setting is the key, variable or label. Home names the file that owns the setting. Scope says how much of the system one value governs. Written by is the verb or person that sets it. Default is what happens when it is unset; a dash means there is no default. Read by names the programs that act on it. Script names without a directory are in .claude/scripts/, hook names are in .claude/hooks/, and command files are in .claude/commands/coding-planner/.
The manifest
The manifest, .claude/pipelines/coding-pipeline.json, is the pipeline's policy: which steps exist, in what order, what each step dispatches and what must be proved before a node may leave it. One value governs every campaign run on this pipeline, because no script carries a step name of its own. A person changes it by editing and committing the file. An unattended session cannot, because guard-enforcement-surface.ts denies writes under .claude/pipelines/ in a dispatched session. Keys under steps.definitions are set per step, written below as <step>.<key>.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
pipeline (coding-planner) |
coding-pipeline.json |
The manifest's name; pipeline.name on a root records it |
A person, by commit | — : --pipeline defaults to coding-planner, and a name no manifest declares escalates |
pipeline-transition.sh, pipeline-driver.sh (find the manifest file by this name) |
steps.routes (root, compound, leaf) |
coding-pipeline.json |
Every node; a step's successor is its neighbour in its route | A person, by commit | — | pipeline-transition.sh, pipeline-driver.sh, bd-pipeline-guard.ts (valid step names) |
<step>.command |
coding-pipeline.json |
One step, on every node that reaches it | A person, by commit | null: nothing is dispatched, as on delegate |
pipeline-driver.sh, which hands the path to pipeline-executor.sh |
<step>.gate |
coding-pipeline.json |
One step | A person, by commit | null: the step's floor settles it |
pipeline-driver.sh |
<step>.verify (the floors) |
coding-pipeline.json |
One step | A person, by commit | []: no evidence check, so the driver never completes the step by itself |
pipeline-transition.sh (complete, integrate and deliver run them), pipeline-driver.sh |
<step>.relaxable |
coding-pipeline.json |
One step | A person, by commit | false: an autonomy level naming the step escalates |
pipeline-driver.sh, pipeline-transition.sh |
<step>.relax_requires |
coding-pipeline.json |
One step (integrate: integration-branch) |
A person, by commit | Absent, null or "": no extra condition. The exact string integration-branch is the only value the scripts know; any other value, read by JSON type and exact text (so false, numbers and strings that differ only by whitespace count), never relaxes the step and never opens it on silence: the driver escalates every node under a level that names it and every node at the step itself, and complete refuses a dispatched session or an --on-silence caller |
pipeline-driver.sh, pipeline-transition.sh |
<step>.resume |
coding-pipeline.json |
One step | A person, by commit | — : a dispatchable step without one escalates | pipeline-driver.sh, which passes it to pipeline-executor.sh |
<step>.waivable.label |
coding-pipeline.json |
generate-design (design:waived), create-prp (prp:waived) |
A person, by commit | Absent: waive refuses the step |
pipeline-transition.sh (waive) |
<step>.on_complete |
coding-pipeline.json |
One step | A person, by commit | [], which every step declares |
pipeline-transition.sh (complete) |
review.fix_command |
coding-pipeline.json |
The review step | A person, by commit | — : a fix-owed refusal on a step without one escalates | pipeline-driver.sh |
review.fix_resume |
coding-pipeline.json |
The review step's fix dispatch | A person, by commit | — | pipeline-driver.sh |
review.fix_rounds |
coding-pipeline.json |
The review step; the cap on unattended fix rounds (1) | A person, by commit | — : a fix_command without a numeric cap escalates |
pipeline-driver.sh |
autonomy.levels |
coding-pipeline.json |
Every node; names the human gates each level relaxes | A person, by commit | — : a level must name a row here | pipeline-transition.sh (set-mode, init-task validate the name), pipeline-driver.sh |
autonomy.licences |
coding-pipeline.json |
Every node; dispatch-fix for wave and deep |
A person, by commit | With no table, no level holds a licence; a level missing from the table escalates | pipeline-driver.sh |
discovery.max_entries_per_tree |
coding-pipeline.json |
Every tree (a pipeline.discovery_root); how many beads discover may enter for it (3) |
A person, by commit | — : discover escalates |
pipeline-transition.sh (discover) |
autonomy.silence |
coding-pipeline.json |
Every node; seconds a held gate's proposal may go unanswered before the driver advances it (hitl 14400, wave and deep 7200, hold null) |
A person, by commit | With no table, nothing advances on silence; a level missing from the table, or a value other than a non-negative number or null, escalates | pipeline-driver.sh, pipeline-transition.sh (complete) |
entry.entry_stage |
coding-pipeline.json |
Every campaign entering through init (create-prp) |
A person, by commit | The first step on the root route | pipeline-transition.sh (init) |
entry.requires_artifact, entry.verify |
coding-pipeline.json |
Every campaign entering through init |
A person, by commit | false: init escalates; [] |
pipeline-transition.sh (init) |
state_keys |
coding-pipeline.json |
The schema of every pipeline.* key |
A person, by commit | — | No script; it is the authoritative list pipeline-driver.sh points to |
<step>.artifact.glob, <step>.enforced, <step>.display, waivable.requires_ruling, display_name, version, state_source, entry.pre_pipeline_command |
coding-pipeline.json |
Declarative | A person, by commit | — | No script in this repository; waive requires --ruling whatever requires_ruling says |
Bead metadata
Bead metadata is the key-value store each issue in the tracker carries; an issue is a bead. The pipeline keeps all its state there under the reserved pipeline. prefix, and pipeline-transition.sh is its only writer: bd-pipeline-guard.ts denies a direct bd update --set-metadata pipeline.* unless a person prefixes the command with VOX_PIPELINE_OVERRIDE=1. Most of these keys record where a node is, but some are per-campaign facts that change behaviour, such as the integration branch, the delivery mode, the dial and the integrate floor's suite command. The authoritative list is the manifest's state_keys block, which names each key's writing verb; the header of pipeline-transition.sh gives every verb's contract. A key scoped to the campaign root is read only there, by an ancestor walk, so a copy written on any other bead is never read. The landing floor also stamps the bead's own external_ref field with the pull request it proved; that is a tracker field, not metadata.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
pipeline.name |
Bead metadata (state_keys) |
Campaign or task root; carrying it is rootness | init, init-task, adopt --as-root |
Absent: not a root | pipeline-transition.sh, pipeline-driver.sh, pipeline-executor.sh |
pipeline.stage |
Bead metadata (state_keys) |
Every node; the step it is at | init, init-task, complete, waive, set-step |
Unset: the pipeline does not manage the bead | pipeline-transition.sh, pipeline-driver.sh, bd-pipeline-guard.ts |
pipeline.entry_artifact |
Bead metadata (state_keys) |
Root | init (the brief), init-task --prp, adopt --as-root --prp |
Absent | pipeline-transition.sh, pipeline-executor.sh (the run's authority document) |
pipeline.init_operation |
Bead metadata (state_keys) |
Root | init, init-task |
Absent | pipeline-transition.sh (retry idempotency) |
pipeline.entry_route |
Bead metadata (state_keys) |
Task root (task) |
init-task |
Absent: grill campaign behaviour | pipeline-transition.sh, pipeline-driver.sh |
pipeline.adopted |
Bead metadata (state_keys) |
A tree root and its descendants | adopt (a person) |
Absent: the managed-tree checks apply | pipeline-transition.sh |
pipeline.artifact |
Bead metadata (state_keys) |
Node; the pending proposal | propose; cleared by complete and waive |
Absent: no work done, no gate pending | pipeline-transition.sh, pipeline-driver.sh |
pipeline.proposed |
Bead metadata (state_keys) |
Node; step=<s> date=<iso>, when the pending proposal was recorded |
propose --artifact |
Absent: the proposal predates the key and never advances on silence | pipeline-transition.sh (complete), pipeline-driver.sh (the silence window) |
pipeline.scenarios |
Bead metadata (state_keys) |
Node; root holds the whole set | set-scenarios |
Absent | pipeline-transition.sh (coverage, floors), pipeline-driver.sh |
pipeline.fanout_verified |
Bead metadata (state_keys) |
Container; presence makes the node a container | birth-facts |
Absent: not a container | pipeline-transition.sh (kind), pipeline-driver.sh |
pipeline.fanout_children |
Bead metadata (state_keys) |
Container | birth-facts |
Absent: children_terminal skips the unchanged-set clause |
pipeline-transition.sh, pipeline-driver.sh |
pipeline.integrated |
Bead metadata (state_keys) |
Container | integrate |
Absent | pipeline-transition.sh, pipeline-driver.sh |
pipeline.reviewed |
Bead metadata (state_keys) |
Leaf | complete --step review (the review_verdicts floor) |
Absent | pipeline-transition.sh (deliver reads the reviewed commit) |
pipeline.suite_cmd |
Bead metadata (state_keys) |
Campaign root | set-config |
Unset: integrate escalates; init warns |
pipeline-transition.sh (integrated_acceptance) |
pipeline.test_paths |
Bead metadata (state_keys) |
Campaign root | set-config |
Unset: integrate escalates; init warns |
pipeline-transition.sh (integrated_acceptance) |
pipeline.integration_branch |
Bead metadata (state_keys) |
Campaign root; presence is deep mode | set-mode --integration-branch |
Unset: the target is main (wave mode) |
pipeline-transition.sh (target), so the executor's worktree cut, Gate B's base and Gate C |
pipeline.delivery |
Bead metadata (state_keys) |
Campaign root | set-mode --delivery, init-task |
Unset: pr |
pipeline-transition.sh (delivery), pipeline-executor.sh |
pipeline.autonomy |
Bead metadata (state_keys) |
Any managed node, nearest ancestor wins | set-mode --autonomy, init-task |
Unset: every human gate holds | pipeline-transition.sh (autonomy, tree-facts), pipeline-driver.sh, pipeline-executor.sh |
pipeline.discovered_from |
Bead metadata (state_keys) |
A bead discover entered; the finder |
discover |
Absent: not entered by discover |
Nothing yet; the record a run's discovered work is read from |
pipeline.discovery_root |
Bead metadata (state_keys) |
A bead discover entered; the tree its entry counts against |
discover |
Absent | pipeline-transition.sh (discover counts it against the cap; queue lists a run's discovered beads by it) |
pipeline.queue_order |
Bead metadata (state_keys) |
An epic; the order a run takes its descendants in | set-queue |
Absent: the queue has no recorded order | pipeline-transition.sh (queue) |
pipeline.lease |
Bead metadata (state_keys) |
Node; the claim record holder=<token> since=<iso>, which never expires |
claim, release (the holder), release --clear --reason (a person) |
Absent: unclaimed | pipeline-transition.sh, pipeline-driver.sh |
pipeline.exit_status |
Bead metadata (state_keys) |
Node | record-exit |
Absent | pipeline-driver.sh (including owed= and axes=) |
pipeline.attempts |
Bead metadata (state_keys) |
Node, per step | record-exit |
Absent: no failed attempts | pipeline-transition.sh, pipeline-driver.sh |
pipeline.dead_letter |
Bead metadata (state_keys) |
Node | record-exit, at PIPELINE_MAX_ATTEMPTS |
Absent | pipeline-driver.sh, which parks the node until a person clears it |
pipeline.fix_rounds |
Bead metadata (state_keys) |
Node, per step | record-exit --fix-round |
Absent: no rounds spent | pipeline-driver.sh |
pipeline.driver_last |
Bead metadata (state_keys) |
The scope root of a driver pass | record-run |
Absent | pipeline-driver.sh |
pipeline.delivered |
Bead metadata (state_keys) |
Leaf | deliver |
Absent | pipeline-driver.sh |
grill_brief |
Bead metadata (pipeline-transition.sh) |
Campaign | handoff |
Absent | pipeline-transition.sh (init matches the brief) |
grill.session_id |
Bead metadata (pipeline-transition.sh) |
Campaign | handoff |
Absent | pipeline-transition.sh (init matches the producing session) |
grill.autonomy |
Bead metadata (pipeline-transition.sh) |
Campaign; the staged dial | handoff; cleared by set-mode --from-staged |
Absent | pipeline-transition.sh; entry-context refuses while it sits unapplied |
grill.delivery |
Bead metadata (pipeline-transition.sh) |
Campaign; the staged delivery mode | handoff; cleared by set-mode --from-staged |
Absent | pipeline-transition.sh; entry-context refuses while it sits unapplied |
grill.init_operation |
Bead metadata (pipeline-transition.sh) |
Campaign, only while init is in flight |
init, which clears it on success |
Absent | pipeline-transition.sh (init resumes an interrupted entry) |
grill.init_artifact |
Bead metadata (pipeline-transition.sh) |
Campaign, only while init is in flight |
init, which clears it on success |
Absent | pipeline-transition.sh (init resumes an interrupted entry) |
The project's own repository
A codebase run through the pipeline carries a few committed files that describe it. The main one is the project overlay, .vox/project.json, which declares the codebase's identity; the two others prepare a fresh worktree. One value governs every bead whose project:<id> label names the codebase. Where the codebase sits on a given machine is not here; that is the machine configuration below. The lab's machine-configuration notes describe the split.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
schema_version |
.vox/project.json |
One codebase | A person, by commit | — : must be 1 or the resolver refuses |
project-resolver.ts |
project_id |
.vox/project.json |
One codebase | A person, by commit | — : the resolver refuses a folder whose overlay is missing or names another id | project-resolver.ts, pipeline-executor.sh (a path segment of the worktree), guard-worktree-edits.ts (the overlay's presence marks a primary checkout as guarded; the id names the project in a denial) |
modules |
.vox/project.json |
One codebase | A person, by commit | {} |
Not the coding planner: vox/modules/beads/scripts/project-store.ts reads modules.beads.store |
.worktreeinclude |
The codebase's root, committed | Every new worktree of the codebase | A person, by commit | Absent: nothing is copied | pipeline-executor.sh (copies the listed gitignored files from the primary checkout) |
.vox/worktree-setup.sh |
The codebase, committed | Every new worktree of the codebase | A person, by commit | Absent: no setup runs | pipeline-executor.sh (runs it once per bead's worktree) |
The worktree registry
The worktree registry, .claude/worktrees.json in the VOX repository, records each project's shared dev worktree: one long-lived worktree, with secrets and dependencies in place, that a dispatched bead takes when it is free. A project missing from it gets a fresh worktree per bead, and nothing else changes: guard-worktree-edits.ts, the hook that refuses edits in a project's primary checkout, protects the primary of every repository carrying a project overlay except VOX's, listed here or not. Registration only changes where its denial points: to the shared dev worktree rather than to the bead worktrees.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
worktreesRoot |
.claude/worktrees.json |
Every registered project | A person, by commit | — | pipeline-executor.sh, guard-worktree-edits.ts |
projects[] (label, primary, worktree) |
.claude/worktrees.json |
One project | A person, by commit | Absent: no shared worktree; the edit guard still protects the primary through its overlay | pipeline-executor.sh, guard-worktree-edits.ts, bd-pipeline-guard.ts |
The machine configuration
The machine configuration, ~/.vox/config.json, records where things are on one machine. It is never committed, so each machine keeps its own. Its normative definition is readUserConfig in project-resolver.ts, and the lab's machine-configuration notes explain each section. The Codex CLI's own per-machine file is listed here too, because when no model is passed the review and spec harnesses run on the default it names.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
schema_version |
~/.vox/config.json |
One machine | A person | — : must be 1 |
project-resolver.ts |
beads_stores.<name> (repo_root, beads_dir, database, project_id) |
~/.vox/config.json |
One machine | A person, copied from bd context --json |
— : the resolver needs exactly one store and refuses on any mismatch | project-resolver.ts, vox/modules/beads/scripts/project-store.ts |
project_roots.projects |
~/.vox/config.json |
One machine | A person | <home>/CODE/projects |
project-resolver.ts |
project_roots.worktrees |
~/.vox/config.json |
One machine | A person | <home>/CODE/vox-worktrees |
pipeline-executor.sh (each bead's worktree is <worktrees>/<project-id>/<bead-id>); project-resolver.ts validates it; guard-worktree-edits.ts (names that location in a denial) |
projects.<id>.root |
~/.vox/config.json |
One codebase on one machine | A person | Absent: the codebase is at <project_roots.projects>/<id> |
project-resolver.ts |
model |
~/.codex/config.toml (Codex CLI) |
One machine | A person | Codex's built-in default | The Codex CLI when review/vox-review.sh or spec/vox-spec.sh passes no model; both scripts read it back to label the artifact |
Environment variables
An environment variable is a value the shell hands to every program it starts. Most of these variables are documented nowhere but in the scripts that read them, so the Home column names the script that applies the default. Some are set for every Claude Code session by the env block of .claude/settings.json, which pipeline-executor.sh loads explicitly into each dispatched session (--settings $VOX_ROOT/.claude/settings.json); some the executor sets itself for the session it spawns; the rest come from the shell of whoever starts a run.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
VOX_ROOT |
Environment; defaults in each reader | Every script and hook | .claude/settings.json env |
an absolute path to the lab's own checkout, hard-coded in the transition script, driver, executor, bd-pipeline-guard.ts and guard-enforcement-surface.ts; $HOME/CODE/VOX in review/vox-review.sh, spec/vox-spec.sh and guard-worktree-edits.ts |
pipeline-transition.sh, pipeline-driver.sh, pipeline-executor.sh, review/vox-review.sh, spec/vox-spec.sh, bd-pipeline-guard.ts, guard-enforcement-surface.ts, guard-worktree-edits.ts |
VOX_DIR |
.claude/settings.json env |
Every session | .claude/settings.json env |
— : if unset, the guard hooks' registrations (${VOX_DIR}/hooks/...) point nowhere and no guard runs |
The shell that runs each registered hook command |
BEADS_DIR |
.claude/settings.json env |
Every bd call in a session |
.claude/settings.json env |
bd's own store discovery |
bd; project-resolver.ts strips it so a bead's labels come only from the configured store |
VOX_REVIEW_MODEL |
Environment; defaulted in review/vox-review.sh |
Every Gate B review run under it, both rounds | Today: "gpt-6-astra" in .claude/settings.json env, a stopgap (below) |
Unset: round 2 takes round 1's recorded model; round 1 takes the Codex default | review/vox-review.sh, which outranks the round-2 pin with it |
VOX_REVIEW_MAX_KB |
Environment; defaulted in review/vox-review.sh |
One review run | The caller's shell | 256 |
review/vox-review.sh |
VOX_REVIEW_INLINE_KB |
Environment; defaulted in review/vox-review.sh |
One review run | The caller's shell | 12: a context document larger than this is named by path, not pasted |
review/vox-review.sh |
VOX_REVIEW_SILENCE_MIN |
Environment; defaulted in review/vox-review.sh |
One review run | The caller's shell | 10: minutes without a new byte in the review's .partial or session log, after which the harness stops Codex, keeps the partial and ends no_review with reason=silence_<n>min (exit 3); fractions accepted |
review/vox-review.sh |
VOX_REVIEW_MAX_MIN |
Environment; defaulted in review/vox-review.sh |
One review run | The caller's shell | 45: minutes after which any review still running is stopped the same way, reason=ceiling_<n>min; fractions accepted |
review/vox-review.sh |
VOX_SPEC_MODEL |
Environment; defaulted in spec/vox-spec.sh |
One Codex spec run | The caller's shell | Unset: the Codex default | spec/vox-spec.sh |
VOX_SPEC_MAX_KB |
Environment; defaulted in spec/vox-spec.sh |
One Codex spec run | The caller's shell | 256 |
spec/vox-spec.sh |
VOX_SPEC_CONTAINER_CAP |
Environment; defaulted in spec/vox-spec.sh |
One Codex spec run | The caller's shell | 60000 characters of the container spec |
spec/vox-spec.sh |
VOX_SPEC_PRP_CAP |
Environment; defaulted in spec/vox-spec.sh |
One Codex spec run | The caller's shell | 60000 characters of the PRP |
spec/vox-spec.sh |
CODEX_HOME |
Environment; defaulted in review/vox-review.sh, spec/vox-spec.sh |
One machine | The caller's shell | $HOME/.codex |
review/vox-review.sh, spec/vox-spec.sh (where to read the Codex default model) |
PIPELINE_RUN_MAX_PASSES |
Environment; defaulted in pipeline-run.sh |
One headless run | The caller's shell; --max-passes overrides |
16 |
pipeline-run.sh |
PIPELINE_RUN_MAX_BEADS |
Environment; defaulted in pipeline-run.sh |
One headless run with --queue |
The caller's shell; --max-beads overrides |
16 |
pipeline-run.sh |
PIPELINE_RUN_DRIVER, PIPELINE_RUN_TRANSITION |
Environment; read in pipeline-run.sh |
One headless run | A test's stubs | The driver and transition script beside pipeline-run.sh |
pipeline-run.sh |
PIPELINE_DRIVER_DISPATCH |
Environment; set in pipeline-run.sh |
One driver invocation | pipeline-run.sh (the executor beside it), unless already set |
Unset: dispatch actions are reported, not performed | pipeline-driver.sh (set means an unattended host: a bead whose spec's file_scope lands on the enforcement surface rests attended-host instead of being dispatched) |
PIPELINE_DRIVER_MAX_ACTIONS |
Environment; defaulted in pipeline-driver.sh |
One driver pass | The caller's shell; --max-actions overrides |
0: no cap |
pipeline-driver.sh |
PIPELINE_DRIVER_MAX_CYCLES |
Environment; defaulted in pipeline-driver.sh |
One driver pass | The caller's shell; --max-cycles overrides |
8 |
pipeline-driver.sh |
PIPELINE_ATTEMPT_RESULT |
Environment; set in pipeline-driver.sh |
One dispatch | pipeline-driver.sh, a fresh file per dispatch |
<transcript>.result beside the executor's log |
pipeline-executor.sh, the dispatched session, review/vox-review-blocked.sh, spec/vox-spec-blocked.sh |
PIPELINE_EXECUTOR_MODEL |
Environment; defaulted in pipeline-executor.sh |
Every dispatched session | The caller's shell | opus |
pipeline-executor.sh (--model) |
PIPELINE_EXECUTOR_CLAUDE |
Environment; defaulted in pipeline-executor.sh |
Every dispatched session | The caller's shell, or a test | claude |
pipeline-executor.sh |
PIPELINE_EXECUTOR_LOG_DIR |
Environment; defaulted in pipeline-executor.sh |
Every dispatched session | The caller's shell | /tmp |
pipeline-executor.sh |
PIPELINE_EXECUTOR_RESOLVER |
Environment; defaulted in pipeline-executor.sh |
Every dispatch | The caller's shell, or a test | project-resolver.ts beside the executor |
pipeline-executor.sh |
PIPELINE_MAX_ATTEMPTS |
Environment; defaulted in pipeline-transition.sh |
Every node | The caller's shell | 3 failed attempts, then dead-letter |
pipeline-transition.sh (record-exit) |
PIPELINE_TRANSITION_LOCKS |
Environment; defaulted in pipeline-transition.sh |
One machine | The caller's shell, or a test | ${TMPDIR:-/tmp}/vox-pipeline-transition-locks |
pipeline-transition.sh (the claim mutex) |
VOX_USER_CONFIG |
Environment; defaulted in project-resolver.ts |
One machine | The caller's shell, or a test | ~/.vox/config.json |
project-resolver.ts, pipeline-executor.sh, verify-machine.sh, guard-worktree-edits.ts |
VOX_BD_BIN |
Environment; defaulted in project-resolver.ts |
One machine | The caller's shell, or a test | bd |
project-resolver.ts, verify-machine.sh |
VOX_PROJECT_ROOT |
Environment; set in pipeline-executor.sh |
One dispatched session | pipeline-executor.sh (the bead's worktree) |
— | The dispatched session (afk-contract.md), .vox/worktree-setup.sh, guard-enforcement-surface.ts (expands it when judging a path) |
VOX_PRIMARY_ROOT |
Environment; set in pipeline-executor.sh |
One worktree setup | pipeline-executor.sh |
— | .vox/worktree-setup.sh |
VOX_BEAD |
Environment; set in pipeline-executor.sh |
One worktree setup | pipeline-executor.sh |
— | .vox/worktree-setup.sh |
VOX_AFK |
Environment; set in pipeline-executor.sh |
One dispatched session | pipeline-executor.sh (1) |
Unset: treated as attended | guard-enforcement-surface.ts (denies, rather than audits, surface writes); pipeline-driver.sh (1 means an unattended host, as for PIPELINE_DRIVER_DISPATCH); pipeline-transition.sh (complete and integrate refuse a human gate the dial holds; release --clear refuses) |
VOX_ENFORCEMENT_OVERRIDE |
Environment; read in guard-enforcement-surface.ts |
One session | A person launching a maintenance session; pipeline-executor.sh blanks it |
Unset: the surface guard runs | guard-enforcement-surface.ts |
VOX_SURFACE_AUDIT_LOG |
Environment; defaulted in guard-enforcement-surface.ts |
One session | The caller's shell, or a test | $VOX_ROOT/.claude/logs/enforcement-surface-audit.log |
guard-enforcement-surface.ts |
VOX_PIPELINE_OVERRIDE |
A command prefix; matched in bd-pipeline-guard.ts |
One command | A person correcting bead state | Absent: the guard denies direct writes to pipeline.* |
bd-pipeline-guard.ts, which matches the text VOX_PIPELINE_OVERRIDE=1 in the command rather than reading the environment |
Other VOX_ names in .claude/scripts/ and .claude/hooks/ are not coding-planner settings and are left out: VOX_HOME belongs to setup.sh; VOX_BRIEFING_MODEL, VOX_SOURCE_APP and VOX_VOICE_SERVER_PORT belong to session hooks; VOX_BLOCK_STUB_* and VOX_SPEC_STUB_MODE are test stubs; and VOX_REVIEW_RESULT, VOX_SPEC_RESULT, VOX_REVIEW_BLOCKED and VOX_SPEC_BLOCKED are the prefixes of the harnesses' result lines, not variables.
The review model is a stopgap. The lab decided in October 2026 that Gate B reviews use the model gpt-6-astra: gpt-5.6-sol hung repeatedly, six times on one bead, and gpt-5.5 is refused for a Codex login through a ChatGPT account. To make dispatched sessions inherit the ruling, VOX_REVIEW_MODEL=gpt-6-astra was added to the env block of the primary checkout's .claude/settings.json, the copy the executor loads. It is a local edit there, not a committed setting, so another checkout or another machine reviews on its Codex default. Three things follow from where it sits. review.md names no model on purpose and calls VOX_REVIEW_MODEL an override "for a one-off comparison, not as a standing pin"; this entry is a standing pin, in a file nobody reading review.md would open. Because vox-review.sh ranks the variable above the model recorded in round 1's review, the entry also overrides the round-2 pin: a bead whose round 1 ran on another model is judged in round 2 by gpt-6-astra, a different judge that re-argues the whole change instead of adjudicating the fix, which is the failure the pin exists to prevent. And VOX_SPEC_MODEL is unset, so a spec:codex bead still drafts its spec on the Codex default; the ruling covers Gate B only.
Where this should live. The proposed home is the review step's definition in the manifest, as a key such as steps.definitions.review.model beside fix_rounds. The manifest is already the home of review policy, it is committed and travels to every machine, and an unattended session cannot change it. vox-review.sh reads nothing from the manifest, so the key needs wiring: the script, or the review command passing --model, would read it. A precedence that keeps the round-2 pin intact is --model, then VOX_REVIEW_MODEL, then round 1's recorded model for round 2, then the manifest's model, then the Codex default; the settings entry would then be removed, leaving the variable for one-off comparisons as review.md intends. The move runs against review.md's reason for naming no model: a pinned name once left a review path calling a model the account could not run. A manifest key is still a pinned name, so when the model is retired the review fails until someone edits that key, and review.md's paragraph would need rewriting to say where the name lives and why. Moving the setting and revising review.md are the owner's decision; this section records the proposal and changes neither.
Labels and agent definitions
A label is a tag on a bead, set with bd update --add-label. A few labels change what the pipeline does with the bead that carries them. The deterministic guard does not police labels, so apart from the two waiver labels the transition script writes and the execution: label the fan-out sets, nothing mechanical stops any session from setting one; that a person sets them is a convention. The agent definitions in .claude/agents/ pin the model and effort of the agents that author a step's artifacts. The fleet standard in afk-contract.md §9 requires spec, implement and review-orchestration agents to run the strongest implementation model at effort high and never inherit the session's model; these pins are where that holds when a step dispatches a named agent. The Gate B reviewer is not a local agent, and its model is the review model above.
| Setting | Home | Scope | Written by | Default | Read by |
|---|---|---|---|---|---|
spec:codex / spec:claude |
Bead label (pipeline-transition.sh spec-author) |
Node, nearest ancestor wins | A person | Neither: Claude authors the spec; both on one node refuses | pipeline-transition.sh (spec-author), generate-spec.md, afk-contract.md |
spec:skip |
Bead label (implement.md) |
Node | A person, before implement |
Absent: implement requires a spec |
implement.md (proceeds without a spec only on a person's in-session confirmation), afk-contract.md (an unattended run blocks instead) |
review:skip |
Bead label (pipeline-transition.sh review_verdicts) |
Leaf | A person | Absent: Gate B runs | pipeline-transition.sh (waives the floor and still records the reviewed commit), review/vox-review.sh (Evidence axis), implement.md, review.md, deliver.md |
prp:waived |
Bead label (coding-pipeline.json create-prp.waivable) |
Root | waive --ruling, init-task --waive-prp |
Absent: a PRP is required | pipeline-transition.sh |
design:waived |
Bead label (coding-pipeline.json generate-design.waivable) |
Root | waive --ruling |
Absent | pipeline-transition.sh |
execution:afk / execution:hitl |
Bead label (.claude/skills/to-issues/SKILL.md) |
Each fan-out child | The to-issues fan-out | — : birth-facts refuses a child without exactly one |
pipeline-transition.sh (birth-facts), implement.md (lists ready execution:afk slices); the driver ignores it, since the autonomy dial decides unattended advance |
project:<id> |
Bead label (project-resolver.ts) |
Node; not inherited from a parent | A person | — : the resolver refuses a bead with none or several | project-resolver.ts, so pipeline-executor.sh placement |
model: claude-opus-5-5, effort: high |
.claude/agents/architect.md |
Every dispatch of the architect agent |
A person, by commit | Absent: the agent inherits the session's model | The Claude Code harness, when generate-spec.md, create-prp.md or generate-design.md dispatches the architect |
model: opus, effort: high |
.claude/agents/engineer.md |
Every dispatch of the engineer agent |
A person, by commit | Absent: the agent inherits the session's model | The Claude Code harness, when implement.md dispatches the engineer, including the attended fix round |