/work-run "" [--cap N] [--resume]
Drives a fully-decomposed, fully-specified work group through the
feature-pipeline autonomously. Unlike /work-start --parallel (which
fires one wave of SPECIFIED WDs, waits for all, then exits),
/work-run continuously re-dispatches WDs as their dependencies clear
— so a 10-WD group with cross-WD required_state: SPECIFIED deps can
complete in roughly the wall-clock time of the longest single WD.
The contract. Sub-agents follow the user-required escalation
protocol established by /work-start (see its "User-required
escalation contract" section). When a sub-agent halts on an
escalation, /work-run freezes new dispatches, surfaces the question
via AskUserQuestion, waits for resolution, then resumes. Currently
in-flight sub-agents continue to run — only the dispatch loop is
paused — so the user only blocks the dispatcher, not the work that
was already underway.
Subagent contract — MANDATORY for every dispatch. Every WD this skill dispatches MUST be given this preamble at the top of its prompt:
Subagent contract: Honor
rules/completeness-contract.md(load-bearing — no silent deferrals; trigger phrases = escalation signals, not completion modes). If you cannot complete assigned scope, escalate via AskUserQuestion with user-validatable proof. A return claiming COMPLETE alongside deferred items is a contract violation.
When WD sub-agents return, /work-run MUST scan the return for
trigger phrases ("candidate", "follow-on", "out of scope", "deferred",
"future work", "covered transitively", "edge case") and treat any
occurrence as an escalation signal — block the WD's COMPLETE acceptance
and route to user via AskUserQuestion before marking the WD done.
Arguments:
<group-slug>— the work group to run--cap N— maximum concurrent sub-agents (default: 3). Higher uses more tokens-per-minute; lower yields wall-clock for cost.--resume— explicit resume of an existing orchestrator state. If the directory exists and--resumeis NOT passed, the user is asked viaAskUserQuestionwhether to resume or clear-and-restart.
Prerequisites:
- The group is fully decomposed (
/work-decomposehas run). - Every WD that should be in scope has been planned (
/work-plan). Only WDs withstatus: SPECIFIEDand satisfied deps enter the dispatch queue. WDs inREADYorBLOCKEDare not auto-planned — the user owns the specification phase. - The user has already verified the WGs' specs/decisions are coherent.
/work-runwill surface design questions but it does NOT do upfront spec authoring.
Step 0 — Concurrency guard
Before doing anything else, check whether another /work-run instance
might already be driving this group. Use mkdir-based atomic acquisition
(not echo-into-file, which has a TOCTOU race between liveness check and
write — adversarial review HIGH #3, 2026-05-11):
ORCH_DIR=".work/<group-slug>/.orchestrator"
mkdir -p "$ORCH_DIR" 2>/dev/null || true
LOCK_DIR="$ORCH_DIR/driver.lock"
# Atomic test-and-set. mkdir fails if the directory exists — no race.
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
held_pid=$(cat "$LOCK_DIR/pid" 2>/dev/null || echo "")
if [[ -n "$held_pid" ]] && kill -0 "$held_pid" 2>/dev/null; then
echo "ERROR: /work-run is already running (driver pid $held_pid)"
echo " If that process is dead, remove $LOCK_DIR and retry."
exit 1
fi
# Stale lock — held pid is dead. Clean up and retry once.
rm -rf "$LOCK_DIR"
mkdir "$LOCK_DIR" || { echo "ERROR: could not acquire lock"; exit 1; }
fi
echo $$ > "$LOCK_DIR/pid"
trap "rm -rf '$LOCK_DIR'" EXIT
Cap validation (MEDIUM #1, 2026-05-11): reject --cap values below 1.
A --cap 0 would silently terminate the run without dispatching anything.
if [[ "$cap" -lt 1 ]]; then
echo "ERROR: --cap must be >= 1 (got $cap)"
exit 2
fi
The lock directory is the single-driver enforcement the orchestrator's
state machine relies on. The orchestrator script itself doesn't check
this — /work-run does, because it knows it's the only sanctioned
driver.
Step 1 — Validate work group
Check .work/<group-slug>/ exists. If not, list available groups and
stop. (Same as /work-start Step 1.)
Display the opening header:
───────────────────────────────────────────────
🏃 WORK RUN · <group-slug>
───────────────────────────────────────────────
Step 2 — Initialize or resume orchestrator
Two branches based on whether .work/<group-slug>/.orchestrator/
already exists.
Guard (HIGH #5, 2026-05-11): if --resume was passed but the
orchestrator directory does NOT exist, error out before doing anything
else — the user's explicit intent (resume an existing run) cannot be
satisfied:
if [[ "$resume_flag" == "1" && ! -d "$ORCH_DIR" ]]; then
echo "ERROR: --resume requested but no orchestrator state exists for <group-slug>."
echo " Run /work-run <group-slug> without --resume to start fresh."
exit 1
fi
2a. Orchestrator state exists
Display its current state:
bash .claude/scripts/work-orchestrator.sh status "<group-slug>"
Cold-resume reconciliation (HIGH #7, 2026-05-11). Before re-entering
the loop, scan in-flight/ for stale records — a previous /work-run
session may have died with sub-agents in flight whose notifications
were lost. For each in-flight record:
- Read
feature_slugfrom the record +dispatched_at. - Check
.feature/<feat>/status.mdmtime. If it does not exist OR has not been touched sincedispatched_at+ a small allowance (say 60s), the sub-agent likely never started or its parent died before its first stage transition. - For each such stale record, use
AskUserQuestion:- Title: "
<wd-id>was in-flight when the prior session ended, but status.md shows no activity. Mark as STOPPED, retry dispatch, or keep waiting?" - Options: Mark STOPPED / Retry dispatch / Keep waiting
- Title: "
- Apply the user's choice via the orchestrator
(
complete <wd> STOPPED_AT_unknown "stale on resume"/ move record back to queue + new dispatch in Step 3e / leave it).
If --resume was passed on the command line, skip the resume-vs-clear
prompt below (the user already declared intent) and proceed to the
reconciliation pass + Step 3.
Otherwise, use AskUserQuestion:
- Title: "Existing orchestrator state found for
<group-slug>. Resume from where it left off, or clear and start fresh?" - Options:
- Resume — run cold-resume reconciliation above, then enter loop
- Clear and restart — run
work-orchestrator.sh clear+init(DESTRUCTIVE — completed-set is preserved nowhere else). Warns user that any currently-running sub-agents become orphans; their completion notifications will arrive into a fresh-state /work-run that has no in-flight record matching them, and Step 3a will ignore them. - Stop — exit
/work-runwithout changes
2b. No orchestrator state
Initialize:
bash .claude/scripts/work-orchestrator.sh init "<group-slug>" --cap <N>
If init errors (typo'd group, work-resolve failure), the
orchestrator script will print a diagnostic — surface it to the user
and stop. Do not proceed with a half-built state.
The initial queue contains every WD with status: SPECIFIED AND all
artifact_deps satisfied. If the queue is empty:
No SPECIFIED WDs in <group-slug> ready to dispatch.
This usually means /work-plan has not been run on this group yet, or
all WDs are blocked on cross-WD dependencies whose upstream isn't
COMPLETE.
Run /work-status <group-slug> to see the readiness breakdown.
Run /work-plan <group-slug> to take READY WDs to SPECIFIED.
Clear the orchestrator state (work-orchestrator.sh clear) so the
next run starts fresh, and exit.
Step 3 — Driver loop
This is the main loop. Each iteration is a single LLM turn — the skill body runs top-to-bottom once per turn, then returns. Background-agent completion notifications cause the next turn, which re-enters the loop through this step.
3a. Reconcile any pending in-flight completions
Source of truth. When a background sub-agent completes, the runtime
delivers its final assistant message in the new turn's input — this is
the canonical signal of completion, NOT the dispatch marker (the
marker only records begin; /work-run never writes ack or fail,
so its result field is always null while in-flight).
Matching notification → WD. The agent's final message will start
with the <feature-slug>: prefix per PR A's escalation contract. To
recover the WD-id, look up the in-flight record by feature_slug:
WD_ID=$(grep -l "\"feature_slug\":[[:space:]]*\"<slug>\"" \
"$ORCH_DIR/in-flight/"*.json 2>/dev/null \
| head -1 | xargs -I{} basename {} .json)
If multiple completion notifications batched in this turn (the user spent time answering AskUserQuestion in 3c, several siblings finished during that wait), process each by repeating the lookup-and-classify sequence. The orchestrator's per-WD mutations are atomic, so order within a turn doesn't matter.
For each completion:
Extract the return line from the new turn's input (the agent's final assistant message — one line of the form
<feature-slug>: <STATUS> — <detail>).Classify (5-way, per
/work-start's escalation contract — matchESCALATION_AT_BEFORESTOPPED_AT_since both share the<STATUS>_AT_<stage>shape):ESCALATION_AT_<stage> — <category>: <question>→ escalationCOMPLETE — <detail>→ cleanSTOPPED_AT_<stage> — <detail>→ stopped mid-pipelineERROR — <detail>→ erroredSKIPPED — <detail>→ claim conflict (rare under orchestrator)- Unparseable → parse-failed
- Agent return literally equals
[Tool result missing due to internal error]→ payload-lost - Agent return contains
The user doesn't want to proceed with this tool use.→ user-stopped
2.5. Completeness-contract validation (MANDATORY if classification is clean).
If step 2 classified the return as clean, run the validator BEFORE
routing to the orchestrator:
mkdir -p /tmp/vallorcine
return_file=/tmp/vallorcine/subagent-return-"<group-slug>"-"<wd-id>".txt
printf '%s\n' "$FULL_RETURN_TEXT" > "$return_file"
bash .claude/scripts/validate-subagent-return.sh "$return_file" --require-ac-coverage 2>/tmp/vallorcine/validator-stderr.txt
rc=$?
Branch on rc:
0 → validation clean, proceed to step 3 with
cleanclassification.1 → trigger phrases detected. The sub-agent claimed COMPLETE while deferring scope. DOWNGRADE classification to
escalationwith categorydeferral-detected. Construct an escalation.json:cat > .feature/"<slug>"/escalation.json <<'JSON' { "question": "WD-<wd-id> claimed COMPLETE but the return contains deferral trigger phrases. Approve as-is, re-dispatch with expanded scope, or stop?", "options": [ {"label": "Approve as-is", "description": "User authorizes the deferred items — treat the WD as COMPLETE"}, {"label": "Re-dispatch with expanded scope", "description": "Send back to the WD pipeline with the deferred items added to scope"} ], "validator_findings": "<paste contents of /tmp/vallorcine/validator-stderr.txt>" } JSONThen route as
block <group> <wd-id> 'escalation:deferral-detected' '.feature/<slug>/escalation.json'.2 → tooling error (file unreadable, script missing). Log stderr but treat as
clean— do NOT block the work group on infrastructure failures.
The check is mechanical: the script greps for fixed phrases. False positives (a legitimate technical use of "candidate") route to the user who can clear them in one keystroke. False negatives ship incomplete work — the asymmetry favors the user.
Route to the orchestrator. Always single-quote the reason argument (MEDIUM #6, 2026-05-11) — return lines can carry
$(), backticks, or semicolons. Pass via single quotes and escape any embedded'via the'''pattern. Example:'this is a 'safer' message'→'this is a '''safer''' message'.Classification Orchestrator call escalation block <group> <wd-id> 'escalation:<category>' '.feature/<slug>/escalation.json'clean complete <group> <wd-id> COMPLETE '<line>'stopped complete <group> <wd-id> 'STOPPED_AT_<stage>' '<line>'errored complete <group> <wd-id> ERROR '<detail>'skipped complete <group> <wd-id> SKIPPED '<detail>'parse-failed complete <group> <wd-id> ERROR 'parse-failed: <first 80 chars>'payload-lost complete <group> <wd-id> ERROR 'payload-lost'user-stopped complete <group> <wd-id> ERROR 'user-stopped'Orphan notifications (clear-and-restart edge case). If the
feature_sluglookup at step 1 returns no matching in-flight record, this notification is from a sub-agent that was orphaned by a priorclear. Log it to stderr and skip — do NOT call the orchestrator with an unknown WD-id (would error).
3b. Check for hung in-flight WDs
HUNG=$(bash .claude/scripts/work-orchestrator.sh hung "<group-slug>" --threshold-seconds 1800)
If non-empty, for each line <wd-id> <delta-seconds> <feature-slug>:
Use AskUserQuestion:
- Title: "WD- has not transitioned in min. Continue waiting, mark as stopped, or investigate?"
- Options:
- Keep waiting — pipeline durations vary; some legitimately take 30+ min
- Mark STOPPED — accept that the sub-agent is wedged;
complete <wd> STOPPED_AT_unknown "hung past threshold"and continue - Investigate — exit
/work-runso the user can inspect.feature/<slug>/manually
3c. Surface escalations if paused
PAUSED=$(grep -oE '"paused":[[:space:]]*(true|false)' "$ORCH_DIR/state.json" | head -1 | grep -o 'true\|false')
If paused == true, enumerate $ORCH_DIR/blocked/*.json. For each
blocked WD:
Read
blocked/<wd>.jsonto getfeature_slugandescalation_path.Read
<escalation_path>(.feature/<slug>/escalation.json) for the structured question. Validate the file exists and parses as JSON with aquestionfield (MEDIUM #2, 2026-05-11):if [[ ! -f "$escalation_path" ]] || ! python3 -c "import json; d=json.load(open('$escalation_path')); assert 'question' in d" 2>/dev/null; then missing_escalation=1 fiIf missing or malformed, construct a fallback AskUserQuestion below.
Use AskUserQuestion with the appropriate construction. Cap the
total option count at 4 (per kit's interactive-prompt standard;
MEDIUM #7, 2026-05-11) — if escalation.json's options array exceeds
2 entries, present the first 2 and instruct the user to consult the
JSON file for the rest.
Construction A — escalation.json valid with options:
- Title: WD-id + the
questionfield - Options (up to 4):
- First option from escalation.json
options[0](label + description) - Second option from
options[1]if present (and not "Other"-shaped) - Skip this WD — mark as ERROR
- Abort run — exit cleanly
- First option from escalation.json
- If
optionshas more than 2 entries, suffix the title with "(see<escalation_path>for all candidate answers)"
Construction B — escalation.json valid without options:
- Title: WD-id + the
questionfield - Options:
- I've resolved it externally — re-dispatch
- Skip this WD
- Abort run
- Other (with text input) — record the answer
Construction C — escalation.json missing or malformed (fallback):
- Title: "WD-
<wd-id>: sub-agent escalated butescalation.jsonis missing/malformed. Final return line:<line>. How to proceed?" - Options:
- Re-dispatch as-is (assume the user has already resolved out of band)
- Skip this WD
- Abort run
- Inspect (exit so user can investigate)
Per the user's chosen action:
- Resolved (Constructions A/B) → write
.feature/<slug>/escalation-resolved.mdrecording the user's choice + verbatim answer text + timestamp, thenunblock <group> <wd-id>(re-queue). The re-dispatched sub-agent prompt below (Step 3e) tells the next agent to read this file. - Sub-agent option chosen (Construction A) → record the chosen
option's label + description in
.feature/<slug>/escalation-resolved.md, thenunblock <wd-id>. - Skip →
skip <group> <wd-id> '<user-supplied reason>'(atomic blocked → completed:ERROR, per PR C's orchestratorskipsubcommand; CRITICAL #1, 2026-05-11). Do NOT useunblock + complete— the intermediate queue state breaks becausecompleterequires in-flight. - Abort → drop the lock, exit cleanly with: "to resume:
/work-run
<group-slug>--resume. Currently in-flight sub-agents will continue to completion; their notifications arrive into the next session." Document explicitly that blocked/ and in-flight/ remain on disk for next-session reconciliation.
Repeat per blocked WD until blocked/ is empty.
Then:
bash .claude/scripts/work-orchestrator.sh resume "<group-slug>"
bash .claude/scripts/work-orchestrator.sh resolve "<group-slug>"
The orchestrator's resume refuses while blocked/ is non-empty
(safety guard from PR B), so this only succeeds after every escalation
is resolved. Run resolve afterwards: the user's escalation
resolutions may have edited specs/ADRs in ways that unblock WDs which
were previously BLOCKED in work-resolve's view but never queued.
Notification timing (HIGH #4, 2026-05-11). AskUserQuestion is synchronous within the turn — while the user is answering, in-flight sub-agents may complete and queue notifications. Those notifications are NOT delivered mid-turn; they queue. After 3c's AskUserQuestion loop, when the turn yields, the next turn fires with the queued notifications visible. Step 3a's "extract feature_slug + lookup in-flight" pattern handles multiple completions in one batch.
3d. Termination check
If all of the following are true, the run is complete:
ready(fromwork-orchestrator.sh ready) is emptyin-flight/is emptyblocked/is empty
Go to Step 4 — completion summary.
3e. Dispatch new sub-agents
Compute the ready set:
READY=$(bash .claude/scripts/work-orchestrator.sh ready "<group-slug>")
For each WD in READY (one per line):
Compute the feature slug:
<group-slug>--<wd-id>.Feature-directory preparation — single deterministic approach (HIGH #6, 2026-05-11 — one prescribed strategy, no alternatives). The dispatched sub-agent's pipeline expects
.feature/<feature-slug>/to exist with a seededstatus.md,brief.md, andcycle-log.md./work-startStep 4 does this work. Rather than duplicate it, the /work-run dispatch protocol is: the sub-agent's prompt invokes/work-start "<group-slug>" <wd-id>as the first step, which creates the feature directory AND immediately falls into the single-WD pipeline (/feature-plan→ ... →/feature-refactor)./work-start's claim-via-work-claim.sh sees the WD is already IMPLEMENTING (orchestrator'sdispatchset this state), takes the IMPLEMENTING path, and proceeds. The sub-agent does NOT re-dispatch — /work-start in autonomous mode runs the pipeline inline. This is the contract.Call
work-orchestrator.sh dispatch <group> <wd-id> <feature-slug>(this moves the WD from queue to in-flight). The dispatch MUST happen before the Agent call so that if the Agent call errors synchronously (next step), the orchestrator's in-flight record accurately reflects the attempt.Write a dispatch marker:
bash .claude/scripts/work-dispatch.sh begin "<group-slug>" "<wd-id>"Check for a prior escalation-resolved.md (MEDIUM #3, 2026-05-11). If
.feature/<feature-slug>/escalation-resolved.mdexists from a previous escalation cycle, its contents are appended to the sub-agent prompt so the agent doesn't re-encounter the same ambiguity. Without this, a re-dispatched sub-agent would re-write the same escalation.json → infinite loop.Dispatch the Agent with
run_in_background: true:Prompt for each sub-agent:
You are the dynamic pipeline runner for <feature-slug>. Invoke /work-start "<group-slug>" <wd-id> --nested — the --nested flag tells /work-start to write nested_in_dispatch: true + execution_strategy: cost into status.md (because nested sub-agent contexts cannot dispatch /feature-coordinate's parallel batches). /work-start will then claim the WD via work-claim.sh, create the feature directory at .feature/<feature-slug>/ if not present, and run the single-WD pipeline (/feature-plan → /feature-test → /feature-implement → /feature-refactor → /feature-pr) in automation_mode autonomous with execution_strategy cost (sequential work-unit execution — the orchestrator provides parallelism at the WD level). <If escalation-resolved.md exists, insert here verbatim:> ── Prior escalation resolution ── The user previously resolved a USER-REQUIRED escalation on this WD. Read .feature/<feature-slug>/escalation-resolved.md for their answer before proceeding. Apply the resolution and continue the pipeline. Follow the user-required escalation contract from /work-start: on a NEW USER-REQUIRED escalation, write .feature/<feature-slug>/escalation.json (schema in /work-start's "User-required escalation contract" section), append a `user-escalation — <category>: <question>` entry to cycle-log.md, set status.md substage to `awaiting-user-input`, and halt. Return EXACTLY one line of one of these forms: "<feature-slug>: COMPLETE — <detail>" "<feature-slug>: ESCALATION_AT_<stage> — <category>: <question>" "<feature-slug>: STOPPED_AT_<stage> — <detail>" "<feature-slug>: ERROR — <detail>"Check the Agent tool's immediate return for synchronous errors (HIGH #2, 2026-05-11).
run_in_background: truereturns immediately, but the runtime may reject the dispatch synchronously (max-agents, user-rejected, malformed prompt). If the return indicates a synchronous error rather than "dispatched":- The agent never started; no notification will arrive.
- Call
complete <group> <wd-id> ERROR '<sync error detail>'in the same turn to release the in-flight slot. - Continue dispatching the rest of the ready set.
Do NOT yield without reconciling synchronous-error dispatches — the in-flight record would otherwise wedge the cap-vs-inflight math until the 30-min hung detector caught it.
After all dispatches (or failed dispatches), the loop body completes its turn. For successfully-dispatched agents, the runtime will deliver a system notification when each finishes; that notification triggers a new LLM turn which re-enters Step 3 from the top.
3f. Yield
End the current turn. The skill body has done what it can with the current orchestrator state. Display a compact summary to the user before yielding:
── /work-run tick · <group> ───────────────
ready dispatched this turn: <N>
in-flight: <M>
completed: <P> / <total>
blocked: <Q>
paused: <yes|no>
next: waiting for background sub-agent completion
Step 4 — Completion summary
The orchestrator's queue, in-flight, and blocked sets are all empty. Print the final summary:
bash .claude/scripts/work-orchestrator.sh status "<group-slug>"
Plus a roll-up:
🏁 /work-run complete · <group-slug>
───────────────────────────────────────
Total WDs: <N>
COMPLETE: <n>
STOPPED: <list with WD-id + stage>
ERROR: <list with WD-id + reason>
Wall clock: <last_tick - started_at>
───────────────────────────────────────
Recommended next steps:
- For STOPPED WDs: /feature-resume <feature-slug> to pick up
- For ERROR WDs: inspect .feature/<slug>/ and decide
- Clear orchestrator state when done: work-orchestrator.sh clear <group>
Use AskUserQuestion:
- Title: "Run complete. Clear the orchestrator state?"
- Options:
- Clear now —
work-orchestrator.sh clear <group>+ remove driver.pid - Keep state for inspection — leave
.orchestrator/for later review
- Clear now —
Then drop the driver lock and exit.
Failure modes and recovery
- User pressed ESC during dispatch — the Agent tool returns "user
rejected"; classify as user-stopped; orchestrator marks fail with
reason
user-stopped; user re-invokes/work-run --resumeto continue. The dispatch marker stays as a record. - Background agent crashes (payload-lost) — same recovery as
user-stopped;
complete <wd> ERROR "payload lost". /work-runprocess killed mid-turn — driver.pid remains, lock blocks re-invocation. User confirms the PID is dead (ps), removes the lock, re-invokes. The orchestrator's persistent state is intact.- Sub-agent dispatch marker says begin but no return notification
ever arrived — the agent is wedged.
hungwill eventually catch it via the status.md mtime threshold; user is prompted to mark STOPPED or investigate.
Why this skill exists
/work-start --parallelfires one wave of ready WDs and waits for ALL to complete before exiting. A 10-WD group with one slow outlier pays for the outlier's full duration without using the freed slots./work-rundispatches newly-ready WDs the moment a sibling completes.- The escalation contract from
/work-start(PR A) lets sub-agents signal "I need user input" in a structured way; without/work-run's loop, users would have to manually re-invoke/work-startafter each escalation./work-runautomates the loop: pauses on escalation, surfaces viaAskUserQuestion, resumes cleanly. - The orchestrator state machine (PR B) persists across crashes;
/work-run --resumepicks up where a killed session left off.
This skill is the "press play" wrapper on top of the contract + state machine.