Execute Task - Worker-Coordinated Story Execution
SkillProductivityExecute a single PRD story through coordinated worker phases (Ralph pattern). Each worker handles its domain, passes context to the next, with back-pressure (tests/lint/typecheck) keeping code on rails.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Execute Task - Worker-Coordinated Story Execution skill
What this skill tells your AI
The instructions your AI receives, as published by indigoai-us/hq-core in .claude/skills/execute-task/SKILL.md and read by ahel’s review.
Execute a single user story from a PRD through coordinated worker phases. Each worker handles their domain and passes context to the next via structured handoffs. Back pressure (typecheck, lint, tests) enforces correctness at every phase.
Usage: execute-task {project}/{task-id}
User's input: $ARGUMENTS
Ralph Principle
"Pick a task, complete it, commit it."
- Fresh context per task
- Sub-agents do heavy lifting (Claude Code
Task, Codexspawn_agent) - Back pressure keeps code on rails
- Handoffs preserve context between workers
- Each sub-agent commits its own work before returning
Return Contract (when invoked as a story sub-agent)
Canonical path (orchestrator-dispatched, e.g. /run-project --inline, /run-pipeline, or any caller injecting RETURN CONTRACT: json): emit ONLY this JSON object as the final message:
{
"status": "passed" | "failed" | "blocked",
"story_id": "<id>",
"commits": ["<short-sha>", ...],
"files_changed": <int>,
"back_pressure": {
"tests": "pass" | "fail" | "skip",
"lint": "pass" | "fail" | "skip",
"typecheck": "pass" | "fail" | "skip",
"build": "pass" | "fail" | "skip"
},
"workers_run": ["<worker-id>", ...],
"notes": "<=240 chars; on non-passed status, name the blocker concisely>"
}
Hard formatting rules:
- Final message MUST be exactly the JSON object — nothing before, nothing after.
- No markdown fences. No commentary. No "I've done X" preamble.
notescapped at 240 characters. Anything longer goes toworkspace/threads/journal/<date>/<story-id>.md, not into the return value.
Field semantics:
status:passed= all workers succeeded + back-pressure green + commits landed;failed= back-pressure failure that could not be auto-recovered;blocked= missing spec/credential/dependency prevented completion.commits: short SHAs of all commits created during this execution (git log --onelinedelta since start), in order.files_changed: total files touched across those commits (git diff --statcount).workers_run: real HQ worker IDs in execution order — placeholder values likeworker,general-purpose,commitare rejected by the orchestrator proof gate.notes: 1-2 sentences; on non-passed, the blocker description must be specific enough for the orchestrator to decide retry/skip/halt.
All normal execute-task behavior still runs (task classification, worker sequence, back pressure, commits, prd.json passes:true on success). Only the shape of the final output message changes.
Opt-out (prose mode): when the prompt explicitly includes RETURN CONTRACT: prose, emit the normal human-readable summary. This path is reserved for direct CLI use (no orchestrator parent). Orchestrators MUST inject RETURN CONTRACT: json per ralph-orchestrator-context-discipline (hard policy).
Runtime Adapter: Claude Code Task vs Codex spawn_agent
This skill is runtime-agnostic. Whenever a step says to spawn a worker with the Task tool:
- Claude Code: use
Task({subagent_type: "general-purpose", ...})exactly as written. - Codex: use
spawn_agent({agent_type: "worker", ...}), thenwait_agent(...), with the same prompt and output contract. Useagent_type: "explorer"only for read-only discovery/planning phases.
Codex worker prompts MUST include this coordination block:
You are not alone in the codebase. Own only this worker phase for {task.id};
do not revert edits made by others; adapt to any existing changes you find.
Commit your phase work before returning. If your runtime returns an integration
patch instead of a parent-visible commit, say so and list every changed path.
Return only the requested JSON.
The orchestrator still enforces the same proof gates in both runtimes: parseable JSON, real worker IDs in the handoff/summary, back-pressure status, parent-visible commits or parent-created integration commits, lock release, and passes:true only after a successful story.
Codex nesting rule: when /execute-task is invoked from a Codex sub-agent, spawn_agent / wait_agent are not available inside that sub-agent. Do not fake the worker sequence. /run-project in Codex must avoid this by running a filesystem-mediated worker-phase loop: the parent writes small phase input envelopes under workspace/orchestrator/{project}/executions/{story-id}/, spawns one fresh top-level Codex worker per phase, and absorbs compact phase JSON only. Phase workers load PRD/project/worker/policy/repo context themselves from file paths. Direct /execute-task {project}/{task-id} from the Codex parent session may still use the normal Codex adapter because the parent has spawn_agent.
If Codex spawn_agent / wait_agent are unavailable, do not fake the worker sequence in the parent; stop and ask the user to use --session-mode for direct parent execution or resume in a runtime with Codex sub-agent support. Do not route Codex-triggered story execution through the Claude headless builder.
Work Mesh Live — trusted bind (do this first)
Before any other tool call that touches project work, bind the session per
.claude/skills/_shared/work-mesh-live-bind.md (US-011):
bash core/scripts/work-mesh-live-bind-trusted.sh \
--company "{co}" --project "{project}" --task "{task}"
Omit --task when unknown. This writes workspace/sessions/<sid>/meta.yaml
and reconciles with observation.trustedContext (no --trusted CLI flag).
Process
1. Parse Arguments
Extract {project}/{task-id} from $ARGUMENTS.
Split on /: first token is project, second is task-id.
If no arguments or missing parts:
Usage: /execute-task {project}/{task-id}
Example: /execute-task campaign-migration/CAM-003
Stop here.
1.5 Trusted session bind (US-011)
Once {project} and {task-id} are parsed (and company {co} is known from
the PRD path companies/{co}/projects/... or session meta), bind immediately:
bash core/scripts/work-mesh-live-bind-trusted.sh \
--company "{co}" --project "{project}" --task "{task-id}"
Do this before loading workers or editing files.
2. Load Task Spec
Resolve project location using qmd search (never Glob for prd.json):
qmd search "{project} prd.json" --json -n 5
From results, find the entry whose path includes /{project}/prd.json. If qmd is unavailable or returns nothing, fall back to direct Read at these paths in order:
companies/{co}/projects/{project}/prd.json(company projects)personal/projects/{project}/prd.json(personal/HQ projects)
If no prd.json found:
ERROR: prd.json not found for {project}. Run /plan {project} first.
Stop.
Read prd.json. Validate structure strictly — no fallbacks:
-
userStories array required: If
userStoriesis missing or not an array:ERROR: prd.json missing userStories array. Migrate legacy 'features' key to 'userStories'.Stop.
-
Required fields per story: Each story must have
id,title,description,passes. Report any story with missing fields and stop. -
Find the target story: Match
task-idagainststory.id. If not found:Task {task-id} not found in {project} prd.json.Stop.
-
Check completion: If
story.passes === true:Task {task-id} already complete (passes: true). Skipping.Stop.
-
Check dependencies: If the story has
dependsOn, verify each dependency story haspasses: true. If any dependency is incomplete:Task {task-id} blocked: depends on {dep-id} which is not yet complete.Stop.
Extract from the matched story:
id,title,descriptionacceptance_criteria(oracceptanceCriteria)files(for file locking)dependsOn(already checked)e2eTests(for acceptance-test-writer phase)worker_hints(optional worker inclusion)model_hint(story-level model override)codex_model_hint(Codex CLI model override)linearIssueId(for Linear sync)
Also record from prd.metadata:
company— active company slugrepoPath— target repo pathlinearCredentials,linearInProgressStateId,linearDoneStateId,linearReviewersqualityGates— custom quality gate commandsdocsPath— documentation location
2.5 Check Codex CLI Availability
For any code-related task type, pre-flight check whether the Codex CLI is installed:
which codex >/dev/null 2>&1
- If available: set
codex_available = true - If unavailable: set
codex_available = false, warn:Warning: Codex CLI not found. codex-reviewer will run as Claude-only review (no Codex model).
This enables graceful degradation — the pipeline continues without Codex but logs a warning.
2.6 Check Story Checkout State
Guard against concurrent execution of the same story.
-
Load config: Read
core/settings/orchestrator.yaml→checkout.enabledandcheckout.stale_timeout_minutes(defaults:true,30). -
Skip if disabled: If
checkout.enabled: false, skip this step entirely and proceed to step 3. -
Read state.json: Read
workspace/orchestrator/{project}/state.json. If missing, skip — no checkout to check. -
Check for existing checkout: If
current_task.idmatches this story ANDcurrent_task.checkedOutByis not null:a. Extract checkout info:
checkedOutPid = state.current_task.checkedOutBy.pid checkedOutSession = state.current_task.checkedOutBy.sessionId checkedOutAt = state.current_task.checkedOutBy.startedAtb. Check if PID is alive:
kill -0 {checkedOutPid} 2>/dev/nullc. If PID is ALIVE — ask the user via AskUserQuestion:
Story {task.id} is currently checked out by PID {checkedOutPid} (session: {checkedOutSession}, started: {checkedOutAt}). Another /execute-task may be running. Proceed anyway (override) or abort? Options: ["Proceed anyway", "Abort"]- If user chooses Abort: stop immediately
- If user chooses Proceed anyway: log warning and continue
d. If PID is DEAD — release the stale checkout:
- If
checkedOutAtis null/empty: release unconditionally. Setcurrent_task.checkedOutBy = null, updateupdated_at. Warn "Released checkout held by dead PID {checkedOutPid} (no timestamp)". Proceed normally. - If
checkedOutAtis present: compute age =now() - Date.parse(checkedOutAt). Dead PID = safe to take over regardless. Setcurrent_task.checkedOutBy = null, updateupdated_at. Warn with age vsstale_timeout_minutes * 60. Proceed normally.
-
If no conflict: proceed normally.
Report:
Checkout check: clear (no active checkout for {task.id})
or after release:
Checkout check: released dead checkout for {task.id}, proceeding
3. Classify Task Type
Analyze the story's title, description, and acceptance criteria. Match against patterns:
| Type | Indicators |
|---|---|
schema_change | database, migration, schema, table, column, prisma, SQL |
api_development | endpoint, API, REST, GraphQL, route, service |
ui_component | component, page, form, button, React, UI, responsive |
full_stack | Combination of backend + frontend indicators |
codex_fullstack | codex, AI-generated, codex-powered full stack |
enhancement | animation, polish, refactor, optimization, UX |
content | copy, content, documentation, marketing text |
Codex worker routing:
- codex-reviewer is mandatory for all code task types (schema_change, api_development, ui_component, full_stack, codex_fullstack, enhancement). Always included after code-reviewer.
- codex-coder and codex-debugger remain optional — included when
worker_hintsor task indicators match:
| Pattern | Worker | Inclusion |
|---|---|---|
| "codex", "AI-generated" | codex-coder | Optional (hints match) |
| "auto-fix", "debug recovery" | codex-debugger | Optional (hints match) |
Report classification:
Task: {task.id} - {task.title}
Type: {type} (matched: {indicators})
4. Select Worker Sequence
Based on task type, determine the worker sequence:
schema_change:
- product-planner (if spec unclear)
- database-dev
- backend-dev
- acceptance-test-writer (if e2eTests non-empty)
- code-reviewer
- codex-reviewer
- dev-qa-tester
api_development:
- product-planner (if spec unclear)
- backend-dev
- codex-coder (optional, if worker_hints include codex)
- acceptance-test-writer (if e2eTests non-empty)
- code-reviewer
- codex-reviewer
- codex-debugger (optional, before QA if back-pressure issues)
- dev-qa-tester
ui_component:
- product-planner (if spec unclear)
- frontend-dev
- codex-coder (optional, if worker_hints include codex)
- motion-designer
- acceptance-test-writer (if e2eTests non-empty)
- code-reviewer
- codex-reviewer
- codex-debugger (optional, before QA if back-pressure issues)
- dev-qa-tester
full_stack:
- product-planner
- architect
- database-dev
- backend-dev
- frontend-dev
- codex-coder (optional, if worker_hints include codex)
- acceptance-test-writer (if e2eTests non-empty)
- code-reviewer
- codex-reviewer
- codex-debugger (optional, before QA if back-pressure issues)
- dev-qa-tester
codex_fullstack:
- product-planner (if spec unclear)
- architect
- database-dev
- codex-coder
- acceptance-test-writer (if e2eTests non-empty)
- codex-reviewer
- dev-qa-tester
content:
- content-brand
- content-product
- content-sales
- content-legal
enhancement:
- (relevant dev based on files)
- acceptance-test-writer (if e2eTests non-empty)
- code-reviewer
- codex-reviewer
- codex-debugger (optional, if auto-fix needed)
Sequence rules:
- Skip product-planner if the story already has detailed acceptance criteria.
- Skip acceptance-test-writer if
e2eTestsis empty or absent. - Filter by active workers — check
core/workers/registry.yamland skip any worker whosestatusis notactive.
Worker phase descriptions (for execution plan display):
| Worker | Phase Description |
|---|---|
| product-planner | Clarify spec and acceptance criteria |
| architect | Design system architecture |
| database-dev | Implement schema and migrations |
| backend-dev | Implement backend service |
| frontend-dev | Implement frontend UI |
| codex-coder | Generate code via Codex AI |
| motion-designer | Add animations and motion |
| code-reviewer | Review changes (Claude-based) |
| codex-reviewer | Mandatory second-opinion review via Codex AI |
| acceptance-test-writer | Write story-level acceptance tests from e2eTests |
| codex-debugger | Auto-fix issues via Codex AI |
| dev-qa-tester | Verify implementation |
| content-brand | Brand-aligned content creation |
| content-product | Product content and documentation |
Present the execution plan:
Execution Plan for {task.id}:
Phase 1: {worker} -> {phase description}
Phase 2: {worker} -> {phase description}
Phase 3: {worker} -> {phase description}
Phases: {N} | Type: {type}
Proceed? [Y/n]
5. Initialize Execution State
Create the execution tracking directory and file:
mkdir -p workspace/orchestrator/{project}/executions
Write to workspace/orchestrator/{project}/executions/{task-id}.json:
{
"task_id": "{task.id}",
"project": "{project}",
"started_at": "{ISO8601}",
"status": "in_progress",
"current_phase": 1,
"phases": [
{"worker": "{worker1}", "status": "pending"},
{"worker": "{worker2}", "status": "pending"}
],
"handoffs": [],
"codex_debug_attempts": []
}
5.0.5 Acquire Story Checkout
If checkout.enabled: true (from core/settings/orchestrator.yaml):
-
Read state.json: Read
workspace/orchestrator/{project}/state.json. If missing, create with minimal structure:{"version":1,"current_task":{},"updated_at":"{ISO8601}"}. -
Write checkout entry into
current_task.checkedOutBy:{ "pid": {current_process_pid}, "startedAt": "{ISO8601}", "sessionId": "{started_at from step 5}" }Also ensure
current_task.idis set to this story's ID andupdated_atis refreshed.Getting the PID: Run
echo $$in bash to get the current shell's PID. Use that value as thepidfield. -
Write state.json back with the updated
checkedOutByblock. -
Report:
Checkout acquired for {task.id} (PID: {pid})
If checkout.enabled: false: skip this step silently.
5.1 Audit: Task Started
core/scripts/audit-log.sh append \
--event task_started \
--project {project} \
--story-id {task.id} \
--company {company} \
--session-id {started_at} \
--action "Task execution started: {task.title}" || true
5.5 Acquire File Locks
If the story has a non-empty files array and prd metadata has repoPath:
- Load config: Read
core/settings/orchestrator.yaml→file_locking. - Skip if disabled: If
file_locking.enabled: false, skip this step entirely. - Read existing locks: Read
{repoPath}/.file-locks.json(create if missing:{"version":1,"locks":[]}). - Stale lock cleanup: For each existing lock, check if owner PID is running via
kill -0 {pid} 2>/dev/null. If not running AND lock is older thanstale_lock_timeout_minutes, remove it. - Conflict check: For each file in
task.files:- Self-owned lock: If already locked by the SAME story ID, skip it (orchestrator may have pre-acquired locks for swarm mode).
- Conflict with DIFFERENT story: Apply
conflict_modefrom config:hard_block: STOP — report conflicting files + owner story, exit with{"status":"blocked","blocked_by":[...]}soft_block: Log warning, proceed but addlocked_filesto worker context so workers skip themread_only_fallback: Log warning, proceed withread_only_filesin worker context
- Acquire locks: For each unlocked file, append to
.file-locks.json:
(Get PID via{"file": "{path}", "owner": {"project": "{project}", "story": "{task.id}", "pid": {$$}}, "acquired_at": "{ISO8601}"}echo $$in bash.) - Update state.json: Update project's
checkedOutFiles:[{"file": "{path}", "story": "{task.id}", "repo": "{repoPath}"}]
Report: File locks acquired: {N} files for {task.id}
5.5.5 Sync Linear Issue to In Progress (Best-Effort)
If the story has linearIssueId and prd metadata has linearCredentials:
-
Cross-company guard: Before using
linearCredentials, verify the path matches the active company percompanies/manifest.yaml. If it points to a different company's settings, ABORT Linear sync and warn. -
Read API key:
LINEAR_KEY=$(cat {prd.metadata.linearCredentials} | python3 -c "import sys,json; print(json.load(sys.stdin)['apiKey'])") ISSUE_ID="{task.linearIssueId}" IN_PROGRESS_STATE="{prd.metadata.linearInProgressStateId}" -
Set issue to In Progress:
curl -s -X POST https://api.linear.app/graphql \ -H "Content-Type: application/json" \ -H "Authorization: $LINEAR_KEY" \ -d "{\"query\": \"mutation { issueUpdate(id: \\\"$ISSUE_ID\\\", input: { stateId: \\\"$IN_PROGRESS_STATE\\\" }) { success } }\"}" -
Comment on issue: "Started by HQ — task in progress."
Skip silently if no linearIssueId or no credentials configured. Never block execution on Linear sync failure.
5.6 Load Applicable Policies
Load policies via frontmatter-only gate. Use bash core/scripts/read-policy-frontmatter.sh {file} for each policy file — this reads frontmatter only (not full body), keeping context lean.
-
Company policies: Determine the active company from
prd.metadata.companyor manifest repo lookup. Read frontmatter for each file incompanies/{co}/policies/(skipexample-policy.md). For any policy withenforcement: hardwhosetriggermatches the current task, additionally read its## Rulesection via targeted Read + range. -
Repo policies: If working inside a repo, check
{repoPath}/.claude/policies/if it exists. Same frontmatter-only pattern. -
Global policies: Policy digests (
core/policies/_digest.md) are retired. Filter policies incore/policies/by frontmattertriggerviabash core/scripts/read-policy-frontmatter.sh {file}— don't load all. SessionStart injects matching policies viainject-policy-on-trigger; do not look for a digest file.
Include applicable policy rules in worker prompts (step 6b) under ### Applicable Policies.
Enforcement distinction:
- Hard enforcement policies are absolute constraints — workers must not violate them.
- Soft enforcement policies allow deviation but require logging.
6. Execute Each Phase
For each worker in the sequence, spawn a sub-agent via the Task tool. Each sub-agent runs in its own isolated context window, commits its work before returning, and passes a structured handoff forward.
6a. Load Worker Config
-
Read
core/workers/registry.yaml(auto-generated, read-only index) to find the worker path:grep -A 4 " - id: {worker-id}$" core/workers/registry.yaml | grep "path:"Extract the
path:value. This may resolve tocore/workers/public/dev-team/{worker-id}/,core/workers/public/{worker-id}/, orcompanies/{co}/workers/{worker-id}/. -
Read
{worker_path}/worker.yamlto get:instructions— worker's role, process, and accumulated learningscontext.base— files the worker always needsskills.installed— worker's skillsverification.post_execute— back-pressure commandsexecution.model— model tier for this worker (opus/sonnet/haiku)execution.codex_model— OpenAI model for Codex CLI invocations (codex workers only)
-
Resolve model for this phase:
model = task.model_hint || worker.execution.model || "opus"Story-level
model_hint(from prd.json) overrides worker default. Fallback: opus. -
Resolve Codex model (for codex workers only):
codex_model = task.codex_model_hint || worker.execution.codex_model || "gpt-5.4"Story-level
codex_model_hintoverrides worker default. Fallback: gpt-5.4. -
If the worker has a skill file relevant to the task, note its path so the sub-agent prompt can reference it.
6b. Build Worker Prompt
Construct the sub-agent prompt as a single markdown block:
## You are: {worker.name}
## Task: {task.id} - {task.title}
### Description
{task.description}
### Acceptance Criteria
{task.acceptance_criteria as checklist}
### Files to Focus On
{task.files or inferred from description}
### Context from Previous Phase
{handoff_context from previous worker, if any}
### Codebase Exploration
If the target repo has a qmd collection (check `qmd status`), prefer `qmd vsearch "<concept>" -c {collection} --json -n 10` for conceptual search (e.g. "where is auth handled", "billing service pattern"). Use Grep only for exact pattern matching (specific imports, function references, string literals).
### Applicable Policies
{policies loaded in step 5.6, if any}
### Codex CLI Model (codex workers only)
{worker.execution.codex_model || "gpt-5.4"} — pass via `-c model="{codex_model}"` to all codex exec/review commands
### Your Instructions
{worker.instructions}
### Back Pressure (Run Before Completing)
{worker.verification.post_execute commands}
# Repo-specific back-pressure checks may be added here if needed
# e.g. coverage checks, manifest freshness, etc.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 84
- Forks
- 15
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
execute-task-indigoai-us- Source
- github.com/indigoai-us/hq-core