feat(recovery): route failed write-less stages to recovery instead of futile retry

Adds the failure-ticket + recovery-routing mechanism: a deterministic
gate->capability table gates whether a stage has agency to fix its own
failure, opens a FailureTicketOpenedEvent when it doesn't, and routes to
a metadata role=recovery stage (per-stage budget, cap 2, not reset by
TransitionExecuted) instead of retrying in place. Extends salvage
decisions with a RECOVER option so the review-gate judge can also route
to recovery, unifies deterministic-gate and review-gate routing through
routeToRecovery(), and has ExecutionPlanCompiler synthesize a
write-capable recovery stage + edge for freestyle plans. Read-only tools
(file_read, list_dir) are now always available on any tool-granting
stage so a recovery stage can inspect the write-less stage's failure
without flooding context via shell ls -R.
This commit is contained in:
2026-07-08 10:28:53 +04:00
parent d6bada6f10
commit f51a8dada4
21 changed files with 325 additions and 29 deletions
+3 -2
View File
@@ -908,7 +908,7 @@ func shortPath(p string) string {
// fill left-justifies a styled line and pads with bg to width w.
func (m Model) fill(s string, w int, bg color.Color) string {
return " " + padTo(s, w-1, bg)
return lipgloss.NewStyle().Background(bg).Render(" ") + padTo(s, w-1, bg)
}
// justify places left and right segments on a bg-filled line of width w, truncating
@@ -936,7 +936,8 @@ func (m Model) justify(left, right string, w int, bg color.Color) string {
mid = 1
}
pad := lipgloss.NewStyle().Background(bg).Render(strings.Repeat(" ", mid))
return " " + left + pad + right + lipgloss.NewStyle().Background(bg).Render(" ")
edge := lipgloss.NewStyle().Background(bg).Render(" ")
return edge + left + pad + right + edge
}
// padTo pads (or truncates) a possibly-styled string to visible width w, filling