Complete autonomous recovery controls

This commit is contained in:
kami
2026-07-30 14:57:25 +04:00
parent 8174400b1a
commit e8fadfc998
18 changed files with 364 additions and 77 deletions
+9 -5
View File
@@ -752,17 +752,21 @@ func (c *Coordinator) rotate(ctx context.Context, hard float64) {
}
ref, err := a.Release(ctx, session)
if err != nil {
// A release failure is operational state, not a silent retry. Keep
// the fenced lease and pane for recovery while durably exposing the
// failed phase to the worker and operator.
_ = c.block(task, "rotation release: "+err.Error())
continue
}
if ref == "" {
_ = c.block(task, "rotation release: empty handoff reference")
continue
}
anchorSHA, err := herdr.HeadSHA(session.Worktree)
if err != nil {
// Cannot certify the anchor: do not release with an invalid
// TaskReleased payload (it would fail validation and strand
// the lease/session). Leave the lease intact for the next
// tick or TTL expiry to reclaim.
// Cannot certify the anchor. Record the fault while retaining the
// owner; an unseen bare continue used to leave this state opaque.
_ = c.block(task, "rotation anchor: "+err.Error())
continue
}
b, _ := json.Marshal(map[string]any{"handoff_ref": ref, "reason": reason, "anchor_sha": anchorSHA, "harness_id": task.Lease.HarnessID, "lease_epoch": task.Lease.Epoch, "expected_version": task.Version})
@@ -1020,7 +1024,7 @@ func (c *Coordinator) rememberSession(taskID string, s herdr.Session) error {
}
func (c *Coordinator) block(t domain.Task, reason string) error {
p := map[string]any{"blocker": reason, "block_reason": string(domain.InferBlockReason(reason)), "pane_state": "unknown", "session_evidence": domain.SessionEvidence{PaneState: "unknown", Source: "coordinator", CheckedAt: time.Now().UTC()}}
p := map[string]any{"blocker": reason, "block_reason": string(domain.InferBlockReason(reason)), "lifecycle_phase": "needs_attention", "last_error": reason, "pane_state": "unknown", "session_evidence": domain.SessionEvidence{PaneState: "unknown", Source: "coordinator", CheckedAt: time.Now().UTC()}}
if s, ok := c.Session(t.ID); ok {
p["pane_id"] = s.PaneID
p["harness_id"] = s.HerdrID