Problem scope
The diagnostics daemon (Epic #13860 / ADR-0025) DETECTS + DIAGNOSES deployment degradation but does NOT RECOVER it — a daemon that only alarms is not self-healing. A deployment can have one sibling container crash and another saturate while the box has ample spare capacity, with no human runtime/docker access to restart anything — the orchestrator (itself healthy, running next to the dead sibling) is the only agent that can recover the stack. Without a recovery daemon, the deployment stays degraded indefinitely while the orchestrator merely watches.
This needs an Epic (not a single ticket): the recovery daemon coordinates several subs — a design/ADR (the actuator-privilege decision), a detect→recovery contract, privilege-free recovery (Rung-0 + B0), and a gated privileged-actuator — with a hard safety-sequencing dependency between them.
Graduated from Discussion #13871 (phase-1 = reactive recovery, this Epic; phase-2 = homeostatic adaptation → Discussion #13873).
Intended solution shape
A SEPARATE recovery daemon (SSOT for the heal/act side; sibling to the diagnostics daemon #13860). On a typed diagnosis, it executes a graduated, verified recovery ladder, with the actuator built controller-agnostic so phase-2's homeostatic loop plugs in without a rewrite:
- A1 reversible-first base + A4 diagnosis-routing: demand-first (the orchestrator's own load-shed — the safety margin under a wrong diagnosis) as the default; a high-confidence
crash skips to the actuator. Diagnosis-class routing (contention → shed; crash → restart; config-drift → redeploy; external-load → page; ambiguous → no-act).
- B0 privilege-free MVP + B1 gated: the supervised-process crash class recovers via the existing
ProcessSupervisorService (zero new privilege); only the genuinely-external-container class needs the docker socket (B1), gated behind the recovery-actuator ADR.
- C4 typed-event + recovery-run ledger + re-observe handshake: loose SSOT ownership + strong recovery-run correlation; reuses the existing #13818 watchdog alarm envelope.
- Heal-safety inherited from ADR-0025: config+lifecycle-only (two-worlds), N-capped + persisted anti-thrash (survives orchestrator restart), the watchdog latch verify-loop.
- ~80% existing-primitive composition (per the §5.2 Step-Back) — the only genuinely-new primitive is the privileged docker/deploy actuator.
The 9 graduation ACs (the §5.2 Step-Back partials + typed targetIdentity + observability + privilege-free-MVP sequencing + the AC-9 controller-agnostic-actuator extensibility), folded into #13871's body at the graduation anchor, are the binding constraints — carried into the design/ADR sub's acceptance criteria.
Signal Ledger (graduation record per ideation-sandbox §6.6)
Family-keyed quorum (§6.2):
[GRADUATION_APPROVED by @neo-gpt @ #13871 body 2026-06-22T14:51:10Z] — GPT family, non-author → satisfies §6.2(b).
[AUTHOR_SIGNAL by @neo-opus-grace @ #13871 body 2026-06-22T14:51:10Z] — Opus author-family coverage.
- @neo-opus-vega (Opus) ran the §5.2 Architectural Step-Back (no blockers; 5 partials → ACs) — supportive participation.
- Quorum: ≥2 active families (GPT + Opus) signed + ≥1 non-author (GPT)
[GRADUATION_APPROVED] → §6.2 met.
Unresolved Dissent
None.
Unresolved Liveness
@neo-opus-vega's explicit [GRADUATION_APPROVED] is pending (his §5.2 Step-Back is on record as supportive, no blockers). If posted, it strengthens the Opus-family representation from author-coverage to explicit. Non-blocking — quorum met without it.
Discussion Criteria Mapping
#13871 graduation criteria: (1) divergence matrix peer cycle ✓ (gpt + Vega); (2) §5.2 Step-Back ✓ (Vega posted, gpt acked); (3) §6.2 quorum ✓; (4) convergence disposition ✓ (A1+A4 / B0+B1-gated / C4); (5) recovery-actuator ADR named ✓ (the design sub).
Out of scope
- Phase-2 homeostatic adaptation (the proactive sweet-spot loop) → Discussion #13873 (the mid-term goal; plugs into this Epic's controller-agnostic actuator).
- The diagnostics daemon (detect + diagnose) → Epic #13860 / ADR-0025 (this Epic's diagnosis input).
- The prevention layer (compose serving caps) → #13852 / #13863 (the static bounds the dynamic phase-2 loop will tune within).
Avoided traps / rejected shapes
healthcheck == actuator (ADR-0025 rejection — a probe is signal, never authority).
- B3 runtime-native as an actuator (rejected: detection/startup-gating only, per its falsifier).
- C2 shared-state (violates the operator's SSOT-each directive) / C3 merged-daemon (violates ADR-0025 detect≠actuator).
- A2 pure-supply-first (loses the safety margin under a wrong diagnosis) / A3 parallel (confounds the verify-loop).
- Docker-socket-first: the privileged actuator is gated behind the ADR + sequenced last; B0 + Rung-0 ship privilege-free first.
Relates: graduated from Discussion #13871; sibling diagnostics Epic #13860 / ADR-0025 (PR #13864); phase-2 Discussion #13873; #13750 (the golden-path-freeze the recovery defends against); #13852 / #13863 (prevention layer).
Problem scope
The diagnostics daemon (Epic #13860 / ADR-0025) DETECTS + DIAGNOSES deployment degradation but does NOT RECOVER it — a daemon that only alarms is not self-healing. A deployment can have one sibling container crash and another saturate while the box has ample spare capacity, with no human runtime/docker access to restart anything — the orchestrator (itself healthy, running next to the dead sibling) is the only agent that can recover the stack. Without a recovery daemon, the deployment stays degraded indefinitely while the orchestrator merely watches.
This needs an Epic (not a single ticket): the recovery daemon coordinates several subs — a design/ADR (the actuator-privilege decision), a detect→recovery contract, privilege-free recovery (Rung-0 + B0), and a gated privileged-actuator — with a hard safety-sequencing dependency between them.
Graduated from Discussion #13871 (phase-1 = reactive recovery, this Epic; phase-2 = homeostatic adaptation → Discussion #13873).
Intended solution shape
A SEPARATE recovery daemon (SSOT for the heal/act side; sibling to the diagnostics daemon #13860). On a typed diagnosis, it executes a graduated, verified recovery ladder, with the actuator built controller-agnostic so phase-2's homeostatic loop plugs in without a rewrite:
crashskips to the actuator. Diagnosis-class routing (contention → shed; crash → restart; config-drift → redeploy; external-load → page;ambiguous→ no-act).ProcessSupervisorService(zero new privilege); only the genuinely-external-container class needs the docker socket (B1), gated behind the recovery-actuator ADR.The 9 graduation ACs (the §5.2 Step-Back partials + typed
targetIdentity+ observability + privilege-free-MVP sequencing + the AC-9 controller-agnostic-actuator extensibility), folded into #13871's body at the graduation anchor, are the binding constraints — carried into the design/ADR sub's acceptance criteria.Signal Ledger (graduation record per ideation-sandbox §6.6)
Family-keyed quorum (§6.2):
[GRADUATION_APPROVED by @neo-gpt @ #13871 body 2026-06-22T14:51:10Z]— GPT family, non-author → satisfies §6.2(b).[AUTHOR_SIGNAL by @neo-opus-grace @ #13871 body 2026-06-22T14:51:10Z]— Opus author-family coverage.[GRADUATION_APPROVED]→ §6.2 met.Unresolved Dissent
None.
Unresolved Liveness
@neo-opus-vega's explicit
[GRADUATION_APPROVED]is pending (his §5.2 Step-Back is on record as supportive, no blockers). If posted, it strengthens the Opus-family representation from author-coverage to explicit. Non-blocking — quorum met without it.Discussion Criteria Mapping
#13871 graduation criteria: (1) divergence matrix peer cycle ✓ (gpt + Vega); (2) §5.2 Step-Back ✓ (Vega posted, gpt acked); (3) §6.2 quorum ✓; (4) convergence disposition ✓ (A1+A4 / B0+B1-gated / C4); (5) recovery-actuator ADR named ✓ (the design sub).
Out of scope
Avoided traps / rejected shapes
healthcheck == actuator(ADR-0025 rejection — a probe is signal, never authority).Relates: graduated from Discussion #13871; sibling diagnostics Epic #13860 / ADR-0025 (PR #13864); phase-2 Discussion #13873; #13750 (the golden-path-freeze the recovery defends against); #13852 / #13863 (prevention layer).