| Field | Value |
|---|---|
| Decision ID | ADR-034 |
| Initiative | unsorry swarm reliability — auto-recompose recovery |
| Proposed By | unsorry maintainers |
| Date | 2026-06-14 |
| Status | Accepted |
When all of a decomposed parent’s sub-lemmas are proved, the unblock sweep (ADR-009) re-opens
the parent for recomposition — a fresh prove attempt that assembles the proved subs into the
parent. If that attempt fails, prove_goal() falls through to the uniform AFFINITY_FAIL = -10
demote (swarm/agent.sh, demote_goal). Crucially, decompose_goal is now idempotent (#368):
it refuses to re-decompose a goal that already has a decomposition record, so a failed
recompose can no longer split further — it lands on the demote.
Two failed recomposes (−20) push the parent below TAU_V = -5, where ADR-010 ranking (_rank)
drops it as “non-viable, awaiting re-decomposition.” But re-decomposition is exactly what #368
forbids. The parent can then be neither re-decomposed nor auto-selected — the proved subtree
is stranded and only an operator can recover it (the #380 --goal viability override, or the
#379 affinity restore). The euclid-perfect-numbers recompose (#370) failed its first attempt
and would have buried all six proved leaves had it failed twice; sq-add-sq-eq-three-mul-sq-s4
was stranded at aff≜-10.
The demote-on-fail and the #368 idempotency guard are in direct tension for the recompose case. ADR-016 already establishes the principle that a failure carrying no real evidence against the goal (an infrastructure failure) must not demote it — “twice now a quota outage has demoted a whole tree below τ_v.” A failed recompose is the analogous “recoverable work, don’t bury it” case for a real failure: the subs are proved, the parent is genuinely provable, it just needs another attempt (a stronger rung, a different model).
In the context of the ADR-009 unblock→recompose sweep that re-opens a fully-proved-subtree
parent for assembly and the ADR-010 affinity demote that deprioritises failed goals,
facing the fact that a failed recompose lands on the uniform −10 demote which can drop the
parent below TAU_V, where ADR-010 marks it non-viable “awaiting re-decomposition” — yet the #368
idempotency guard forbids re-decomposing it, so the proved subtree can never auto-close and needs
operator intervention (#379/#380),
we decided for detecting a recompose attempt with a new recompose-candidate predicate (the
goal has a decomposition record whose subs are all proved — the same subs-⊆-proved check
unblockable already computes, minus the status filter) and flooring the demote at TAU_V for
it (new = max(aff − 10, τ_v)), so the parent sinks to lowest-but-viable priority and the sweep
keeps auto-retrying it,
and neglected skipping the demote entirely (leaves a genuinely-stuck recompose at normal
priority — a budget-poisoning loop), leaving it as-is (operator-only recovery defeats the point of
an automatic sweep), and raising TAU_V globally (weakens the viability floor for every goal),
to achieve automatic closure of a proved subtree without an operator un-burying it, while still
deprioritising a recompose the current model cannot yet assemble,
accepting that a floored recompose stays in the retry pool indefinitely (bounded: it ranks
lowest, so real work always goes first), that this is a heuristic over the merged decomposition/
index state (read at the synced repo root), and that an ordinary leaf/undecomposed failure is
unchanged (still the full −10).
Detect the recompose candidate; apply −10 but clamp at TAU_V. Pros: parent stays selectable
(criterion 1) and is deprioritised to lowest priority, so a stuck recompose can’t hog the budget.
Cons: a permanently-unprovable recompose stays in the pool (at lowest priority).
No penalty (mirror ADR-016 exactly). Rejected: a recompose the model genuinely can’t close stays at normal priority and is re-attempted every cycle — a poison goal that wastes the budget, since nothing deprioritises it.
Keep the uniform −10; recover stranded parents via #379/#380. Rejected: the unblock→recompose sweep exists to drive a proved subtree to closure automatically; requiring a human un-bury defeats its purpose.
| Relationship | ADR ID | Title | Notes | |————–|——–|——-|——-| | Refines | ADR-010 | Affinity-Gap Selection | Adds a floored-demote case to the −10/τ_v policy | | Refines | ADR-009 | Goal Decomposition | Makes the unblock→recompose sweep self-recovering | | Relates To | ADR-016 | Infrastructure Failure Guard | Same “don’t bury recoverable work” principle, distinct condition |
| Reference ID | Title | Type | Location | |————–|——-|——|———-| | REF-1 | Recompose no-bury spec | Specification | specs/SPEC-034-A-Recompose-Failure-No-Bury.md | | REF-2 | Decompose idempotency guard (the tension) | Issue/PR | https://github.com/agenticsnz/unsorry/issues/366 | | REF-3 | Tracking issue | Issue | https://github.com/agenticsnz/unsorry/issues/388 |
| Status | Approver | Date | |——–|———-|——| | Proposed | unsorry maintainers | 2026-06-14 | | Accepted | unsorry maintainers | 2026-06-14 |