Drive Epic
The resident driver. One long-lived session per active epic. It replaces the earlier scheduled-tick trigger model: one session boots from durable PM/git state, holds the dependency graph and decision-register canon in context, executes lane playbooks inline and serially (one in flight at a time; never as nested lane subagents — the leaf-worker contract per epic-orchestrator/execution-model.md), and advances on completion events — until park (no automated action possible), refresh-park (planned, count-based context refresh), or bloat (context degraded). Cron survives only as a slow liveness guard (this skill's step 0) and the daily janitor (reconcile-tickets).
Detect by content and event, never by clock or silence — the resident driver's founding principle, and the same one await-worker.sh v2 enforces one level down.
Contract
| Trigger |
Inputs |
Outputs |
Durable writes |
Allowed delegation |
Failure states |
| hourly liveness cron, or manual invocation |
PM scope, repo path(s), heartbeat location, retry ceiling (default 3), optional humanContact |
epic advanced through as many actions as fit before park, refresh-park, or bloat, or a clean no-op |
driver claim + refreshes, continuation brief, daily heartbeat, everything the lane playbooks write |
lane playbooks (mature-ticket, deliver-ticket) executed inline with their phase workers dispatched as this session's own leaves; submit-ticket-pr Merge and submit-epic-pr inline; state-reader / evidence-checker leaf workers (checker conditional per the epic-orchestrator Evidence Rule — the driver's inline-walked lanes are the primary skip case) |
PM unreachable, claim yield, retry ceiling, fence trip, tool/auth failure |
Step 0a — parse the invocation (first step, before anything else)
This skill is invoked by running a session on it with an instruction string. First, inspect that instruction. If it begins with rule-coherence — i.e. the operator ran the drive-epic skill with the input rule-coherence <merge-sha> approve|reject|redirect:<scope> — parse <merge-sha> and the flag, then run the Human-Ruling Resolution Route (Ruling Mode) below and stop — do not fall through to the guard or the drive loop. Any other invocation (an epic id, "drive EPIC-123", a bare resume) is a normal driver run: proceed to Step 0 — the guard.
A bare resume typed into a session that has been sitting idle is not a resume — it is a cold boot in the wrong place. Reset first (/clear on Claude Code) and invoke this skill in the fresh session: Boot is built for the cold path, and a parked driver's context is rebuilt from durable state either way, so continuing the stale transcript buys nothing and re-caches everything it had accumulated. Cost model and the anti-clock caveat: epic-orchestrator/execution-model.md § 8.
(There is no separate rule-coherence skill, command, or shell alias — the spec's intent is "no new skill; a drive-epic ruling mode reusing its claim/fence/routing." The operator delivers a ruling by invoking this skill with the rule-coherence … instruction; this parse step is the branch.)
Ruling Mode
Reached when Step 0a parsed a rule-coherence <merge-sha> approve|reject|redirect:<scope> invocation. Run the Human-Ruling Resolution Route below instead of the guard + drive loop. The ruling mode reuses this skill's claim/fence/routing; it never enters the drive loop.
Step 0 — the guard
- Daily heartbeat. Post/refresh at the designated heartbeat location (
${CLAUDE_PLUGIN_ROOT}/scripts/heartbeat.sh <heartbeat-location> — dedupe and update-in-place live in the script); optional mirror on the active epic. Posted on every guard path and refreshed by the driver inside its own drive loop — if the scheduler guarantees same-task no-overlap (see the live-test gate), the hourly guard cannot fire while a driver run is live, so a driver run crossing ~24h would otherwise starve the dead-man switch; the in-loop refresh closes that.
- Driver-claim check (
${CLAUDE_PLUGIN_ROOT}/scripts/driver-claim.sh status <epic-id>).
- Fresh live claim → exit, one line logged. (A live driver owns the epic; do not interleave.)
- Stale/absent + actionable work →
driver-claim.sh acquire <epic-id> <session-run-id>; win → this session becomes the driver, proceed to Boot; lose → self-close and exit no-op.
- No actionable work — including an epic parked on an unresolved pending-human entry — → exit; the janitor's digest owns the human reminder. The human resolves a pending-human park out-of-band by invoking this skill with a
rule-coherence … instruction (a session, parsed by Step 0a — not a comment this guard must detect), so there is no wake edge here.
- Backstop: an epic that is
coherence-pending with the set-difference empty and no unresolved entry (a ruling session crashed between its RULING write and its in-session clear) is actionable — boot to evaluate the clear predicate — so a resolved-but-still-labeled epic never strands.
- Pending-capacity wake edge: an epic labeled
pending-capacity (a runtime-local Frontier capacity park — AGENTS.md § Frontier Availability Policy) is a wake candidate, unlike a pending-human park. Run a minimal one-shot Frontier probe through the same runtime-native executor recorded in the CAPACITY_PARK entry (smallest possible prompt, e.g. "reply DONE"); a guard in another runtime does not substitute its own executor or treat that capacity as local. Probe success → clear pending-capacity, treat the epic as actionable (acquire + Boot; the booted driver retries the blocked dispatch first), probe failure → exit still parked (re-probed next guard tick). Persistent capacity flapping — probe success boots a driver whose retried dispatch fails again, burning a lane attempt each time — is counted by the standard retry ceiling → blocked + escalation. A steady park (every probe fails; no driver ever boots, so no lane attempt increments the ceiling) is outside the ceiling's reach by construction — the janitor's capacity-park digest bands (reconcile-tickets § Capacity Parks) are the declared backstop, so a park↔probe loop is never silent.
- Second signed-off epic in scope → escalate, never interleave (single-active-epic posture).
Manual sessions run the identical step 0; per-ticket session claims enforce the same discipline for manual lane work.
(The wedged-driver probe is not a guard step — it re-homed to the daily janitor sweep, since under same-task no-overlap the hourly guard cannot fire against the live driver it would probe. See reconcile-tickets.)
Boot
Once this session is the driver:
Mint the run id (already minted for the claim); start the refresher. Two build paths depending on whether session-pid capture can be pinned from the desk (this is itself coupled to live-test-gate item 3 — the refresher's orphan detection must watch the session process, not the transient shell):
- Orphan-aware path (preferred, if the ancestry walk resolves a stable session process): derive
<session-pid> by walking the PPID chain up from $$ (the current Bash shell is transient — $$ is wrong on its own). The walk: starting at $$, repeatedly read ps -o ppid=,comm= -p <pid> and step to the parent, stopping at the first ancestor whose comm is the session/harness process (not bash/sh/the Bash-tool wrapper) — that ancestor is the wrapper's parent that survives a single Bash call and dies with the session. Capture it once at boot:session_pid="$$"
while :; do
read -r ppid comm < <(ps -o ppid=,comm= -p "$session_pid" 2>/dev/null | awk '{print $1, $2}')
[[ -z "${ppid:-}" || "$ppid" -le 1 ]] && break
case "$comm" in *bash|*sh|*zsh) session_pid="$ppid"; continue ;; esac
session_pid="$ppid"; break
done
Then launch ${CLAUDE_PLUGIN_ROOT}/scripts/driver-claim.sh refresh <epic-id> <session-run-id> "$session_pid" as a background shell; it bumps the claim every ~15m and self-exits on that pid's disappearance/reparenting.
- Interim lease-only fallback (buildable now, no orphan self-exit): if the ancestry walk cannot be confirmed to resolve the true session process from the desk (gate item 3 unverified), launch the refresher in lease-only mode — pass a sentinel
<session-pid> of 0 (or omit orphan watching), so the refresher simply bumps Refreshed at every ~15m and never self-exits. A crashed session's claim then decays by lease alone in ≤45m — no worse than the per-ticket tier-3 lease behavior, and the takeover reap-before-release adopts it on the next guard hour. This keeps the driver shippable while item 3 is pending; the orphan-aware path is a strict improvement once the walk is confirmed live.
The refresher's own subcommand supports both: with a real pid it watches PPID; with pid 0 it skips the orphan check and refreshes on the lease cadence only (see the refresh subcommand's pid-0 branch).
Coherence audit (set-difference). Fetch all merged child PRs targeting the epic branch (mergeCommit oids — the surface verify-merge.sh reads, --limit above any plausible child count, paged-or-asserted) and all coherence-verdict register-entry Merge SHA: keys from the epic — verdict entries only, i.e. those with no Kind: field (a MODE/CAPACITY_PARK/FABLE_MAKEUP entry is not a verdict and never satisfies the audit) — (a paged read — comment-window honesty). Every merged SHA must hold a coherence-verdict register entry. Any merged-but-unregistered SHA ⇒ treat the epic as coherence-pending and queue the review for the unregistered set, oldest mergedAt first. An unresolved pending-human entry parks the queue — no further reviews until the human resolves it via rule-coherence. No merged child PRs ⇒ pass; a stray coherence-pending with zero merged PRs clears vacuously.
Takeover reap-before-release (only if this boot took over a stale driver claim). First post a one-line takeover note — a # Driver Claim-adjacent bookkeeping comment on the epic (the "takeover/janitor notes" species comment-species.sh already classifies as bookkeeping) recording: this session's run id, the predecessor's run id + reaped-claim id (closed taken-over by the acquire stale-close), and the boot timestamp. This is the audit-trail line spec §41/§47 asks for — the durable "who took over from whom" record; keep it minimal (one line, no state-clock effect since it is bookkeeping). Then read the predecessor's dispatch manifest (per-session filename at the epic worktree's absolute path) and reap/stop/confirm any non-terminal lane workers first (${CLAUDE_PLUGIN_ROOT}/scripts/reap-workers.sh <manifest>, then stop stragglers via the agent-layer primitive and append reap records) — because whether an in-flight Agent-tool lane survives its dispatcher's death is a live-test-gate item, and re-dispatching beside a still-writing orphan is the concurrency hazard. Only then foreign-release (by id) the per-ticket claims bearing the predecessor's session id (release-claim.sh <ticket> released-no-op --foreign <claim-id>; the children scan uses the watchdog's nested-query shape).
State-reader. Dispatch the state-reader worker (epic-orchestrator/state-reader-prompt.md, Fast tier) and consume its map; read the register canon and the latest continuation brief; load the state tables (epic-orchestrator/state-transitions.md).
Manifest init. Initialize the dispatch manifest at the epic worktree's absolute path: <epic-worktree-abs>/.dodi/dispatch-manifest-<session-run-id>.jsonl; create <epic-worktree-abs>/.dodi/.gitignore containing * on first use (ignores its contents and itself). The driver works against the epic worktree by absolute path (cwd resets between Bash calls) and is that worktree's sole writer — leaf fix workers dispatched by the walking session write under its ownership (one supervising session still serializes all epic-worktree writes); all lane work happens in per-lane ephemeral worktrees.
Drive loop
Repeat until park, refresh-park, or bloat:
- Select by the mode-parameterized priority table in
epic-orchestrator/state-transitions.md (§ Workflow Mode and Driver Priority Table — single canon for the full ordering and eligibility). Read the epic's mode-sprint/mode-waterfall label each loop pass (piggybacking the re-scan — the register's latest Kind: MODE entry is truth if the label is ever stale), and re-evaluate the mode at each lane close-out and the boot audit (the driver's own inline judgment over the coupling graph it already holds — no separate dispatch; a flip lands a new Kind: MODE entry, per state-transitions.md § Workflow Mode), to pick the lane-slot order: sprint interleaves deliver→mature, waterfall runs mature-all-then-deliver-all (deliver ineligible until every unblocked child carries ready-to-implement); the top four slots (merge → coherence review → epic PR → resume RESUMABLE) are mode-independent. Same demotion rules and coherence-pending blocking scope from that table — which includes the merge slot: no merge action is eligible while coherence-pending is set (only a RESUMABLE deliver-lane resume is exempt, per the table). This keeps reviews serial and the register append-ordered; the set-difference audit is the producer-independent backstop.
- Merge action is fail-closed: apply
coherence-pending before the merge command (the irreversible write is the inlined submit-ticket-pr merge — gh pr merge, not a git push), with the fence verified immediately before it.
- Coherence-review action targets every merged-but-unregistered SHA, oldest
mergedAt first, serially, halting after the first pending-human verdict completes its own routing. Definitions (pending-human, unresolved, clean, halt) are in AGENTS.md / the state table and are referenced, not restated. The label clears iff the set-difference (over coherence-verdict entries only — those with no Kind: field) is empty ∧ no register entry over the epic's merged SHAs is unresolved — register-wide, never batch-scoped; an unresolved pending-human entry gates further remediation, the boot queue, and the guard's actionable-work test until its RULING lands (written out-of-band by the human's rule-coherence session). Zero merged child PRs clears vacuously.
- Claim the ticket:
${CLAUDE_PLUGIN_ROOT}/scripts/claim.sh <ticket> <action> <session-run-id>. Retain its claim_id= output for clean-park input.
- Act. Execute the selected lane's playbook natively —
epic-orchestrator/lanes/mature-playbook.md or lanes/deliver-playbook.md — per epic-orchestrator/execution-model.md: walk the playbook's phase sequence in this session, dispatching each phase worker as this session's own leaf with its runtime-native tier pin and Frontier policy, awaiting dual-wake, appending each dispatch to the manifest (absolute-path anchored), and recording each progress marker yourself as its boundary is crossed. execution-model.md owns the leaf rule, tier pins and runtime resolution, dual-wake await, STALLED handling, the RESUMABLE/seam/continuation-brief mechanics, and manifest discipline — this step references them and never restates them.
- Close out — if the session is taking a lane
RESUMABLE exit, use Clean park and return; skip this generic close-out to avoid duplicate releases or briefs. Otherwise (prefix-resumable — any close-out is resumable from any prefix by a successor; the SHA-keyed evidence, claim ids, and manifest reap records are the resume keys): fence (driver-claim.sh verify + refresher-alive check), release the ticket claim (pin the concrete ${CLAUDE_PLUGIN_ROOT}/scripts/release-claim.sh <ticket> <exit-state> --session <session-run-id> — the --session filter is how "release own-session by default" is actually enforced: release-claim.sh's no-flag default targets the newest open claim of any session, so a driver omitting it could close a foreign successor's claim, the §54 wrong-claim-close shape from the survivor side), post checkpoint evidence, reap the manifest (reap-workers.sh; stop stragglers; append reap records).
- Re-scan (per-iteration staleness probe): newest comment/history on the epic, excluding this session's own writes and bookkeeping species — classification routed through the species-aware
${CLAUDE_PLUGIN_ROOT}/scripts/watchdog-scan.sh output with an epic-only scope flag (the probe is the partition's fifth consumer, not a sixth inline copy), with the own-writes filter applied on top by run id. API authorship cannot discriminate (every plugin session writes as the same key), so "self" is session-run-id-in-body: the driver skips writes it knows it just made; any residual unattributable history event (labels/history carry no body) triggers a benign full re-scan. Human app comments are distinct users and always count. Full state-reader re-scan on external change.
Per-ticket retry ceilings carry over (stagnation-counting, progress-reset) — the driver owns the counter, not claim.sh.
Park, refresh-park, and bloat — the three exits
No time- or silence-based exit or succession trigger — a clock rule would recreate the conflicting-rule-set problem. Refresh-park is count-based at a durable boundary, not a clock rule.
- Park: no action the parked session itself takes (human-parked on a pending-human entry, blocked, or empty queue). A
pending-capacity park is still a park in this sense — its guard probe later boots a fresh driver, a different session's action, not this one's.
- Refresh-park: planned, count-based — the driver takes a
RESUMABLE exit at the current seam once the seam budget (below) is reached (may be mid-lane). The successor resumes the parked lane: the guard sees the RESUMABLE lane as actionable and cold-boots a fresh driver, the proven path.
- Bloat: emergency — context degraded — await the in-flight lane's own exit (the driver cannot command a mid-flight checkpoint), then exit; crash recovery is the floor if the exit protocol degrades. A degraded context is the worst judge of its own degradation — refresh is the rule, bloat the exception. At any park evaluation no lane is in flight by construction (awaiting an in-flight lane is itself an available action, reached before park).
Planned context refresh (the refresh-park budget)
- Budget: N completion-anchored lane-progress seams crossed per driver session (default ~12, invocation-overridable — ≈ 3 full mature lanes, ~2.5 deliver lanes, or ~1.3 full sprint children). A countable seam is a completion-anchored durable point — the moment a phase's worker has returned and its durable artifact is recorded, before the next dispatch. For mature these are its four state-transition boundaries (all completion-anchored — a phase label is applied only on clean phase output). For deliver these are its completion-anchored internal checkpoints excluding the dispatch-anchored
implementing (posted when workers are dispatched, before any commit exists — it never counts and never trips the budget).
- Counter / trigger: increment the seam counter as each completion-anchored seam is recorded; evaluate only at those seams — durable, and with nothing in flight by construction (the prior worker returned, the next is not yet dispatched, so there is no live leaf worker to orphan). When seams-this-session ≥ budget, take a
RESUMABLE exit at the current seam via the mid-lane exit sequence below and release with exit state refresh-park. The seam counter resets in the fresh successor (a resumed lane does not re-count seams it crossed before the park — resume re-enters at the recorded seam without re-posting it).
- Mid-lane exit sequence (pinned, fence-first): run Clean park with the driver and lane's exact session/claim identifiers. It is mandatory for every mid-lane
RESUMABLE exit, including capacity and emergency exits as well as refresh-park. The helper persists the anchor and brief, closes the ticket claim as RESUMABLE, and yields for confirmed refresher shutdown before the final fenced driver release. Use driver exit state refresh-park when the seam budget trips. The shared procedure owns the ordering and failure/retry mechanics; cleanup never clears context.
For an exit with a RESUMABLE lane, the clean-park procedure above governs close-out; do not repeat these writes. Between-lanes exit protocol, all three: fence, post the continuation brief (format below; repo mirror continuation-brief.md for validation), driver-claim.sh release <epic-id> <claim-id> <parked|bloat-handoff|refresh-park>, exit. Brief-post failure: retry once, then exit anyway — durable state alone is the proven cold-boot path.
Continuation brief format (self-contained, posted as an epic comment under the # Continuation Brief header): state map reference with evidence links; chosen next action + one line of why; live concerns from notes; anything in flight that must not be redone (open PRs, running lanes, partial close-outs with their resume keys).
The Human-Ruling Resolution Route (ruling mode)
Reached via Step 0a when this skill was invoked with the instruction rule-coherence <merge-sha> approve|reject|redirect:<scope> (the operator runs the drive-epic skill with that instruction — there is no separate command). The answer surface is a session, not a parsed comment — no NL parse of PM comments, no comment-authorship discrimination, no wake edge, no automation-key prerequisite. The ruling is an automation-authored register write under the existing idempotent SHA-keyed discipline.
Full lifecycle (both edges pinned):
- Acquire. The epic is normally parked on the pending-human entry, so no driver contends. If a driver is live (it may legitimately be resuming a RESUMABLE lane during the park), wait until that claim is released or goes stale, then acquire — bounded, because the driver parks after at most its one in-flight lane. Never "hand off to a pickup" (no channel could deliver the ruling's content to the live driver). A crashed ruling session simply re-runs and re-acquires under the same wait, idempotent under SHA-keyed skip-what-exists. Fence/refresher posture: the ruling session starts no refresher — its claim freshness bounds it by construction (a sub-lease; a session running past ~45m simply decays and re-runs idempotently), so it fences with
${CLAUDE_PLUGIN_ROOT}/scripts/driver-claim.sh verify (the three claim-state conjuncts only — the fourth, refresher-alive, is n/a here since no refresher was started) immediately before the RULING write and again immediately before the label clear, aborting on ownership loss. This matches spec §107 ("reusing its claim/fence/routing") without contradicting Task B's four-conjunct fence definition (the fourth conjunct is asserted by callers that run a refresher; the ruling session does not).
- Route. Read the pending entry named by
<merge-sha>, route per flag (below), and write the RULING as the last write.
- Clear. Evaluate the register-wide clear predicate and clear
coherence-pending if satisfied, in this same session — nothing else owns this evaluation now (the async wake edge that used to occasion it is deleted). A guard backstop covers a crash between the RULING write and the clear (the guard's actionable-work test).
- Exit. Release the claim with a
ruled exit state and exit. The ruling mode never enters the drive loop and never selects from the priority table — it must not become the driver from the human's terminal, nor idle holding a fresh claim that blocks the guard from booting a real driver.
Per-flag routing (the reviewer recorded the routing inputs at review time; the ruling session executes them):
- GATE1_AMENDMENT. approve ⇒ execute the held route recorded in the pending entry (canonization, superseded-by note, affected-children label strips — recorded held/not-yet-performed). reject or
redirect:<scope> ⇒ the MATERIAL_DRIFT machinery (corrective ticket, dependents held by relation); redirect:<scope> scopes the corrective (judgment fed to the corrective-drafting step), bare reject scoped by the contradiction; the corrective's blocked-by relations cover the AMENDMENT's originally-held dependents.
- GATE1_REFRESH (its constituent decisions were each already individually canonized, so the register canon itself is what the human rules on). approve ⇒ the underlying writes already routed; the
RULING records the re-approved delta as the new Gate-1 baseline (held route is a no-op by construction). reject or redirect ⇒ de-canonize: post superseding register entries, update the canon summary to remove the rejected decisions, and strip readiness labels on the per-decision affected-children set the reviewer recorded in the pending entry (a mechanical durable read) — a MATERIAL_DRIFT corrective only where the scope names rework.
Ordering and idempotence. The route ends by writing the RULING — the last write (fail-closed): a mid-route crash leaves the pending entry unresolved, and the human simply re-invokes drive-epic with the same rule-coherence <sha> … instruction, idempotent under SHA-keyed skip-what-exists. The RULING is a decision-register-entry.md variant with the same # Decision Register Entry header and the same Merge SHA: key the audit reads; its body records the outcome and the writes performed. unresolved ≡ a pending-human entry with no later RULING for its SHA — the <merge-sha> argument binds it.
Scheduling migration and prerequisites
- Scheduling (settled): the resident driver runs as
dodi-drive-epic (hourly, off-peak minute, permission mode Auto; epic-PR merge in no allow-list); dodi-reconcile-tickets is the daily janitor (unchanged schedule). The earlier interim scheduled-tick fallback is retired — the resident driver is now the sole scheduling path.
- Prerequisites (carried, plus one new term): branch protection on main/master; escalation channel tested end-to-end;
LINEAR_API_KEY in the session environment; and coherence rulings are delivered by invoking the drive-epic skill with the instruction rule-coherence <sha> approve|reject|redirect — a session run, not a chat reply and not a separate command. (Step 0a parses that instruction and branches to ruling mode.) The operator learns this before the first park.
- Machine-off operation (cloud routines) remains the upgrade path.
Rules
- Mechanics live in
dodi-dev/scripts/ — run the script and judge its result; never restate a script's mechanism in prose.
- Every worker/lane dispatch carries an explicit model-tier pin per AGENTS.md; a plugin hook also enforces this.
- Single active epic. A second signed-off epic in scope is escalated, not interleaved.
- Never merge an epic PR; Gate 2 is procedural and absolute.
- The fence guards every durable-write close-out, every wake, and the merge command. Ownership lost → abort durable writes and exit; read error → brief retry, then exit without durable writes.
Evidence
- Record per session: run id, guard outcome, boot audit result, actions taken, exit (
parked/refresh-park/bloat-handoff/no-op/ruled/taken-over/error — the release-state enum), continuation-brief link, driver-claim release state.
Stop Conditions
- PM unreachable / auth failure — escalate, exit.
- Claim yield (lost the acquire race) — exit no-op.
- Fence trip (ownership lost) — abort durable writes, exit.
- Retry ceiling on a ticket — mark
blocked, escalate.
- Park, refresh-park, or bloat — exit protocol above.
1---2name: drive-epic3description: Use as the resident driver — one long-lived orchestrator session per active epic that boots from durable state, holds the dependency graph and decision-register canon in context, dispatches lanes, and advances on completion events until park, refresh-park, or bloat. Also the session-delivered coherence-ruling mode.4---56# Drive Epic78The resident driver. One long-lived session per active epic. It replaces the earlier scheduled-tick trigger model: one session boots from durable PM/git state, holds the dependency graph and decision-register canon in context, executes lane playbooks **inline and serially** (one in flight at a time; never as nested lane subagents — the leaf-worker contract per `epic-orchestrator/execution-model.md`), and advances on **completion events** — until **park** (no automated action possible), **refresh-park** (planned, count-based context refresh), or **bloat** (context degraded). Cron survives only as a slow liveness guard (this skill's step 0) and the daily janitor (`reconcile-tickets`).910**Detect by content and event, never by clock or silence** — the resident driver's founding principle, and the same one `await-worker.sh` v2 enforces one level down.1112## Contract1314| Trigger | Inputs | Outputs | Durable writes | Allowed delegation | Failure states |15| --- | --- | --- | --- | --- | --- |16| hourly liveness cron, or manual invocation | PM scope, repo path(s), heartbeat location, retry ceiling (default 3), optional humanContact | epic advanced through as many actions as fit before park, refresh-park, or bloat, or a clean no-op | driver claim + refreshes, continuation brief, daily heartbeat, everything the lane playbooks write | lane playbooks (`mature-ticket`, `deliver-ticket`) executed **inline** with their phase workers dispatched as this session's own leaves; `submit-ticket-pr` Merge and `submit-epic-pr` inline; state-reader / evidence-checker leaf workers (checker conditional per the `epic-orchestrator` Evidence Rule — the driver's inline-walked lanes are the primary skip case) | PM unreachable, claim yield, retry ceiling, fence trip, tool/auth failure |1718## Step 0a — parse the invocation (first step, before anything else)1920This skill is invoked by running a session on it with an instruction string. **First, inspect that instruction.** If it begins with `rule-coherence` — i.e. the operator ran the drive-epic skill with the input `rule-coherence <merge-sha> approve|reject|redirect:<scope>` — parse `<merge-sha>` and the flag, then run the **Human-Ruling Resolution Route** (Ruling Mode) below and **stop** — do **not** fall through to the guard or the drive loop. Any other invocation (an epic id, "drive EPIC-123", a bare resume) is a normal driver run: proceed to Step 0 — the guard.2122**A bare resume typed into a session that has been sitting idle is not a resume — it is a cold boot in the wrong place.** Reset first (`/clear` on Claude Code) and invoke this skill in the fresh session: Boot is built for the cold path, and a parked driver's context is rebuilt from durable state either way, so continuing the stale transcript buys nothing and re-caches everything it had accumulated. Cost model and the anti-clock caveat: `epic-orchestrator/execution-model.md` § 8.2324(There is no separate `rule-coherence` skill, command, or shell alias — the spec's intent is "no new skill; a drive-epic ruling mode reusing its claim/fence/routing." The operator delivers a ruling by invoking **this** skill with the `rule-coherence …` instruction; this parse step is the branch.)2526## Ruling Mode2728Reached when Step 0a parsed a `rule-coherence <merge-sha> approve|reject|redirect:<scope>` invocation. Run the **Human-Ruling Resolution Route** below instead of the guard + drive loop. The ruling mode reuses this skill's claim/fence/routing; it never enters the drive loop.2930## Step 0 — the guard31321. **Daily heartbeat.** Post/refresh at the designated heartbeat location (`${CLAUDE_PLUGIN_ROOT}/scripts/heartbeat.sh <heartbeat-location>` — dedupe and update-in-place live in the script); optional mirror on the active epic. Posted on every guard path **and refreshed by the driver inside its own drive loop** — if the scheduler guarantees same-task no-overlap (see the live-test gate), the hourly guard cannot fire while a driver run is live, so a driver run crossing ~24h would otherwise starve the dead-man switch; the in-loop refresh closes that.332. **Driver-claim check** (`${CLAUDE_PLUGIN_ROOT}/scripts/driver-claim.sh status <epic-id>`).34 - Fresh live claim → exit, one line logged. (A live driver owns the epic; do not interleave.)35 - Stale/absent + **actionable work** → `driver-claim.sh acquire <epic-id> <session-run-id>`; win → this session **becomes** the driver, proceed to Boot; lose → self-close and exit no-op.36 - **No actionable work** — including an epic parked on an unresolved pending-human entry — → exit; the janitor's digest owns the human reminder. The human resolves a pending-human park out-of-band by invoking this skill with a `rule-coherence …` instruction (a session, parsed by Step 0a — not a comment this guard must detect), so there is **no wake edge** here.37 - **Backstop:** an epic that is `coherence-pending` with the set-difference empty **and** no unresolved entry (a ruling session crashed between its `RULING` write and its in-session clear) *is* actionable — boot to evaluate the clear predicate — so a resolved-but-still-labeled epic never strands.38 - **Pending-capacity wake edge:** an epic labeled `pending-capacity` (a runtime-local Frontier capacity park — AGENTS.md § Frontier Availability Policy) *is* a wake candidate, unlike a pending-human park. Run a minimal one-shot **Frontier probe** through the same runtime-native executor recorded in the `CAPACITY_PARK` entry (smallest possible prompt, e.g. "reply DONE"); a guard in another runtime does not substitute its own executor or treat that capacity as local. Probe success → clear `pending-capacity`, treat the epic as actionable (acquire + Boot; the booted driver retries the blocked dispatch first), probe failure → exit still parked (re-probed next guard tick). Persistent capacity **flapping** — probe success boots a driver whose retried dispatch fails again, burning a lane attempt each time — is counted by the standard retry ceiling → `blocked` + escalation. A **steady** park (every probe fails; no driver ever boots, so no lane attempt increments the ceiling) is outside the ceiling's reach by construction — the janitor's capacity-park digest bands (`reconcile-tickets` § Capacity Parks) are the declared backstop, so a park↔probe loop is never silent.393. **Second signed-off epic in scope → escalate**, never interleave (single-active-epic posture).4041Manual sessions run the identical step 0; per-ticket session claims enforce the same discipline for manual *lane* work.4243(The wedged-driver probe is **not** a guard step — it re-homed to the daily janitor sweep, since under same-task no-overlap the hourly guard cannot fire against the live driver it would probe. See `reconcile-tickets`.)4445## Boot4647Once this session is the driver:48491. **Mint the run id** (already minted for the claim); **start the refresher.** Two build paths depending on whether session-pid capture can be pinned from the desk (this is itself coupled to **live-test-gate item 3** — the refresher's orphan detection must watch the *session* process, not the transient shell):50 - **Orphan-aware path (preferred, if the ancestry walk resolves a stable session process):** derive `<session-pid>` by walking the PPID chain up from `$$` (the current Bash shell is transient — `$$` is wrong on its own). The walk: starting at `$$`, repeatedly read `ps -o ppid=,comm= -p <pid>` and step to the parent, stopping at the first ancestor whose `comm` is the session/harness process (not `bash`/`sh`/the `Bash`-tool wrapper) — that ancestor is the wrapper's parent that **survives** a single Bash call and dies with the session. Capture it once at boot:51 ```bash52 session_pid="$$"53 while :; do54 read -r ppid comm < <(ps -o ppid=,comm= -p "$session_pid" 2>/dev/null | awk '{print $1, $2}')55 [[ -z "${ppid:-}" || "$ppid" -le 1 ]] && break56 case "$comm" in *bash|*sh|*zsh) session_pid="$ppid"; continue ;; esac57 session_pid="$ppid"; break58 done59 ```60 Then launch `${CLAUDE_PLUGIN_ROOT}/scripts/driver-claim.sh refresh <epic-id> <session-run-id> "$session_pid"` as a background shell; it bumps the claim every ~15m and self-exits on that pid's disappearance/reparenting.61 - **Interim lease-only fallback (buildable now, no orphan self-exit):** if the ancestry walk cannot be confirmed to resolve the true session process from the desk (gate item 3 unverified), launch the refresher in **lease-only mode** — pass a sentinel `<session-pid>` of `0` (or omit orphan watching), so the refresher simply bumps `Refreshed at` every ~15m and never self-exits. A crashed session's claim then decays **by lease alone in ≤45m** — no worse than the per-ticket tier-3 lease behavior, and the takeover reap-before-release adopts it on the next guard hour. This keeps the driver shippable while item 3 is pending; the orphan-aware path is a strict improvement once the walk is confirmed live.6263 The refresher's own subcommand supports both: with a real pid it watches PPID; with pid `0` it skips the orphan check and refreshes on the lease cadence only (see the refresh subcommand's pid-`0` branch).642. **Coherence audit (set-difference).** Fetch all merged child PRs targeting the epic branch (`mergeCommit` oids — the surface `verify-merge.sh` reads, `--limit` above any plausible child count, paged-or-asserted) and all **coherence-verdict** register-entry `Merge SHA:` keys from the epic — verdict entries only, i.e. those with **no `Kind:` field** (a `MODE`/`CAPACITY_PARK`/`FABLE_MAKEUP` entry is not a verdict and never satisfies the audit) — (a **paged** read — comment-window honesty). **Every merged SHA must hold a coherence-verdict register entry.** Any merged-but-unregistered SHA ⇒ treat the epic as `coherence-pending` and queue the review for the unregistered set, **oldest `mergedAt` first**. An unresolved pending-human entry parks the queue — no further reviews until the human resolves it via `rule-coherence`. No merged child PRs ⇒ pass; a stray `coherence-pending` with zero merged PRs clears vacuously.653. **Takeover reap-before-release** (only if this boot took over a stale driver claim). **First post a one-line takeover note** — a `# Driver Claim`-adjacent bookkeeping comment on the epic (the "takeover/janitor notes" species `comment-species.sh` already classifies as bookkeeping) recording: this session's run id, the predecessor's run id + reaped-claim id (closed `taken-over` by the `acquire` stale-close), and the boot timestamp. This is the audit-trail line spec §41/§47 asks for — the durable "who took over from whom" record; keep it minimal (one line, no state-clock effect since it is bookkeeping). Then read the predecessor's dispatch manifest (per-session filename at the epic worktree's absolute path) and **reap/stop/confirm any non-terminal lane workers first** (`${CLAUDE_PLUGIN_ROOT}/scripts/reap-workers.sh <manifest>`, then stop stragglers via the agent-layer primitive and append reap records) — because whether an in-flight Agent-tool lane survives its dispatcher's death is a live-test-gate item, and re-dispatching beside a still-writing orphan is the concurrency hazard. **Only then** foreign-release (by id) the per-ticket claims bearing the predecessor's session id (`release-claim.sh <ticket> released-no-op --foreign <claim-id>`; the children scan uses the watchdog's nested-query shape).664. **State-reader.** Dispatch the state-reader worker (`epic-orchestrator/state-reader-prompt.md`, Fast tier) and consume its map; read the register canon and the latest continuation brief; load the state tables (`epic-orchestrator/state-transitions.md`).675. **Manifest init.** Initialize the dispatch manifest at the epic worktree's **absolute path**: `<epic-worktree-abs>/.dodi/dispatch-manifest-<session-run-id>.jsonl`; create `<epic-worktree-abs>/.dodi/.gitignore` containing `*` on first use (ignores its contents and itself). The driver works against the epic worktree **by absolute path** (cwd resets between Bash calls) and is that worktree's **sole writer** — leaf fix workers dispatched by the walking session write under its ownership (one supervising session still serializes all epic-worktree writes); all lane work happens in per-lane ephemeral worktrees.6869## Drive loop7071Repeat until park, refresh-park, or bloat:72731. **Select** by the mode-parameterized priority table in `epic-orchestrator/state-transitions.md` (§ Workflow Mode and Driver Priority Table — single canon for the full ordering and eligibility). Read the epic's `mode-sprint`/`mode-waterfall` label each loop pass (piggybacking the re-scan — the register's latest `Kind: MODE` entry is truth if the label is ever stale), and **re-evaluate the mode at each lane close-out and the boot audit** (the driver's own inline judgment over the coupling graph it already holds — no separate dispatch; a flip lands a new `Kind: MODE` entry, per `state-transitions.md` § Workflow Mode), to pick the lane-slot order: sprint interleaves deliver→mature, waterfall runs mature-all-then-deliver-all (deliver ineligible until every unblocked child carries `ready-to-implement`); the top four slots (merge → coherence review → epic PR → resume RESUMABLE) are mode-independent. Same demotion rules and `coherence-pending` blocking scope from that table — **which includes the merge slot: no merge action is eligible while `coherence-pending` is set** (only a `RESUMABLE` deliver-lane resume is exempt, per the table). This keeps reviews serial and the register append-ordered; the set-difference audit is the producer-independent backstop.74 - **Merge action** is fail-closed: **apply `coherence-pending` before the merge command** (the irreversible write is the inlined `submit-ticket-pr` merge — `gh pr merge`, not a git push), with the fence verified immediately before it.75 - **Coherence-review action** targets every merged-but-unregistered SHA, **oldest `mergedAt` first, serially, halting after the first pending-human verdict completes its own routing.** Definitions (*pending-human*, *unresolved*, *clean*, *halt*) are in AGENTS.md / the state table and are referenced, not restated. The label clears iff the set-difference (over coherence-verdict entries only — those with no `Kind:` field) is empty ∧ **no register entry over the epic's merged SHAs is unresolved** — register-wide, never batch-scoped; an unresolved pending-human entry gates further remediation, the boot queue, and the guard's actionable-work test until its `RULING` lands (written out-of-band by the human's `rule-coherence` session). Zero merged child PRs clears vacuously.762. **Claim** the ticket: `${CLAUDE_PLUGIN_ROOT}/scripts/claim.sh <ticket> <action> <session-run-id>`. Retain its `claim_id=` output for clean-park input.773. **Act.** Execute the selected lane's playbook **natively** — `epic-orchestrator/lanes/mature-playbook.md` or `lanes/deliver-playbook.md` — per `epic-orchestrator/execution-model.md`: walk the playbook's phase sequence in this session, dispatching each phase worker as this session's own **leaf** with its runtime-native tier pin and Frontier policy, awaiting dual-wake, appending each dispatch to the manifest (absolute-path anchored), and recording each progress marker yourself as its boundary is crossed. `execution-model.md` owns the leaf rule, tier pins and runtime resolution, dual-wake await, STALLED handling, the `RESUMABLE`/seam/continuation-brief mechanics, and manifest discipline — this step references them and never restates them.784. **Close out** — if the session is taking a lane `RESUMABLE` exit, use [Clean park](../epic-orchestrator/clean-park.md) and return; skip this generic close-out to avoid duplicate releases or briefs. Otherwise (prefix-resumable — any close-out is resumable from any prefix by a successor; the SHA-keyed evidence, claim ids, and manifest reap records are the resume keys): **fence** (`driver-claim.sh verify` + refresher-alive check), release the ticket claim (pin the concrete `${CLAUDE_PLUGIN_ROOT}/scripts/release-claim.sh <ticket> <exit-state> --session <session-run-id>` — the `--session` filter is how "release own-session by default" is actually enforced: `release-claim.sh`'s no-flag default targets the newest open claim of *any* session, so a driver omitting it could close a foreign successor's claim, the §54 wrong-claim-close shape from the survivor side), post checkpoint evidence, **reap the manifest** (`reap-workers.sh`; stop stragglers; append reap records).795. **Re-scan** (per-iteration staleness probe): newest comment/history on the epic, **excluding this session's own writes and bookkeeping species** — classification routed through the species-aware `${CLAUDE_PLUGIN_ROOT}/scripts/watchdog-scan.sh` output with an **epic-only scope flag** (the probe is the partition's fifth consumer, not a sixth inline copy), with the own-writes filter applied on top by run id. API authorship cannot discriminate (every plugin session writes as the same key), so "self" is session-run-id-in-body: the driver skips writes it knows it just made; any residual unattributable history event (labels/history carry no body) triggers a benign full re-scan. Human app comments are distinct users and always count. Full state-reader re-scan on external change.8081Per-ticket retry ceilings carry over (stagnation-counting, progress-reset) — the driver owns the counter, not `claim.sh`.8283## Park, refresh-park, and bloat — the three exits8485**No time- or silence-based exit or succession trigger** — a clock rule would recreate the conflicting-rule-set problem. Refresh-park is count-based at a durable boundary, not a clock rule.8687- **Park:** no action the *parked* session itself takes (human-parked on a pending-human entry, blocked, or empty queue). A `pending-capacity` park is still a park in this sense — its guard probe later boots a *fresh* driver, a different session's action, not this one's.88- **Refresh-park:** planned, count-based — the driver takes a `RESUMABLE` exit at the current seam once the seam budget (below) is reached (**may be mid-lane**). The successor resumes the parked lane: the guard sees the `RESUMABLE` lane as actionable and cold-boots a fresh driver, the proven path.89- **Bloat:** emergency — context degraded — await the in-flight lane's own exit (the driver cannot command a mid-flight checkpoint), then exit; crash recovery is the floor if the exit protocol degrades. A degraded context is the worst judge of its own degradation — refresh is the rule, bloat the exception. At any park evaluation no lane is in flight by construction (awaiting an in-flight lane is itself an available action, reached before park).9091### Planned context refresh (the refresh-park budget)9293- **Budget:** N **completion-anchored lane-progress seams crossed** per driver session (default ~12, invocation-overridable — ≈ 3 full mature lanes, ~2.5 deliver lanes, or ~1.3 full sprint children). A **countable seam is a completion-anchored durable point** — the moment a phase's worker has returned and its durable artifact is recorded, before the next dispatch. For **mature** these are its four state-transition boundaries (all completion-anchored — a phase label is applied only on clean phase output). For **deliver** these are its completion-anchored internal checkpoints **excluding the dispatch-anchored `implementing`** (posted when workers are dispatched, before any commit exists — it never counts and never trips the budget).94- **Counter / trigger:** increment the seam counter as each completion-anchored seam is recorded; evaluate **only at those seams** — durable, and with nothing in flight by construction (the prior worker returned, the next is not yet dispatched, so there is no live leaf worker to orphan). When seams-this-session ≥ budget, take a `RESUMABLE` exit at the current seam via the mid-lane exit sequence below and release with exit state `refresh-park`. The seam counter resets in the fresh successor (a resumed lane does not re-count seams it crossed before the park — resume re-enters *at* the recorded seam without re-posting it).95- **Mid-lane exit sequence (pinned, fence-first):** run [Clean park](../epic-orchestrator/clean-park.md) with the driver and lane's exact session/claim identifiers. It is mandatory for every mid-lane `RESUMABLE` exit, including capacity and emergency exits as well as refresh-park. The helper persists the anchor and brief, closes the ticket claim as `RESUMABLE`, and yields for confirmed refresher shutdown before the final fenced driver release. Use driver exit state `refresh-park` when the seam budget trips. The shared procedure owns the ordering and failure/retry mechanics; cleanup never clears context.9697For an exit with a `RESUMABLE` lane, the clean-park procedure above governs close-out; do not repeat these writes. Between-lanes exit protocol, all three: **fence**, post the **continuation brief** (format below; repo mirror `continuation-brief.md` for validation), `driver-claim.sh release <epic-id> <claim-id> <parked|bloat-handoff|refresh-park>`, exit. Brief-post failure: retry once, then exit anyway — durable state alone is the proven cold-boot path.9899**Continuation brief format** (self-contained, posted as an epic comment under the `# Continuation Brief` header): state map reference with evidence links; chosen next action + one line of why; live concerns from notes; anything in flight that must not be redone (open PRs, running lanes, partial close-outs with their resume keys).100101## The Human-Ruling Resolution Route (ruling mode)102103Reached via Step 0a when this skill was invoked with the instruction `rule-coherence <merge-sha> approve|reject|redirect:<scope>` (the operator runs the `drive-epic` skill with that instruction — there is no separate command). The answer surface is a **session, not a parsed comment** — no NL parse of PM comments, no comment-authorship discrimination, no wake edge, no automation-key prerequisite. The ruling *is* an automation-authored register write under the existing idempotent SHA-keyed discipline.104105**Full lifecycle (both edges pinned):**1061071. **Acquire.** The epic is normally parked on the pending-human entry, so no driver contends. If a driver *is* live (it may legitimately be resuming a RESUMABLE lane during the park), **wait until that claim is released or goes stale, then acquire** — bounded, because the driver parks after at most its one in-flight lane. Never "hand off to a pickup" (no channel could deliver the ruling's content to the live driver). A crashed ruling session simply re-runs and re-acquires under the same wait, idempotent under SHA-keyed skip-what-exists. **Fence/refresher posture:** the ruling session starts **no refresher** — its claim freshness bounds it by construction (a sub-lease; a session running past ~45m simply decays and re-runs idempotently), so it fences with `${CLAUDE_PLUGIN_ROOT}/scripts/driver-claim.sh verify` (the three **claim-state** conjuncts only — the fourth, refresher-alive, is n/a here since no refresher was started) immediately **before** the `RULING` write and again immediately **before** the label clear, aborting on ownership loss. This matches spec §107 ("reusing its claim/fence/routing") without contradicting Task B's four-conjunct fence definition (the fourth conjunct is asserted by callers that *run* a refresher; the ruling session does not).1082. **Route.** Read the pending entry named by `<merge-sha>`, route per flag (below), and write the `RULING` as the **last write**.1093. **Clear.** **Evaluate the register-wide clear predicate and clear `coherence-pending` if satisfied, in this same session** — nothing else owns this evaluation now (the async wake edge that used to occasion it is deleted). A guard backstop covers a crash between the `RULING` write and the clear (the guard's actionable-work test).1104. **Exit.** **Release the claim with a `ruled` exit state and exit. The ruling mode never enters the drive loop and never selects from the priority table** — it must not become the driver from the human's terminal, nor idle holding a fresh claim that blocks the guard from booting a real driver.111112**Per-flag routing** (the reviewer recorded the routing inputs at review time; the ruling session executes them):113114- **GATE1_AMENDMENT.** *approve* ⇒ execute the **held route** recorded in the pending entry (canonization, superseded-by note, affected-children label strips — recorded held/not-yet-performed). *reject* or `redirect:<scope>` ⇒ the **MATERIAL_DRIFT machinery** (corrective ticket, dependents held by relation); `redirect:<scope>` scopes the corrective (judgment fed to the corrective-drafting step), bare `reject` scoped by the contradiction; the corrective's blocked-by relations cover the AMENDMENT's originally-held dependents.115- **GATE1_REFRESH** (its constituent decisions were each already individually canonized, so the register canon itself is what the human rules on). *approve* ⇒ the underlying writes already routed; the `RULING` records the re-approved delta as the new Gate-1 baseline (held route is a no-op by construction). *reject* or *redirect* ⇒ **de-canonize**: post superseding register entries, update the canon summary to remove the rejected decisions, and strip readiness labels on the **per-decision affected-children set the reviewer recorded in the pending entry** (a mechanical durable read) — a MATERIAL_DRIFT corrective only where the scope names rework.116117**Ordering and idempotence.** The route ends by writing the `RULING` — the **last write** (fail-closed): a mid-route crash leaves the pending entry unresolved, and the human simply re-invokes drive-epic with the same `rule-coherence <sha> …` instruction, idempotent under SHA-keyed skip-what-exists. The `RULING` is a `decision-register-entry.md` variant with the same `# Decision Register Entry` header and the same `Merge SHA:` key the audit reads; its body records the outcome and the writes performed. *unresolved* ≡ a pending-human entry with no later `RULING` for its SHA — the `<merge-sha>` argument binds it.118119## Scheduling migration and prerequisites120121- **Scheduling (settled):** the resident driver runs as `dodi-drive-epic` (hourly, off-peak minute, permission mode Auto; epic-PR merge in no allow-list); `dodi-reconcile-tickets` is the daily janitor (unchanged schedule). The earlier interim scheduled-tick fallback is retired — the resident driver is now the sole scheduling path.122- **Prerequisites** (carried, plus one new term): branch protection on main/master; escalation channel tested end-to-end; `LINEAR_API_KEY` in the session environment; and **coherence rulings are delivered by invoking the `drive-epic` skill with the instruction `rule-coherence <sha> approve|reject|redirect` — a session run, not a chat reply and not a separate command.** (Step 0a parses that instruction and branches to ruling mode.) The operator learns this before the first park.123- Machine-off operation (cloud routines) remains the upgrade path.124125## Rules126127- Mechanics live in `dodi-dev/scripts/` — run the script and judge its result; never restate a script's mechanism in prose.128- Every worker/lane dispatch carries an explicit model-tier pin per AGENTS.md; a plugin hook also enforces this.129- **Single active epic.** A second signed-off epic in scope is escalated, not interleaved.130- Never merge an epic PR; Gate 2 is procedural and absolute.131- The fence guards every durable-write close-out, every wake, and the merge command. Ownership lost → abort durable writes and exit; read error → brief retry, then exit without durable writes.132133## Evidence134135- Record per session: run id, guard outcome, boot audit result, actions taken, exit (`parked`/`refresh-park`/`bloat-handoff`/`no-op`/`ruled`/`taken-over`/`error` — the release-state enum), continuation-brief link, driver-claim release state.136137## Stop Conditions138139- PM unreachable / auth failure — escalate, exit.140- Claim yield (lost the acquire race) — exit no-op.141- Fence trip (ownership lost) — abort durable writes, exit.142- Retry ceiling on a ticket — mark `blocked`, escalate.143- Park, refresh-park, or bloat — exit protocol above.