You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
review_valve.t27: name an effect give-up instead of reading it as UNRECORDED #7092
#7059 (PR #7091) makes control.t27 end an attempt with DO_GIVE_UP after EFFECT_RUN_LIMIT runs of one effect key. dispatch.t27 counts it as a real attempt (counts_against_issue(false)). Once retry_allowed is spent, the dispatch escalates.
review_valve.t27 then reads the kind back from the row's counters:
An effect give-up sets none of those flags. Its criteria exist, the reviewer did not send it back, it is not a dead letter, and the reviewer did not give up. So it falls through to KIND_UNRECORDED.
That is bounded: next_step(KIND_UNRECORDED, ...) holds until the floor, releases, and closes after the counted releases. But the reason is lost exactly where the runtime knew it. A turn that crashed its holder three times (a provider or runtime fault) looks the same as a row nobody can explain. It gets the same floor, and the close note says nothing about the crash loop.
Fix
Add KIND_EFFECT_GAVE_UP : u8 = 8 after KIND_BASE_TRUTH_ALONE, so no existing code moves (the BrowserOS runtime mirrors these numbers).
Add escalation_kind_recorded(recorded: u8, ...) or a fifth flag effect_gave_up, checked after reviewer_gave_up. A runtime that records the reason passes it, and a row that does not still reads UNRECORDED.
next_step(KIND_EFFECT_GAVE_UP, ...): release at once (no floor). Another holder on another runtime is the cure for a crash loop, and dispatch's ceiling already bounds the total. Close after the counted releases, with the reason in judged_note.
Tests:
a give-up row reads KIND_EFFECT_GAVE_UP only when recorded;
Implemented locally as 5c67ee3b6. It opens as a PR after #7091 merges (one PR at a time). One design change from this issue:
The give-up waits the 30-minute empty-attempt floor; it is not released at once.
The issue proposed an instant release, on the grounds that another holder on another runtime cures a crash loop. That reasoning fails at the valve:
By the time a row escalates, dispatch has already spent its retries.
A reclaimed task moves to another runtime (actors.t27). So the crash loop has already followed the work across runtimes.
The faults an instant retry would meet again are a deploy restart or a provider quota window, and RETRY_FLOOR_MINUTES exists to wait those out.
So next_step(KIND_EFFECT_GAVE_UP, ...) holds until minute 30, releases once, and then closes. That is the same timing as before. What changes is the name, which the close note can now carry.
Shape:
escalation_kind is unchanged.
The new recorded_escalation_kind(recorded, ...) lets the counters decide first. A record only names a row they leave UNRECORDED, and only with a kind for which is_recorded_kind is true.
Checks:
The kind codes 0..8 are pinned by an invariant. With the invariant removed, 8 -> 9 passes every test.
zig 11/11 passed, 0 vacuous.
tri mutate spec, whole file on the lab: 50 of 50 killed.
Activity
gHashTag commentedon Oct 6, 2026
Implemented locally as 5c67ee3b6. It opens as a PR after #7091 merges (one PR at a time). One design change from this issue:
The give-up waits the 30-minute empty-attempt floor; it is not released at once.
The issue proposed an instant release, on the grounds that another holder on another runtime cures a crash loop. That reasoning fails at the valve:
RETRY_FLOOR_MINUTESexists to wait those out.So
next_step(KIND_EFFECT_GAVE_UP, ...)holds until minute 30, releases once, and then closes. That is the same timing as before. What changes is the name, which the close note can now carry.Shape:
escalation_kindis unchanged.recorded_escalation_kind(recorded, ...)lets the counters decide first. A record only names a row they leave UNRECORDED, and only with a kind for whichis_recorded_kindis true.Checks:
tri mutate spec, whole file on the lab: 50 of 50 killed.