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:
@@ -225,7 +225,11 @@ func (m Model) handleKey(k keyMsg) (tea.Model, tea.Cmd) {
|
||||
// On the idle launcher, Tab cycles the launch target (chat → workflows → chat) in any
|
||||
// edit mode, so it works whether or not the input is focused.
|
||||
if m.displayState() == StateIdle && k.Type == keyTab {
|
||||
m.cycleLauncherWf()
|
||||
if k.Shift {
|
||||
m.cycleLauncherWfBack()
|
||||
} else {
|
||||
m.cycleLauncherWf()
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
if m.editMode == ModeInsert {
|
||||
@@ -1290,6 +1294,12 @@ func (m *Model) cycleLauncherWf() {
|
||||
m.launcherWf = (m.launcherWf + 1) % (len(m.workflows) + 1)
|
||||
}
|
||||
|
||||
// cycleLauncherWfBack is cycleLauncherWf in reverse, for Shift+Tab.
|
||||
func (m *Model) cycleLauncherWfBack() {
|
||||
n := len(m.workflows) + 1
|
||||
m.launcherWf = (m.launcherWf - 1 + n) % n
|
||||
}
|
||||
|
||||
// cycleRightPanel advances the in-session right panel: events → changes → off → events.
|
||||
func (m *Model) cycleRightPanel() {
|
||||
m.rightPanel = (m.rightPanel + 1) % 3
|
||||
|
||||
Reference in New Issue
Block a user