| name | execute |
| description | Execute the approved plan. Orchestrates execution graph segments through specialized executor agents, including dependency management and error recovery. |
dynos-work: Execute Skill
Manages the parallel execution of the implementation plan via the execution graph. Handles dependency ordering, executor spawning, and final integration.
Command Funnel (applies to every command in this skill)
Every deterministic step below runs through the plugin CLI. Resolve it once at the start of the skill and substitute the ABSOLUTE path literally into each command you run (permission prefix-matching operates on literal command text):
PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
if [ -z "$PLUGIN_ROOT" ]; then
echo "Set CODEX_PLUGIN_ROOT or CLAUDE_PLUGIN_ROOT to the dynos-work plugin root." >&2
exit 2
fi
DYNOS="${PLUGIN_ROOT}/bin/dynos"
"$DYNOS" ctl <subcommand> wraps hooks/ctl.py; "$DYNOS" hook <script> ... wraps helper scripts (router, lib_tokens, build_prompt_context, ...) with PYTHONPATH handled internally. A permissions-ON user can allow the single <plugin-root>/bin/dynos prefix once instead of approving every call.
JSON payloads (classification, execution-graph, repair-log) are piped to the ctl wrapper over stdin with --from - and a heredoc — NEVER staged at /tmp or any raw filesystem path (the write policy denies those, by design). Temp files, when genuinely needed, belong in .dynos/task-{id}/_scratch/.
Ruthlessness Standard
- No silent shortcuts.
- No executor prompt may omit learned rules or prevention rules when they exist.
- A segment is not done because code was written; it is done when the required behavior is proven.
- If verification is weak, the segment is weak.
- If a segment can pass with shallow evidence, the prompt is too soft.
- No executor gets to hide behind “close enough.”
What you do
Step 0 — Contract validation
Validate that all required inputs from the start skill are present:
"$DYNOS" ctl validate-contract --skill execute --task-dir .dynos/task-{id}
If validation fails with missing required inputs, print the errors and stop. Do not proceed with execution.
Step 1 — Review the Plan
Read spec.md, plan.md, and execution-graph.json. Ensure you understand the dependency chain.
Step 2 — Snapshot the Base
Before any modification starts:
- Create a temporary git branch and return to the working branch:
git checkout -b dynos/task-{id}-snapshot && git checkout -
- Record the snapshot SHA into manifest.snapshot via the audited ctl wrapper:
"$DYNOS" ctl record-snapshot .dynos/task-{id}
The command auto-detects HEAD SHA and branch from git. It accepts optional
--head-sha SHA and --branch NAME to override. Stage gate refuses anything
at or beyond EXECUTION. Re-run with the same SHA is a no-op
(status: already_recorded); a different SHA is refused.
Read the printed JSON for head_sha and branch. Append to log:
{timestamp} [DECISION] snapshot recorded — branch {branch} at {head_sha}
Two-Phase Execution for Oversized Segments
When a segment in the execution graph is marked with two_phase: true (indicating its weighted budget hit the ceiling per AC 6), the segment requires two-phase A/B split execution under write-first discipline:
-
Spawn A (read-only): Spawn A is a read-only analysis pass that produces a precise change-list artifact at evidence/<seg-id>-change-list.md. This change-list is the authoritative specification for all writes spawn B will perform.
-
Spawn B (write phase): Spawn B reads the change-list generated by spawn A as its sole work specification. Spawn B applies all changes under write-first discipline: create the evidence file early with the change-list reproduced verbatim, then execute each item in sequence.
The two-phase separation ensures oversized segments remain verifiable and transparent. The literal strings two_phase: true, change-list, and write-first mark this requirement in the execution graph and in spawn instructions.
Step 3 — Execute segments (Optimized Scheduler)
Run deterministic execute setup first:
"$DYNOS" ctl run-execute-setup .dynos/task-{id}
This command runs execution preflight validation, advances PRE_EXECUTION_SNAPSHOT -> EXECUTION, builds the executor plan, and writes the executor-routing receipt. Use its JSON output directly.
Inline vs spawn decision (authoritative):
The inline branch is permitted ONLY when every cell in row 1 of the table below is satisfied. Any deviation forces the spawn path. Read the executor plan router output first, then look up the row that matches manifest.fast_track, the number of segments in execution-graph.json, and the segment's route_mode + agent_path from the router's response.
| fast_track | n_segments | route_mode | agent_path | path_taken |
|---|
| true | 1 | generic | null | inline (no subagent spawn) |
| true | 1 | replace | any non-null | spawn (inline FORBIDDEN) |
| true | 1 | alongside | any non-null | spawn (inline FORBIDDEN) |
| false OR n_segments > 1 | any | any | any | spawn |
If any segment has route_mode in {replace, alongside} with non-null agent_path, the inline branch is FORBIDDEN — take the spawn path.
Inline execution procedure (only when the decision table row says inline): Inline execution skips the executor subagent entirely and runs the segment in this thread. This avoids the ~30K token overhead of agent context setup, but it does NOT relax any other rule. Even in the inline branch, you MUST still run the router, write the executor-routing receipt, and apply the prompt-injection/prevention pipeline to your own work:
- Run the executor plan router:
"$DYNOS" hook router executor-plan --root . --task-type {task_type} --graph .dynos/task-{id}/execution-graph.json
- Write the executor-routing receipt (required for stage transitions).
- Re-verify the decision table row. If the router's response contains any segment with
route_mode in {"replace", "alongside"} with a non-null agent_path, STOP — the inline branch is FORBIDDEN for this task. Fall through to the spawn path below.
- Run
inject-prompt with your base prompt to get the complete prompt with prevention rules. Apply those rules to your own work.
- Log the routing decision:
{timestamp} [ROUTE] {executor} model={model} route={route_mode} source={route_source}
- Transition the manifest stage
PRE_EXECUTION_SNAPSHOT → EXECUTION (mandatory — without this the Step 4 transition to TEST_EXECUTION is illegal per ALLOWED_STAGE_TRANSITIONS). transition_task auto-appends the [STAGE] → EXECUTION log line; do not write it manually:
"$DYNOS" ctl transition .dynos/task-{id} EXECUTION
Then read the segment, extract the criteria from spec.md, make the code changes yourself, write evidence, and proceed to Step 4. Log: {timestamp} [INLINE] seg-1 — fast-track inline execution (no subagent spawn).
Inline execution does not relax standards. It removes spawn overhead, not rigor.
Skipping the router in inline mode silently ignores learned agents and breaks the self-learning feedback loop.
Normal execution (fast_track is false or >1 segment):
run-execute-setup already performed deterministic preflight validation and stage advancement. If it failed, repair the plan first; do not patch around a broken plan during execution.
Immediately compute the authoritative execution schedule:
"$DYNOS" ctl run-execution-batch-plan .dynos/task-{id}
This command is authoritative for:
- segment state:
completed | cached | pending
- cache eligibility and cache rejection reasons
- dependency depth / critical path
- next runnable batch and remaining batches
Do NOT re-derive dependency depth, cache reuse, or runnable batches in prompt logic. Consume the JSON output and follow it.
If cached_segments is non-empty, treat those segments as already satisfied and reuse their evidence files. Append to log: {timestamp} [CACHE] {segment-id} — skipping (inputs unchanged).
If next_batch is empty while pending_segments is non-empty, stop and fail the task as blocked. That means dependency resolution or receipt state is inconsistent; do not guess.
Progressive auditing remains allowed, but only after a segment is deterministically known complete or cached.
Deterministic routing (MANDATORY): Use the segments payload returned by run-execute-setup as the authoritative executor plan. Do not rebuild routing in prompt logic. The executor-routing receipt has already been written.
Do NOT read project_rules.md tables manually. The router handles model policy, agent routing, and security floors deterministically.
Learned Agent Injection (MANDATORY — NOT OPTIONAL): For every segment, you MUST build the executor prompt using the deterministic prompt builder. This is not a suggestion. This is an enforcement gate. For each segment in the executor plan:
echo "{your base prompt for this segment}" | "$DYNOS" hook router inject-prompt --root . --task-type {task_type} --graph .dynos/task-{id}/execution-graph.json --segment-id {seg-id}
This command:
- Reads the base prompt from stdin
- Looks up the routing decision for this segment (model, route_mode, agent_path)
- If
route_mode is replace or alongside, reads the learned agent .md file and appends its rules to the prompt
- Appends prevention rules from the project's history
- Logs a
learned_agent_applied event to .dynos/events.jsonl
- Prints the complete prompt to stdout
Routing cache (automatic — no extra steps): executor-plan writes its result to .dynos/task-{id}/router-cache/executor-plan.json keyed by a fingerprint over every input that drives the plan (graph, policy, effectiveness scores, retrospectives, learned registry, benchmark history, prevention rules). Each inject-prompt call checks this cache first; on a fingerprint match it reuses the cached entry instead of re-deriving routing. This guarantees the model the executor was spawned under matches the model the prompt was injected for, even with epsilon-greedy exploration enabled. Cache lookups emit a router_cache_lookup event with status: hit | fingerprint_drift | stale_segment. Inspect freshness with:
"$DYNOS" hook router router-cache-status --root . --task-type {task_type} --graph .dynos/task-{id}/execution-graph.json
If status is stale, re-run executor-plan before continuing inject-prompt calls — otherwise per-segment routing will silently fall back to live builds.
Use the OUTPUT of this command as the prompt for the Agent tool spawn. Do NOT construct the executor prompt yourself. Do NOT skip this step. If you spawn an executor without running inject-prompt first, the learned agent is silently ignored and the whole self-learning system is broken.
Per-spawn tool budget (since 2026-05-06): The injected prompt now includes a ## Tool-Use Budget block specifying the per-spawn tool-call budget computed deterministically from the segment's files_expected count and the resolved model (via hooks/lib_tool_budget.py::compute_segment_budget). The executor agent self-paces against that value. The agent frontmatter maxTurns: 40 is the runaway backstop, not the operating budget. Operators should expect executor self-pacing to track segment scope, not the model tier.
Sidecar capture (MANDATORY). cmd_inject_prompt writes the SHA-256 of the exact bytes it printed to stdout to .dynos/task-{id}/receipts/_injected-prompts/{seg-id}.sha256 (single-line lowercase hex digest, no trailing newline) plus a companion .txt of the same bytes. Both files are written atomically via tempfile + os.replace, so a retry overwrites cleanly. Immediately after inject-prompt returns, read the sidecar:
INJECTED_PROMPT_SHA256=$(cat .dynos/task-{id}/receipts/_injected-prompts/{seg-id}.sha256)
You MUST pass this captured digest to receipt_executor_done(...) below as injected_prompt_sha256. The receipt writer asserts the same sidecar exists and matches; a mismatch raises ValueError with the literal substrings injected_prompt_sha256 sidecar missing or injected_prompt_sha256 mismatch.
If the injected prompt is weak, strengthen the base prompt before spawning. Do not accept vague executor instructions.
If inject-prompt fails or is unavailable, stop and fix the deterministic routing path. Do NOT manually read agent_path or hand-build the learned-agent prompt.
Role granting (MANDATORY — BEFORE every Agent tool spawn): Grant the executor role via the deterministic ctl wrapper immediately before spawning each executor subagent. The wrapper enforces the role allowlist; the grant is single-use and is consumed by the spawned subagent's first tool call (the orchestrator's own session is pinned at SessionStart and can never consume a grant — that closes the role-leak behind the 2026-04-30 audit-chain forgery incident; the audit-* forgery defense itself is the spawn-log cross-check at receipt time). Direct writes to active-segment-role, role-grants.json, or role-bindings.json are denied by write_policy.py and surface a wrapper hint.
"$DYNOS" ctl stamp-role .dynos/task-{id} --role "{executor-role}"
Where {executor-role} is the exact role string from the executor plan (e.g. backend-executor, ui-executor, integration-executor). The write MUST complete (exit 0) before the Agent tool invocation that spawns the subagent — this is a sequential constraint, not a timing one. Exit 1 means the wrapper refused; the stderr names the cause (audit-* refusal, role outside allowlist, missing task dir).
Model selection: Pass the model field from the executor plan as the model parameter when spawning the agent. If model is null, use default (omit the model parameter).
TDD-First Awareness: Check if .dynos/task-{id}/evidence/tdd-tests.md exists. If it does, include this instruction in the base prompt (before piping through inject-prompt): "A TDD test suite has already been committed. Your implementation must make those tests pass. Do NOT write new tests or modify existing test files."
Spawn budget check (MANDATORY — BEFORE every batch):
Before issuing each parallel next_batch executor batch (this includes the first batch AND every subsequent next_batch — the check runs once per spawn cycle, before EACH batch, not only the first), run:
"$DYNOS" ctl check-spawn-budget .dynos/task-{id}
Parse the JSON output. The command emits a single-line JSON object with keys status, count, threshold, exempt_count, and task_class. If status is 'paused' or 'already_paused':
- Write
escalation.md in the task directory containing the full JSON output and a human-readable explanation that the spawn budget was exceeded — name the executor count, the threshold, the task_class, and instruct the operator to run "$DYNOS" ctl spawn-resume .dynos/task-{id} --reason "<≥20-char rationale>" after diagnosing why spawns were wasted.
- Append a line beginning with
[BUDGET-PAUSE] to execution-log.md that records the timestamp, count, and threshold.
- Exit non-zero.
Do not proceed with spawning executors. The pause is intentional — the policy is calibrated from this project's retrospective history, and the wasted-spawn count has crossed the learned threshold.
Spawn the next_batch executor agents in parallel.
Executor agents by type:
ui-executor, backend-executor, ml-executor, db-executor, refactor-executor, testing-executor, integration-executor, docs-executor, infra-executor, security-executor, data-executor, observability-executor, release-executor.
The base prompt for each executor (before inject-prompt) must include:
- Its specific segment object from
execution-graph.json
- The full text of each acceptance criterion referenced by the segment's
criteria_ids field, extracted from spec.md
- Evidence files from dependency segments: for each segment ID in the executor's
depends_on list, read .dynos/task-{id}/evidence/{dependency-segment-id}.md and include its contents
- Instruction to write evidence to
.dynos/task-{id}/evidence/{segment-id}.md
- Pre-loaded file contents (MANDATORY): Run the context builder for each file in the segment's
files_expected and append its output to the base prompt:
"$DYNOS" hook build_prompt_context --root . {files_expected_1} {files_expected_2} ...
Append the printed output verbatim to the base prompt. This gives the executor the current file contents upfront so it does not need to call Read at the start, keeping the context window small. If a file does not exist yet (new file), the script emits a placeholder — include it anyway so the executor knows the file is new.
Do NOT pass the full spec.md or plan.md to executors.
Segment finalization (MANDATORY): After each executor completes, finalize the segment through ctl instead of hand-writing receipt / ownership / manifest logic:
"$DYNOS" ctl run-execution-segment-done \
.dynos/task-{id} \
"{seg-id}" \
--injected-prompt-sha256 "{INJECTED_PROMPT_SHA256 captured from sidecar}" \
--model "{model from plan or null}" \
--agent-name "{agent_name from plan or None}" \
--executor-type "{executor from plan}" \
--evidence-path ".dynos/task-{id}/evidence/{seg-id}.md" \
--tokens-used {total_tokens from Agent result or None}
This command deterministically:
- verifies segment ownership against
execution-graph.json
- verifies the evidence file exists
- writes
receipt_executor_done(...)
- updates
manifest.json.execution_progress from deterministic execution state
Role cleanup (MANDATORY — AFTER run-execution-segment-done writes the receipt): Clear unconsumed role grants immediately after the segment receipt is written. This prevents a stale grant from leaking into any subsequent segment's spawn. Do NOT rm the role file by hand — active-segment-role is wrapper-required and the deletion is denied by write_policy; clear-role is the sanctioned (privilege-reducing) path:
"$DYNOS" ctl clear-role .dynos/task-{id}
After each batch (or cached resolution) completes, record events and verify:
- Token capture (executor spawn): automatic. The
SubagentStop hook
(wired in hooks.json) parses the subagent's transcript and writes the
spawn record to the active task's token-usage.json via
hooks/lib_tokens_hook.py. Do NOT also emit a manual --type spawn
lib_tokens.py record call here — it would double-count the tokens.
(The --type deterministic records below are NOT covered by SubagentStop
and remain the orchestrator's responsibility.)
- Token capture (deterministic checks): After file ownership and evidence verification:
"$DYNOS" hook lib_tokens record \
--task-dir .dynos/task-{id} \
--agent "ctl-check-ownership" \
--model "none" \
--input-tokens 0 \
--output-tokens 0 \
--phase execution \
--stage "EXECUTION" \
--type deterministic \
--segment "{segment-id}" \
--detail "Verified file ownership and evidence — {pass|fail}"
- Also record the router decision before each batch:
"$DYNOS" hook lib_tokens record \
--task-dir .dynos/task-{id} \
--agent "router-executor-plan" \
--model "none" \
--input-tokens 0 \
--output-tokens 0 \
--phase execution \
--stage "EXECUTION" \
--type deterministic \
--detail "Router: {executor}={model} route={route_mode}"
- Use
run-execution-segment-done as the authoritative completion gate.
- Append to log:
{timestamp} [DONE] {segment-id} — complete.
- Re-run
run-execution-batch-plan to get the next authoritative batch.
For inline fast-track execution (no subagent spawn), record with --type inline:
"$DYNOS" hook lib_tokens record \
--task-dir .dynos/task-{id} \
--agent "inline-executor" \
--model "none" \
--input-tokens 0 \
--output-tokens 0 \
--phase execution \
--stage "EXECUTION" \
--type inline \
--segment "seg-1" \
--detail "Fast-track inline execution (no subagent spawn)"
Test execution (Step 4): Record test runner events with --phase execution --stage TEST_EXECUTION.
When run-execution-batch-plan reports no pending segments, advance through ctl:
"$DYNOS" ctl run-execution-finish .dynos/task-{id}
This command refuses the transition if any segment is still pending, AND it runs the full evidence verification (non-empty evidence/{seg-id}.md per segment, every files_expected entry on disk) inside the stage gate before advancing to TEST_EXECUTION. If it reports "status": "blocked" with failures, address each listed failure and re-run. Do not manually decide that execution is complete.
Step 4 — Run tests
Read plan.md Test Strategy. Run the specified tests. Use incremental testing if the framework supports it (e.g. jest --onlyChanged).
If an approved TDD suite exists, run those tests first as the blocking contract suite. If they pass, run any broader regression suite required by plan.md.
If tests pass:
- Append to log:
{timestamp} [DONE] tests — passed
- Continue to Audit
If tests fail:
- Append to log:
{timestamp} [FAIL] tests — failed. Repairs required.
- Update
manifest.json stage to REPAIR_PLANNING (legal per ALLOWED_STAGE_TRANSITIONS["TEST_EXECUTION"]; the older single REPAIR stage no longer exists)
- Hand off to
/dynos-work:audit — its Step 4 owns the two-phase repair loop. There is no standalone repair skill.
Step 5 — Verify completion
After successfully completing execution and tests (and any repairs triggered by tests or audit), re-run the deterministic evidence verifier as confirmation:
"$DYNOS" ctl run-execution-verify-evidence .dynos/task-{id}
This command checks every non-cached segment's evidence file exists and is non-empty, and that every files_expected entry exists on disk. It is legal at both EXECUTION and TEST_EXECUTION (the same verification already ran inside the run-execution-finish stage gate in Step 3 — this re-run confirms nothing regressed during test execution). It exits non-zero with a JSON error payload if any check fails. Do NOT assert completion in prompt logic — consume this command's output as the authoritative pass/fail signal.
If the command reports "status": "blocked", address each listed failure before proceeding.
Append to log:
{timestamp} [DONE] execute — all segments verified by ctl (segments_verified={N})
Handoff — Write handoff record
After Step 5, run:
"$DYNOS" ctl write-execute-handoff .dynos/task-{id}
This writes .dynos/task-{id}/handoff-execute-audit.json deterministically from the live manifest stage. Do NOT hand-write the handoff JSON in prompt logic.
Hard Rules
- No speculative implementation: Executors must stay strictly within their segment's
files_expected and criteria_ids.
- Atomic evidence: Evidence files must be written only after the segment's implementation is complete.
- Dependency discipline: Never spawn an executor before its dependency evidence files are available.
- Caching Discipline: Never skip a segment if its
files_expected have any uncommitted changes not reflected in the existing evidence.
- Ownership discipline: A segment that edits files outside
files_expected is invalid until repaired and re-evidenced.