Reconciler observed waiting_secret across two reports (16:52+17:07 UTC), re-staged the stored secret at 17:10:00 (no ep0 token, no gen bump); agent re-consumed 17:10:26 and converged (applied) 17:10:28 — no operator click. Demo host untouched (scoped). Fleet widening remains a deliberate follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEPuEwyyGDJdcsXLFsTWJn
3.5 KiB
felhom.eu — task reports
Overwrite this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in hub/CHANGELOG.md; the scripts history lives in scripts/CHANGELOG.md.
PBS-DR self-heal reconciler (hub v0.56.0) — 2026-07-15
Task: implement SPIKE-pbsdr-selfheal-2026-07-15 (e8f8c44) — a hub periodic reconciler that
re-stages a consumable PBS-DR secret for boxes stuck in waiting_secret/consumed_failed after
losing their agent-side converged marker (re-install / restore / snapshot rollback onto a stable
host_id). Class: risky/supervised. Repo: felhom.eu (hub/) only — no agent change (the spike
proved the agent already self-heals once a secret is consumable).
Baselines: hub main e8f8c44 → this work; hub live image v0.55.0 → v0.56.0. Green gate
(go build/vet/test ./... in hub/) passing across the module.
What shipped
internal/store/pbsdr.go—RestageHostPBSSecret(hostID) (bool, error): clearsconsumed_atIFF a secret row exists (no INSERT, no value change, no generation bump);restaged=false→ no row → the caller escalates.PBSDRHealStates(): one query joining each host's descriptor enable/provision flags to its latest reportpbs_dr.state+ id (mirrorsGetHostOOBStates).internal/pbsdrheal/reconciler.go(new) — 5-min reconciler (wgsync shape). Work set = descriptor enabled + provisioned. Per host, on the latest reportpbs_dr.stateheld across a debounce (≥2 distinct reports):waiting_secret→ re-stage the stored secret (no ep0 call, no gen bump), or escalate to Re-issue if none stored;consumed_failed→ escalate to Re-issue only. Converged/disabled/verify_failed/unprovisioned/DR-OFF = pure no-op. Distinct audit events per heal.PBSDRHEAL_ONLY_HOSTscopes a supervised rollout.internal/web/pbsdr.go—ReissuePBSDR(ctx, customerID): the non-HTTP core of the operator Re-issue button, now the reconciler's escalation seam. The operator handler is unchanged (303/400 codes stay test-pinned).cmd/hub/main.go— reconciler started unconditionally;PBSDRHEAL_ONLY_HOSTenv read.
Tests (green) + red-proofs (verified): internal/pbsdrheal/reconciler_test.go (Scenarios A–F +
scope + no-re-heal, real store + fake action seam, audit events asserted); internal/store/pbsdr_test.go
(re-stage semantics + no-generation-bump guard + PBSDRHealStates parsing). All six §10 red-proofs
(A, A-gen, B, C, D, E) failed exactly their guard test then reverted.
Deploy + live validation: built felhom-hub:0.56.0, manifest bump + ArgoCD sync (Synced/Healthy);
first rollout scoped via PBSDRHEAL_ONLY_HOST=demo-vm-felhom-2f4b00 (STOP discipline). Drill live-leg
(qm300 rolled to post_day0_golden136 → stuck waiting_secret): the reconciler observed
waiting_secret across two reports and at 17:10:00 UTC re-staged the stored secret (event
pbsdr_selfheal_restaged; no ep0 token, no gen bump) → agent re-consumed 17:10:26 → converged
applied 17:10:28, hands-free. The demo host demo-felhom-01 was never touched (out of scope).
Fleet-wide widening (remove PBSDRHEAL_ONLY_HOST) is a deliberate operator follow-up, not done
this session.
⚠️ ARCHITECTURE IMPACT: before this, no automatic recovery existed for a box re-installed/restored
onto its stable host_id — PBS-DR (hence escrow + offsite) stayed silently unconverged until a manual
operator Re-issue. This reconciler closes that gap.