Do not try to resume a failed workflow from transient cache state. Treat
origin/<base> as the source of truth, inspect the result, clean up or repair
the target project as needed, and relaunch from the sidecar. The workflow
re-selects unblocked roadmap work from the current roadmap state.
When a normal task or addendum fails or halts after its worktree exists, the
ODW workflow runs a read-only assessment of the surviving task branch unless
assessPartialBranches=false. The per-task result may include an assessment
object and the top-level result includes an assessments summary array. The
classification is one of:
adopt-complete: the branch appears to satisfy the task and can continue through the ordinary review and integration path after gates are verified.adopt-partial: the branch contains a coherent useful slice, but the roadmap task must remain unchecked.continue-manual: the branch needs operator judgement before any merge.discard: the branch is stale, unsafe, incoherent, or too incomplete to keep.
Assessment is report-only. It never marks roadmap checkboxes, pushes, merges,
or cherry-picks. Use it to decide whether to preserve, manually finish, park,
or discard the branch before relaunching from origin/<base>.
After an adopt-partial or continue-manual verdict — and also after an
infrastructure fault such as schema-retry exhaustion — the workflow commits any
dirty docs/execplans/*.md artefacts onto the task branch so they survive
worktree cleanup. This is artefact salvage. It never merges, pushes, or ticks a
roadmap checkbox; it commits only under a deterministic machine identity
(df12-build). Paths outside the docs/execplans/ tree, symlinks, and paths
that escape the worktree are all rejected before the commit. Salvage does not
run for adopt-complete (which proceeds through the ordinary path) or
discard (thrown away); it also skips when host evidence collection failed and
is therefore untrustworthy (the deterministic continue-manual from a
collection-error), or when no worktree path is present in the assessment
evidence. The per-task result carries a result.salvage field
({ classification, committed, skipped, sha, detail }) for every branch where
salvage ran or was skipped; the top-level run result includes a salvages
summary array and the summary string appends
| salvaged artefacts on N branch(es) when N > 0. Salvage runs only when
partial-branch assessment is enabled (assessPartialBranches=true), the same
switch that governs assessment; it does not run when that setting is disabled.
Auth failures, provider outages such as 429, 500, or 529,
worktree-creation failures, dry runs, successful tasks, and manual-merge-ready
branches are not assessed. Provider outages also suppress the final remediation
flush, so transient adapter failures do not create roadmap work.
Infrastructure faults are classified separately from product failures. When a
stage agent's process dies — an ODW adapter timeout or crash, or schema-retry
exhaustion — the failure carries no evidence about the task branch, so the
workflow retries the stage agent in place up to stageAttempts total attempts.
A persistent fault terminates the task with status infra-fault rather than
failed: no assessment agent is spawned, remediation triage writes are skipped
(as with provider faults), and the halt detail directs the operator to relaunch
with resumePartialBranches=true and resumeMode="continue". Integration is
the exception: a fault there is never retried, because a hidden-success first
attempt may already have pushed — inspect origin/<base> and the roadmap
before relaunching. The run result's faultMetrics object counts retries and
terminal faults per class (infraRetries, infraFaults, providerFaults,
authFaults).
That host-level caution is distinct from the integration agent's own retry
loop, which is idempotent by construction. Because sibling tasks merge through
a single merge lock, origin/<base> can advance between a task's rebase and
its squash push. The agent lands each squash on a throwaway branch created with
git switch --discard-changes -C integrate-<slug> origin/<base>: the -C
force-resets integrate-<slug> onto the freshly fetched base whether or not
the branch already exists, and --discard-changes restores the index and
working tree to that base, throwing away any half-finished squash a prior
attempt left staged. (The earlier git switch -c form instead failed with
a branch named 'integrate-<slug>' already exists the moment a first attempt
had created the branch.) So when the push is rejected non-fast-forward, the
agent re-fetches, re-rebases the task branch onto the new origin/<base>,
re-runs that reset, and retries the push — with no manual cleanup, no collision
with the branch the previous attempt left behind, and no stale squash carried
forward. It retries until the push lands. The host simply does not extend that
idempotence across a process crash, where a hidden success may already have
merged.
Separately from that in-agent retry loop, the host validates the integration
agent's report before counting a task done. All five fields — ok, rebased,
squashMerged, pushed, and roadmapMarkedDone — must be truthy. A branch
that was squash-merged and pushed but never rebased onto origin/<base> (so
rebased is false or absent) halts the task at stage: integrate. The
detail field reports the integration agent's conflicts text or its
summary, whichever is set first; only when both are absent does it fall back
to the generic
'integration incomplete (need ok+rebased+squashMerged+pushed+roadmapMarkedDone)'.
Inspect the halted task result's detail field, rebase the branch manually if
it is salvageable, and relaunch; otherwise discard the branch and relaunch from
origin/<base>.
Addendum implementations have one extra recovery state. If an addendum agent
reports all work items complete, green gates, and no open issues, but fails to
set the strict ok=true schema field, the workflow returns
manual-merge-ready instead of treating the branch as ordinary failed work.
That preserves throughput without auto-merging ambiguous output: an operator
must rerun gates, confirm review evidence, reconcile the roadmap checkbox, and
then integrate or discard the branch.
Fresh-run recovery
A fresh launch can also discover branches that survived an earlier failed run.
Set resumePartialBranches=true and choose the maximum action with
resumeMode before launching:
- Assess-only (
resumeMode="assess", the default): the workflow maps survivingroadmap-*branches back to roadmap ids, assesses each candidate with the same ADR 002 contract used for in-run failures, and reports the outcome in a top-levelrecoveryobject. Nothing is merged, pushed, ticked, or deleted, andprocessedis unchanged. Use this mode first: it guarantees the target project is not written to, though recovered branch content still flows to the assessment adapter (see the security guide's data-flow and prompt-injection notes). - Review-mode resume (
resumeMode="review"): in addition to assessment, a candidate classifiedadopt-completethat is clean, committed, task-scoped, and carries validation evidence re-enters the ordinary dual review and merge-lock integration path without re-running implementation. Everything else is still report-only, and anadopt-completeverdict with incomplete evidence is downgraded tocontinue-manualwith an explicit skip reason (dirty-worktree,no-committed-work,not-task-scoped,missing-validation-evidence,evidence-collection-error, oraddendum-branch). Blocking evidence gaps (missingEvidence) still trigger that downgrade, but advisory residual risk (residualRisk) never does: it is non-blocking and is instead carried forward into the resumed dual review and integration prompts as an explicit, clearly delimited advisory section for the reviewer or integrator to weigh. Review-mode resume mutates the target project exactly as ordinary integration does, so grant it the same permissions and trust. - Continue-mode resume (
resumeMode="continue"): no judgement agent at all. The workflow collects host git evidence and reads the committed ExecPlanStatus:line, then dispatches deterministically:DRAFT(or a missing plan) re-enters planning,APPROVEDorIN PROGRESSre-enters implementation,COMPLETEre-enters the dual review, andBLOCKEDis reported. The downstream gates and reviewers are the judgement. Dirty worktrees, addendum branches, evidence-collection failures, and plans the host cannot read are reported instead of resumed (reasons such asdirty-worktree,plan-blocked, andplan-unreadable). Continue-mode resume mutates the target project exactly as ordinary work does.
The recovery result object indexes the pass for operators: candidates,
assessed, resumed, per-candidate results (classification and action), and
skipped entries with machine-readable reasons (including unmapped-branch,
already-complete, missing-worktree, and candidate-cap from discovery).
Ids with surviving branches are held out of normal selection for the rest of
the run, so the pool cannot collide with an existing branch; hoover the branch
or resume it before expecting normal selection to rebuild that task. Survivor
branches that are still unresolved when the run ends are listed in
recovery.unresolved (id, branch, last classification, action, and reason),
and the run reports halted: needs-operator-recovery … instead of a clean
stop, so a blocked roadmap frontier is never mistaken for finished work. A
fatal auth preflight blocks recovery entirely
(recovery.blocked = "auth-preflight-failed"), and dry runs never resume.
Use the df12-build-supervisor skill for the detailed operator playbook:
failure-mode diagnosis, orphan worktree cleanup, remediation triage, stash
hygiene, and deciding when a roadmap frontier is actually dry.
Read docs/security-and-permissions.md before granting a workshop write,
network, or GitHub access. It names the permissions and external services a run
can use, and explains why roadmap, design, audit, and review text must be
treated as prompt-injection input rather than trusted control logic.