Never edit this file directly. To make additions or modifications, edit the appropriate section in
./vstack.toml. Then runvstack refresh.
Orchestration
STOP — Required Setup
Load IN ORDER before anything else. Do not proceed if any fails.
- Load
github. - Load
worktree. - Load tracker: Linear issue → load
linear; GitHub issue → usegithubonly.
MODE SWITCH: Loading this skill puts you in orchestrator mode. Do not write code yourself. Delegate all implementation, review, and QA work to specialist sub-agents using the workflows in this skill.
If you are running in Claude Code: Always create a team before launching agents. Spawn and delegate to agents within the team context so they share state and can be messaged for re-delegation. When asking the user a question or presenting options, always use the
AskUserQuestiontool.SendMessageaccepts exactlyto,summary,message— extra fields (type,recipient,content,body) have caused duplicate delivery on idle wake-up.
If you are running in Codex: Under
approval_policy = never, the Codex CLI classifies shell CONTROL SYNTAX as approval-required no matter how harmless the inner commands are —for/whileloops, multi-command blocks (;- or newline-separated),VAR=x cmdenv-assignment prefixes,$(...)substitution (a literal backtick anywhere in the command counts, even inside a quoted search pattern — write the regex hex escape\x60instead), and redirection all rejected withapproval required by policy, but AskForApproval is set to Never. That error means the command shape was flagged, not access: do not retry the same shape and do not wait for approval (none can arrive) — rewrite as one simple command per tool call. Replace polling loops with the orch waiters.agents/skills/orch/scripts/ci-wait(CI status) and.agents/skills/orch/scripts/approval-wait(review approval) — orch scripts, nevergithub.shsubcommands; replace multi-item sweeps such as per-worktreegit statuswith a separate single command per item; derive values with helper scripts (git-context,workflow-state) instead of substitution; write files with harness file tools orapply_patch, never redirection. The classifier also rejects some porcelain verbs outright — a policy-rejected top-levelgit rebasehas a documented replacement: the worktree skill's guardedcreate <ID> --reuse/--restackpath withworktree restack continue|skip|abortcontrols, or, when that too is unavailable, the exact replay in worktreeSKILL.md§ Policy-blocked rebase (cherry-pick replay fallback) — never an improvised force-push. Authoring rules for generated workflow commands live in § Harness-Safe Shell.Spawn generated vstack agents with
agent_typeset to the actual generated agent name andfork_context: false. Reviewers returned bylist-review-agentsmust first spawn asagent_type=<reviewer-name>; dev agents selected fromagent:Xlabels must first spawn asagent_type=X. The spawn API'stask_nameschema accepts only lowercase letters, digits, and underscores ([a-z0-9_]) and rejects hyphenated names before launch (vstack#751), so spawn a canonical hyphenated agent name by translating hyphens to underscores in the runtimetask_nameonly (reviewer-arch→task_name=reviewer_arch;agent_typestill resolves the generated agent whose name is canonical). Attempt this translation before anyworkerfallback — atask_nameschema rejection is a naming mismatch, not a missing agent type. Useworkeronly for an intentional generic-worker fallback, when no matching generated agent exists, when the selected agent is deliberately generic, or after the generated-agent spawn is attempted and the Codex spawn API rejects or does not expose that generatedagent_type. In fallback, preserve the logical selected agent name in reports and workflow-state keys (child_sessions[agent],review_agent_ids[reviewer-name]); record the runtimeagent_type=workerand fallback reason separately in status and workflow state. Translated or not, the canonical hyphenated name is the identity everywhere orch records it — workflow-state keys, report artifacts, delegation records; the underscoretask_nameis a runtime detail recorded, like the worker fallback, in runtime metadata (review_agent_runtime_types[reviewer-name].task_name). Two-step pattern: (1) spawn the selected runtime agent with the<bootstrap_format>message, (2)send_inputaDELEGATION:prefixed message containing exactly the filled<delegation_format>content — nothing more.The Codex collaboration runtime also caps concurrent agent threads — MultiAgentV2's configurable default is 4 total, counting this primary session (
features.multi_agent_v2.max_concurrent_threads_per_sessionin~/.codex/config.toml; set the legacyagents.max_threadstoo, but never alone — MultiAgentV2 silently ignores it, so raising only that key changes nothing (openai/codex#33447, #33039) — and a running session keeps its old cap until restarted); a spawn beyond the effective cap fails withcollab spawn failed: agent thread limit reached. SetREVIEWER_SLOT_BUDGETinvstack.settings.toml[env]to the cap the machine config declares ("4"on a default config) so review workflows compute the available reviewer slots and run reviewers in bounded waves when the set does not fit — see Review Agent Lifecycle Management. If the budget is left at0(unlimited) and a reviewer spawn hits that error anyway, review workflows demote the cycle to bounded waves automatically — sized by the reviewers that did spawn — and recommend the observed budget (review-pr.md§ 2.2 persistent-mode thread-limit recovery).For Codex Desktop app handoff, invoke
workflows/handoff.mdwithharness=codex-app. Whenhandoffreceives multiple issues and the runtime exposes Codex app thread tools, default toharness=codex-appunless the user explicitly selected another harness. Before creating child threads, run the Codex app agent preflight to check whether tracked.codex/agents/*.tomlfiles are present in the saved project branch, because setup hooks run too late for subagent type discovery. If preflight reports a warning, present the exact message and continue only after explicit user acceptance of the risk that child sessions may fall back toworker; stop only on a preflightseverity=erroror if the user declines. Create one Codex app thread per issue withcodex_app.create_thread, target a worktree environment whosestartingStateistype="branch"withbranchNameset to the resolved base branch, start it with exactly$orch start [ISSUE_ID]or$orch start github [OWNER/REPO]#[N], and record the returned thread ID. Do not use aworking-treestarting state for orch app handoff unless the user explicitly requests a dirty local snapshot; that path can start the child before generated Codex agents are visible and forceworkerfallback. Do not silently create degraded child threads; the user must accept the warning first. If the runtime separates thread creation from prompting, callcodex_app.send_message_to_threadonce with that same start prompt. The Codex CLI does not expose these tools; do not emulate app handoff with terminal launch,codex debug app-server, rawcodex app-server, or manual app-thread instructions.
If you are running in OpenCode: The persistent identity of a spawned sub-agent is the
task_idreturned byfunctions.task. On first spawn, store thattask_idin workflow state (child_sessions[agent].agent_idfor dev/QA,review_agent_ids[reviewer-name]for reviewers). On re-delegation (fix cycles, re-review), callfunctions.task(task_id=<stored_id>)— never spawn a fresh task when a stored ID exists. Fresh spawn only if: no stored ID, one resume attempt fails, or the prior task is confirmed dead.
If you are running in Pi with
pi-agents-tmux: usesubagentfor delegation. The Pi dispatch model is one tool call per delegation — no separate "send bootstrap, then send delegation" two-step. The agent's bootstrap is its compiled system prompt (built from frontmatterdescription+ role + skills + the canonical<bootstrap_format>block) and is injected automatically as--append-system-promptwhen the child pi process starts. Thetaskargument tosubagentis the filled<delegation_format>content — nothing more. Do not prepend the bootstrap block to thetaskstring; that double-injects the role boundaries and confuses the child.Two flavors:
- Pane agents (
pane: truein agent frontmatter) live in a persistent tmux pane keyed by agent name. The extension reuses the existing pane on every redelegation — do not passforceSpawn: trueunless you genuinely need a fresh pane (it errors if a live pane already exists, and tells you to either drop the flag or/agents:stop <name>first). Store the returnedtaskIdand agent name in workflow state (child_sessions[agent].agent_idorreview_agent_ids[...]). ThetaskIdis returned in two places: the structuredtaskIdfield on the tool result and an inlineTask ID: <id>line in the assistant-visible content text — read whichever your harness exposes; you do not need a follow-upget_subagent_resultcall just to learn the id.- Bg agents (no
pane: true) are background one-shot processes. By default each call is ephemeral (no persisted session). For multi-step workflows where the samereviewer-*(or other bg agent) must retain conversation context across delegations, passsessionKey: "<workflow-scoped-stable-id>"(e.g.review-issue-PROJ-123). Sameagent + sessionKeyresumes the prior pi session; omit it for truly stateless calls. Bg agents complete by final assistant message captured bysubagent; do not instruct them to callcomplete_subagent. On re-delegation to a pane agent, usesteer_subagentonly for true mid-run correction from this same Pi parent session; its success output readsBridge: activeand shows the expected childsessionFileunder this session runtime. If the bridge target is unavailable, the tool queues an inbox fallback that is not mid-run steering and will be read only when the pane is idle. For idle follow-up work, queue a newsubagenttask to the same pane. Useget_subagent_resultonly as a recovery/status reader for missed or truncated pane completions; it does not affect ownership or delivery. If it returnsneeds_completion, the child finished a turn without the durablecomplete_subagentrecord — do not count it as a return; use the verbose diagnostics/outbox path to send one recovery instruction asking the same pane to callcomplete_subagentfor the storedtaskId. Treat Pi custom completion notifications as agent returns only when the task ID matches stored workflow state; repeated display is not a second return.
Research issues (
researchlabel) are executed byagent:researcher, not by external human sessions. The researcher may run Exa deep research and write findings docs/raw metadata, but must not modify production code. In Pi, treat persistentresearcherpanes like other project agents: key by agent name, store the returnedtaskId, and require exactly one completion message afterfindings.mdexists.
Commands
When invoked with <command> [args], route to the corresponding workflow.
Session
| Command | Arguments | Workflow | Notes |
|---|---|---|---|
start |
[ISSUE_ID] | github OWNER/REPO#N |
workflows/start.md or workflows/start-worktree.md |
Context-aware routing |
start new |
`linear | github ...` | workflows/start-new.md |
handoff |
`linear | github ...` | workflows/handoff.md |
plan-issues |
`PLAN_PATH linear | github` | workflows/plan-issues.md |
parallel-check |
[ISSUE_IDS] |
workflows/parallel-check.md |
Safe parallel handoff analysis |
initialize |
[ISSUE_ID] |
workflows/initialize.md |
Team setup, auth, cache, state (standalone) |
start routing logic:
- Parse explicit args first:
github OWNER/REPO#N→TRACKER=github,ISSUE_ID=issue-N,OWNER/REPOretained for GitHub API calls.[ISSUE_ID]→ Linear unless it already starts withissue-.
- Current directory is a worktree (git common dir differs from
.git) →workflows/start-worktree.mdwith the parsed issue context. - Otherwise →
workflows/start.md
Development
| Command | Arguments | Workflow | Notes |
|---|---|---|---|
dev-start |
[ISSUE_ID] |
workflows/dev-start.md |
Delegate implementation |
dev-fix |
[ISSUE_ID] |
workflows/dev-fix.md |
Delegate review fix items |
ci-fix |
PR_NUMBER | queue |
workflows/ci-fix.md |
Fix CI failures |
Review & Submission
| Command | Arguments | Workflow | Notes |
|---|---|---|---|
review |
[all] | [last N] | [HASH] |
workflows/review.md |
On-demand review (standalone) |
review-codebase |
[PATH] |
workflows/review-codebase.md |
Ad-hoc whole-codebase reviewer fanout |
review-pr |
[PR_NUMBER] |
workflows/review-pr.md |
Pre-submission review |
review-pr-comments |
PR_NUMBER | BRANCH |
workflows/review-pr-comments.md |
Triage PR comments |
submit-pr |
[PR_NUMBER] |
workflows/submit-pr.md |
Local review, push, create PR, async triage, approval gate, CI verify |
merge-pr |
PR_NUMBER | all |
workflows/merge-pr.md |
Verify and merge |
fix-reconcile |
— | workflows/fix-reconcile.md |
Internal (not user-invocable) |
post-summary |
[ISSUE_ID] |
workflows/post-summary.md |
Post summary comments |
Execution Mode
Follow ALL Workflow Execution rules for every command.
Workflows
Session Lifecycle
| Workflow | Trigger | Purpose |
|---|---|---|
workflows/initialize.md |
initialize |
Team setup, auth, cache, state init |
workflows/start.md |
start from main repo |
Select/prepare one Linear or GitHub work item |
workflows/start-worktree.md |
start (from worktree) |
Full session: dev → review → submit → finalize |
workflows/start-new.md |
start new |
Create one Linear or GitHub issue |
workflows/handoff.md |
handoff |
Launch-only work item handoff |
workflows/plan-issues.md |
plan-issues |
Convert plan items into issues |
workflows/parallel-check.md |
parallel-check |
Check safe parallel handoff groups |
Development
| Workflow | Trigger | Purpose |
|---|---|---|
workflows/dev-start.md |
dev-start |
Delegate implementation to specialist agents |
workflows/dev-fix.md |
dev-fix |
Delegate fix items to dev agents |
workflows/ci-fix.md |
ci-fix |
Analyze and fix CI failures |
Review & Submission
| Workflow | Trigger | Purpose |
|---|---|---|
workflows/review.md |
review |
On-demand review with fix handling |
workflows/review-codebase.md |
review-codebase |
Whole-codebase reviewer fanout with findings only |
workflows/review-pr.md |
review-pr |
Pre-submission review with fix handling and QA |
workflows/review-pr-comments.md |
review-pr-comments |
Triage PR review comments via domain agents |
workflows/submit-pr.md |
submit-pr |
Local pre-PR review, push, create PR, async comment triage, approval gate before CI verify, merge gates |
workflows/merge-pr.md |
merge-pr |
Verify conditions and merge PR(s) |
Per-Issue Lifecycle
| Workflow | Trigger | Purpose |
|---|---|---|
workflows/fix-reconcile.md |
fix-reconcile |
Check if fixes address existing open issues |
workflows/post-summary.md |
post-summary |
Post summary and handoff comments |
Reference
| Workflow | Purpose |
|---|---|
workflows/agent-sequencing.md |
Cross-domain blocking relations and delegation order |
workflows/recommendation-bias.md |
Review finding categorization (fix vs issue) |
Scripts
.agents/skills/orch/scripts/<script> [args]
| Script | Purpose |
|---|---|
workflow-state |
Persistent state read/write/append (survives compaction) |
git-context |
Print git-derived workflow values such as branch, head SHA, issue id, repo root, and timestamps without inline shell plumbing |
pr-view-json |
Print PR view JSON and return success for expected status=no_pr so workflows can route to PR creation without shell fallback expressions |
resolve-base-branch |
Print the worktree base branch (WORKTREE_DEFAULT_BRANCH, remote HEAD, or main) |
review-init |
Initialize standalone review context and print branch/worktree/issue/state JSON |
review-artifact-check |
Validate a reviewer's on-disk JSON artifact (exists, mtime >= delegation epoch, jq -e '.verdict', and no self-reported no-review) and print {ok, path, reason} — the sole review-pr completion condition. An artifact whose qa_metadata admits no review happened (review_performed: false or a no-scope reason) is rejected with reason no_review regardless of verdict; artifacts without qa_metadata are unaffected. --file <path> [delegated_at_epoch] validates one explicit artifact; the optional boundary applies the same freshness gate so a stale/misdated external review is rejected |
dev-return-write |
Deterministically write a dev agent's round-scoped completion artifact ([WORKTREE]/tmp/dev-return-[ISSUE_ID]-[ROUND_ID].json) with jq, atomically (temp+mv), instead of hand-authoring the JSON. --worktree PATH --kind implement|fix --issue ID --round-id RID --branch B --commit SHA --validate STR [--qa-label L]... [--bundled] [--no-summary] [--summary-file PATH] [--item N DECISION REASONING]...; writes round_id/schema_version, validates inputs (exit 2 on bad --kind, missing required arg, malformed --validate, bad --item DECISION, empty REASONING, an --issue/--round-id outside ^[A-Za-z0-9._-]+$, or a fix/--bundled invocation with no --item) and prints the artifact's absolute path. Canonical schema: schemas/dev-return.md |
dev-artifact-check |
Validate a dev agent's round-scoped completion artifact and print {ok, path, reason} (valid|missing|invalid|incomplete, gates ordered missing → invalid → incomplete → valid). Round mode --worktree WT --issue ISSUE --round-id RID [--expect-items N,N,...] resolves WT/tmp/dev-return-ISSUE-RID.json, requires its internal round_id == RID (clock-independent identity — no mtime gate, vstack#776), the type-strict scalars (.kind ∈ implement|fix; .issue/.branch/.commit/.validate non-empty strings; .round_id string; .schema_version number), and the items rule (--expect-items = exact delegated set for fix rounds; else non-empty well-formed for fix/bundled; implement allows items: []). A fresh valid artifact for the current round lets dev-start.md § 3 accept a completion whose return was lost to a tool timeout (vstack#770); git/tracker corroboration stays in orch. --file <path> [--round-id RID] [--expect-items ...] validates one explicit artifact. One identity model (round id) — no mtime gate, no legacy positional mode |
tracker-for-issue |
Print github for issue-* ids and linear otherwise |
approval-wait |
Poll for the reviewer-gate verdict plus unresolved-thread count — the submit-pr § 4 review-gate poller: GitHub-native approval (reviewDecision/latestReviews) by default, --mode review waits for a non-author review of the current head + zero unresolved threads, --resolve-mode prints the project's effective gate mode; never parses bot reactions or sticky prose |
ci-wait |
Block until CI completes on a PR — runs after the review gate; correlates the current-head substantive run with custom aggregate status (and with the head's Actions run list when the check rollup hides a newer dispatch), and CI_WAIT_NO_CHECKS_GRACE (default 180s) bounds unregistered checks |
PR_REVIEW_GATE |
Reviewer-gate mode (gate 4): approval requires the GitHub-native approval verdict; review requires a non-author review of the current head + zero unresolved threads (for commenting-only review bots that never approve); off = reviewer-less repo, wait skipped and gate recorded not-applicable (default approval; legacy PR_APPROVAL_GATE on/off maps to approval/off when unset) |
PR_REVIEW_CHECK |
Exact name the trusted review bot publishes on analyzed heads (e.g. Devin Review): in review mode a success signal of that name on the current head — a check-run conclusion or a commit-status context, whichever surface the bot publishes (vstack#654, vstack#681) — is accepted as review evidence when no review object is pinned there, for bots that submit reviews only with findings. Matched by name/context only; trust is user configuration, like PR_REVIEW_NUDGE. Empty (default) = review objects only |
PR_REVIEW_ON_TIMEOUT |
Deadline behavior when no reviewer posts (credits exhausted): block (default) → timeout, workflow prompts; proceed → proceeded (exit 0) recorded as a reviewer-down override so a dead reviewer never stalls the fleet, but only with zero unresolved threads (open threads return comments first) and CI/comment-hygiene gates still apply; a changes_requested always blocks |
PR_REVIEW_OUTAGE_CONTEXT |
Makes proceed end-to-end where CI independently gates on review evidence: names a commit-status context orch posts as success on a proceeded head, which the repo-side gate accepts (outageok term) + refire-bridge re-runs (DEVELOPMENT.md "Reviewer-outage recognition"). Empty (default) = orch-side-only. SECURITY: branch-protection relaxation bounded by the genuine-silence predicate + bot-only status trust; trusted publishers only |
orch-env |
Print the effective value of a vstack [env] setting (process env > vstack.settings.toml > supplied default; numeric defaults reject non-numeric values) — how workflows read CI_FIX_MAX_CYCLES |
session-init |
Initialize session state for a new worktree (called by initialize.md) |
open-terminal |
Launch-only handoff helper for Linear/GitHub worktrees |
parallel-groups |
Local cache for safe parallel handoff analysis |
ci-wait --json returns {status, verdict, elapsed_seconds, pending_checks, failed_checks, passed_checks} where status is complete/timeout/error and verdict is pass/fail/pending. It ignores later all-skipped dispatches when selecting the current-head substantive workflow run and treats an aggregate status still linked to an older run as pending while a newer substantive run has no failures. A settled failure attributable only to superseded runs — e.g. a review run cancelled by concurrency whose same-second review-comment sibling never surfaces in the check rollup — is additionally correlated against the head's Actions run list (vstack#650, vstack#699): any queued or in-progress substantive-event run on the head — including a rerun attempt of an older run, which keeps its original run id — keeps the verdict pending; a newest same-workflow run (by id, or by a rerun attempt's fresher settlement) that completed successfully discards the stale failures; and a failed newest run — or a cancelled run with no newer or fresher sibling — stays terminal. Current-run failures remain terminal; a missing replacement reaches the existing timeout and never passes. Every exit path emits a final stdout result (a CI passed/CI failed/CI timeout/CI error line without --json); checks still in progress at the deadline report status: "timeout", never silent success.
approval-wait --json returns {status, review_decision, approvals, changes_requested, unresolved_count, elapsed_seconds} where status is approved/reviewed/changes_requested/comments/timeout/proceeded/error. proceeded (exit 0) is the PR_REVIEW_ON_TIMEOUT=proceed reviewer-down degrade — a deadline reached with zero unresolved threads and no reviewer evidence; the --on-timeout block|proceed flag overrides the setting. Approval is GitHub-native only, either signal: reviewDecision == "APPROVED", or — when reviewDecision is empty because no required-review protection exists — at least one reviewer whose latest review is APPROVED and none whose latest review is CHANGES_REQUESTED. Any reviewer counts, human or bot; emoji reactions, sticky comments, and checklist prose are never parsed. REVIEW_REQUIRED never falls back to latestReviews. status: "comments" is an early return when unresolved review threads exist without a verdict, so callers triage instead of idling to the deadline. Every exit path emits a final stdout result (an Approval .../Review ... line without --json); no verdict at the deadline reports status: "timeout" (or proceeded under PR_REVIEW_ON_TIMEOUT=proceed when there is zero reviewer evidence and no unresolved threads), never silent success.
approval-wait --mode review (vstack#642) waits for a formal review instead of an approval — for repos whose review bots only post COMMENTED reviews. It succeeds with status: "reviewed" (exit 0, JSON adds mode, head_sha, reviews_at_head) when a submitted review is pinned to the current head SHA (re-read every poll, so a force-push resets the wait), is not DISMISSED and not the PR author's own — any state counts, an approval is also a review — with no non-author reviewer's latest review standing at CHANGES_REQUESTED and zero unresolved review threads. With PR_REVIEW_CHECK set to the trusted review bot's published name, a success signal of exactly that name on the current head also counts as review evidence — the newest check-run with that name concluding success, or, when no check-run matches, a commit-status context of that name at state success (vstack#654, vstack#681; some bots publish statuses, not check-runs) — for bots that submit a review object only when they have findings, under the same CHANGES_REQUESTED and thread conditions; the JSON result then carries review_evidence ("review" or "check") naming the signal that passed, plus review_evidence_surface ("check_run" or "status") for check evidence. approval-wait --resolve-mode prints the project's effective gate mode (approval/review/off): PR_REVIEW_GATE when set, else legacy PR_APPROVAL_GATE (on → approval, off → off), else approval — the single implementation of that derivation; workflows read the mode through it. In both wait modes the poller also nudges silent reviewers: after PR_REVIEW_NUDGE_SECS (default 600s) without the mode's signal for the current head — the clock restarts on every push — it posts the user-configured PR_REVIEW_NUDGE comment once per head SHA, or with that setting empty falls back to a GitHub-native re-review request to the PR's requested and past reviewers.
session-init --json reports worktree Linear auth as the structured linear_auth object from linear auth-check. linear_auth.error = "not installed" is reserved for a missing Linear skill command; API key, 1Password, and API failures keep their original auth-check diagnostic.
Both approval-wait and ci-wait use scripts/lib/gh-auth.sh, which wraps the GitHub skill's shared auth helpers, for a bounded auth-resolution ladder — see DEVELOPMENT.md for the full ladder description. GitHub auth is env-first: already-resolved GH_TOKEN, GITHUB_TOKEN, or GH_BOT_TOKEN values from the parent process win before local files are read, and op read is only used for the final selected op:// reference. Auth preflight validates selected env tokens with gh api user; gh auth status is only authoritative for keyring auth when no env token is selected. The waiters probe each candidate auth source at most once before moving to the next fallback. The github.sh router additionally prefers a resolved GH_BOT_TOKEN before a resolved GITHUB_TOKEN for bot-capable operations. Exit 3 on hard auth failure; callers treat both scripts consistently.
workflow-state actions
To target a state directory from a worktree, pass the global --state-dir <path> flag before the subcommand: it applies to every action and takes precedence over ORCH_STATE_DIR. Prefer the flag over an ORCH_STATE_DIR=… workflow-state … env prefix — the env-assignment prefix is rejected under Codex approval=never as a flagged command shape, while a plain flag is classifier-safe. ORCH_STATE_DIR remains supported as an environment fallback (default: tmp). Put non-secret workflow settings in committed vstack.settings.toml under [env]; .env.local remains supported for secrets and personal overrides.
State keys are the normalized issue IDs — issue-N for GitHub issues (per start routing), PROJ-123 for Linear — never the bare GitHub issue number. As a safety net, every action except init aliases a bare numeric key to the issue-N state file when only that file exists; the exact-key file wins when present, and the command errors (exit 2) instead of guessing when files exist under both keys.
.agents/skills/orch/scripts/workflow-state --state-dir /path/to/tmp append PROJ-123 fixed_items '{"description":"Fix"}'
| Action | Purpose |
|---|---|
--state-dir <path> (global, before subcommand) |
Override state directory for every action; precedence over ORCH_STATE_DIR |
init <ID> --agent <name> --worktree <path> [--branch <b>] [--team <t>] |
Initialize state file |
exists [--json] <ID> |
Check state file exists; --json prints {issue_id,path,exists} and exits 0 |
path <ID> |
Print state file path |
get <ID> <.field> |
Read state field |
set <ID> <field> <value> |
Write state field |
set-git-head <ID> <field> [worktree] |
Write current HEAD SHA to a field without command substitution |
set-now <ID> <field> |
Write current epoch seconds to a field without command substitution |
new-round-id <ID> <field> |
Generate a unique per-delegation round token (date +%s%N-$RANDOM — nanosecond timestamp + random suffix, distinct even across rapid re-stamps), store it at <field>, and print it — binds a dev completion artifact to its delegation (vstack#776) |
append <ID> <field> <value> |
Append to array field |
increment <ID> <field> |
Increment counter |
update <ID> <jq-expr> |
Arbitrary jq mutation (e.g. nested merges) |
Schemas
| Schema | Purpose |
|---|---|
schemas/workflow-state.md |
Persistent state file schema (issue/agent/worktree identity, child_sessions, review_agents/review_agent_ids, cycle counters, json_paths, fixed/escalated items, PR comment review tracking) |
schemas/dev-return.md |
Dev completion-artifact (tmp/dev-return-[ISSUE_ID]-[ROUND_ID].json) schema: round-id identity, fields, kind rules, items[] shape, written by dev-return-write, validated by dev-artifact-check |
../reviewer/schemas/review-finding.md |
Review/QA finding JSON format |
Audit input and roadmap-plan schemas live in project-management/schemas/ — cross-skill path.
Configuration
| Variable | Purpose | Default |
|---|---|---|
ORCH_STATE_DIR |
Override state file directory (env fallback for the --state-dir flag, which wins when both are set) |
tmp |
ORCH_CACHE_DIR |
Parallel-group safety cache directory | .cache/orch |
GH_ISSUE_PATTERN |
Regex for issue IDs in branch names | — |
CI_FIX_MAX_CYCLES |
Max automated ci-fix cycles per PR submission / merge recovery (read via orch-env CI_FIX_MAX_CYCLES 6) |
6 |
REVIEWER_SLOT_BUDGET |
Total concurrent agent-session budget of the runtime, counting the primary session (read via orch-env REVIEWER_SLOT_BUDGET 0). 0 = unlimited: all reviewers launch up front and persist. When the reviewer set exceeds the available slots (budget − primary − live dev/QA sessions), review workflows run reviewers in bounded waves. If the runtime contradicts an unlimited budget with a thread-limit spawn failure, the review demotes to bounded waves sized by the observed successful spawns and recommends the observed budget (review-pr.md § 2.2). Codex collaboration runtime: set to the config-declared cap (MultiAgentV2 default 4 total including the primary; features.multi_agent_v2.max_concurrent_threads_per_session in ~/.codex/config.toml) |
0 |
System Dependencies
jqbash4+flock(util-linux) for atomic state updates
Tests
bash skills/orch/tests/run-all.sh # full suite
bash skills/orch/tests/run-all.sh session_init # filter
Each tests/*.sh is self-contained (prints pass: N fail: M, exits non-zero on failure). The runner discovers files at execution time — no registration needed.
Skill Rules
Workflow Execution
Sequential Section Execution
Process sections in order: mark in-progress, execute all sub-sections, mark completed, proceed. Never create tasks for sub-sections — they are steps within the parent task. Never mark a parent complete before all sub-sections finish.
Never skip steps based on predicted outcome or change scope. The workflow text decides, not the agent.
Skip-If Condition Evaluation
Evaluate "Skip if [condition]" literally. If true, append "(SKIPPED)" and mark completed. The workflow decides what to skip.
Nested Workflow Invocation
⤵-marked workflows must be invoked through the harness mechanism — never inlined. Record the return point (→ § X) before invoking.
Worktree Scope
In a worktree, never create, switch to, or act on a different worktree or branch. If the resolved ISSUE_ID differs from the current branch, stop and ask: reuse, abort, or switch explicitly.
Harness-Safe Shell
Generated workflow commands must be safe for strict harness command policies. Prefer one simple command per tool call with explicit arguments. Avoid inline $(...), shell for/while loops, array-building snippets, heredocs, pipelines used only for value plumbing, and redirected writes to tmp/; Codex can classify those helper shapes as approval-required even when approval policy is never.
Run exactly one command per tool call. Multiple commands batched into a single call — newline-separated or ;-separated — are themselves rejected under Codex approval=never, even with no redirection, substitution, or pipeline; the multi-command shape alone triggers the block. Never emit a fenced block that stacks several commands for one call. When related workflow-state operations belong together, fold reads into one workflow-state get '{...}' (a jq object returning every field) and writes into one workflow-state update '... | ...' (a piped jq expression applying every mutation atomically) rather than emitting a multi-command block. Split anything that genuinely can't collapse into one expression — set-git-head/set-now (they compute their value internally), a read mixed with a write, a // empty default that would collapse a combined object, or a per-item loop — into separate one-command blocks, each its own tool call.
Use helper scripts instead of shell plumbing:
git-contextfor branch/head/timestamp/issue values.workflow-statefor state:get '{...}'combines related reads into one call,update '... | ...'combines related writes into one atomic call, andset-git-head/set-now/append/incrementcover git-, clock-, and per-item writes.- Harness file-write/edit tools, or
apply_patch, for Markdown/JSON bodies and completion summaries.
When a workflow needs several files read, issue separate read commands for each file or use the harness file-read tool; do not wrap required reads in a shell loop. When an optional environment variable affects a command, either omit the option and let the script auto-detect, or first read the value with printenv VAR and then run a second command with a literal value. Do not include unset-variable expansions such as "$OPTIONAL_OVERRIDES" in required command examples.
Env-assignment prefixes are normalized at acceptance, not at run time (vstack#714). A required command shaped VAR=value cmd args — however authoritative its source: a maintainer issue spec, a delegated verification list, a review recommendation — is rejected under Codex approval=never for the prefix shape alone. Normalize it at the point where the command enters the workflow (issue preparation, delegation assembly), before any agent is asked to run it, into two simple commands: first confirm the ambient environment already satisfies the requirement — printenv VAR for ordinary variables; locale for locale variables, reading the effective LC_* lines (an unset LC_ALL with an effective C/POSIX locale satisfies LC_ALL=C) — then run the bare cmd args unchanged. The prefix is an environment precondition, not part of the required command; the bare command remains exact. env VAR=value cmd args is not the documented substitute: it merely relocates the assignment, the classifier is not documented to accept it, and a shape that might pass cannot carry a required verification step — if an env form is rejected, that rejection is final (never retry it). If the ambient environment does not satisfy the precondition, report the mismatch to the orchestrator/user as a blocker instead of running the command under the wrong environment. The rule is generic to any env-assignment prefix; locale (LC_ALL=C) is the motivating example.
A literal backtick anywhere in a generated command is command substitution to the classifier (vstack#721). A search over backtick-bearing text (Markdown inline code) is rejected for that shape alone, even when the command is a read-only rg/grep. Author the pattern with the regex hex escape \x60 in single quotes as one simple command ([\x60] inside a bracket expression), in regex mode — rg -F has no escapes and would need the literal character. The canonical statement with the worked example lives in reviewer SKILL.md § Harness-Safe Shell; it applies to every generated command list — dev validation steps, delegated audit searches, fix recommendations — not only reviewer checks.
Never author a workflow step that assumes top-level git rebase will run (vstack#722). Under Codex approval=never the classifier rejects the porcelain verb itself — a harness-side classification that no user authorization or delegation can lift, so an "explicitly authorized" rebase fails identically and must not be retried or replaced with an improvised force-push. The documented equivalent for updating a clean, linear issue branch is the worktree skill's guarded create <ID> --reuse/--restack path or, when the tool path is unavailable, the exact replay in worktree SKILL.md § Policy-blocked rebase (cherry-pick replay fallback) — every step a single simple command, semantically equivalent to the rebase for a clean linear branch. A dirty tree or merge commits in the range put the branch outside that recipe: report a blocker instead of improvising.
Tracker Resolution
Resolve once per workflow, store as TRACKER:
- Caller
trackerparam wins. ISSUE_IDstarts withissue-→github. Issue number =${ISSUE_ID#issue-}; repo from caller context when supplied, otherwise fromgh repo view --json nameWithOwner.- Otherwise →
linear.
.agents/skills/orch/scripts/tracker-for-issue "[ISSUE_ID]"
Use the output as TRACKER before any tracker test. Steps marked Linear only / GitHub only run only for that tracker. Never run linear.sh against a GitHub item — GitHub state lives in `g
…(truncated)