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
Two designs now answer the same question — "a reset fence must not be a dead end" — and they collided when #1732's stack was rebased onto main. One has to win before #1744 can merge.
main @ a7bfb8ba, and origin/feat/durable-turn-deadlines @ c0216625, both changed Conversations.reset_sandbox/2. The rebase fails on commit 1 of 21, in that function, and every other PR in the stack chains off it.
The same problem, twice
#1744 (ADR 0046) found that an unresolved bounded execution made reset_sandbox/2 answer :sandbox_mid_turn forever: awaiting_identity and uncertain are absorbing states, nothing revisited them, and _unsafe_sandbox_open?/1 blocks reset while either is set. Reset is the owner's documented recovery (#1071), so a provider that never named its session or never acknowledged a stop cost them that recovery permanently. Two exits were added:
ExecutionGuard._unsafe_retire_unresolved/2 — ages an obligation out after an hour, keeping last_error.
reset_sandbox(force: true) — resets past an unresolved execution and retires the journals bound to that machine, via _unsafe_retire_sandbox/1 + audit_retired/1.
#1768 (a7bfb8ba) independently found that a reset whose provider delete was never confirmed stranded the row, and answered it differently:
the decision is made on the row re-read under FOR UPDATE, not on the caller's struct;
only ready/suspended reset — pending and starting are now refused too, as the provision watchdog's business;
reset_requested_at is a durable fence: repeat resets answer :sandbox_reset_pending with no second delete, and so does anything that would re-use the machine;
two audit rows, sandbox.reset_requested when the fence commits and sandbox.reset only when the provider confirms;
and the escape, quoting the docstring: "The fence is not a dead end." A write that retires the row still goes through, so "an operator reaps it from /admin/sandboxes … Reaping is the supported way out of an unconfirmed reset."
The question
Does reset_sandbox(force: true) survive, or does #1768's reaping replace it?
Reaping already gives an operator a way past a stuck reset, and update_sandbox/2 deliberately lets a terminal write through the fence so that agent deletion and account deletion still complete. If that also counts as the way out of a stuck execution fence, then force: is a second lever for one job.
The counter-argument: they are not the same fence. :sandbox_reset_pending means Fountain asked the provider to delete and has not heard back. An open journal means a bounded command may still be running and its termination is unconfirmed — a different fact, on a different row, with a different owner (the deadline coordinator, which ages it out). Reaping is also an operator action from /admin/sandboxes; force: is available to the tenant who owns the machine. If the answer is "reap it", a tenant whose own conversation is fenced has to ask an operator.
Option A — reaping replaces force:
Delete the force: option, its docstring paragraph, and its two regressions in sandbox_reset_test.exs.
Keep _unsafe_retire_unresolved/2 (the ageing exit) — it is the coordinator's, and unaffected.
Re-derive it against fix: fence sandbox resets until deletion succeeds #1768: the force? branch has to sit inside the cond on the re-read row, beside current.reset_requested_at -> :sandbox_reset_pending, and decide whether force: true also overrides that fence or only the execution one.
the @pin in conversation_server_size_test.exs comes down again: main is at 2,567 while the stack ends at 2,729 and main's pin still reads 2,774.
State
All nine PRs (#1744–#1752) are approved and green, and out of draft. #1744 is CONFLICTING; the other eight read MERGEABLE only because each is measured against its parent branch, so nothing lands until this is settled. #1754 stays a draft reference.
Two designs now answer the same question — "a reset fence must not be a dead end" — and they collided when #1732's stack was rebased onto
main. One has to win before #1744 can merge.main@a7bfb8ba, andorigin/feat/durable-turn-deadlines@c0216625, both changedConversations.reset_sandbox/2. The rebase fails on commit 1 of 21, in that function, and every other PR in the stack chains off it.The same problem, twice
#1744 (ADR 0046) found that an unresolved bounded execution made
reset_sandbox/2answer:sandbox_mid_turnforever:awaiting_identityanduncertainare absorbing states, nothing revisited them, and_unsafe_sandbox_open?/1blocks reset while either is set. Reset is the owner's documented recovery (#1071), so a provider that never named its session or never acknowledged a stop cost them that recovery permanently. Two exits were added:ExecutionGuard._unsafe_retire_unresolved/2— ages an obligation out after an hour, keepinglast_error.reset_sandbox(force: true)— resets past an unresolved execution and retires the journals bound to that machine, via_unsafe_retire_sandbox/1+audit_retired/1.#1768 (
a7bfb8ba) independently found that a reset whose provider delete was never confirmed stranded the row, and answered it differently:FOR UPDATE, not on the caller's struct;ready/suspendedreset —pendingandstartingare now refused too, as the provision watchdog's business;reset_requested_atis a durable fence: repeat resets answer:sandbox_reset_pendingwith no second delete, and so does anything that would re-use the machine;sandbox.reset_requestedwhen the fence commits andsandbox.resetonly when the provider confirms;/admin/sandboxes… Reaping is the supported way out of an unconfirmed reset."The question
Does
reset_sandbox(force: true)survive, or does #1768's reaping replace it?Reaping already gives an operator a way past a stuck reset, and
update_sandbox/2deliberately lets a terminal write through the fence so that agent deletion and account deletion still complete. If that also counts as the way out of a stuck execution fence, thenforce:is a second lever for one job.The counter-argument: they are not the same fence.
:sandbox_reset_pendingmeans Fountain asked the provider to delete and has not heard back. An open journal means a bounded command may still be running and its termination is unconfirmed — a different fact, on a different row, with a different owner (the deadline coordinator, which ages it out). Reaping is also an operator action from/admin/sandboxes;force:is available to the tenant who owns the machine. If the answer is "reap it", a tenant whose own conversation is fenced has to ask an operator.Option A — reaping replaces
force:force:option, its docstring paragraph, and its two regressions insandbox_reset_test.exs._unsafe_retire_unresolved/2(the ageing exit) — it is the coordinator's, and unaffected._unsafe_retire_sandbox/1still has to be called where a reset does succeed, inside fix: fence sandbox resets until deletion succeeds #1768's locked decision block, so a destroyed machine's journals do not outlive it.Option B —
force:staysforce?branch has to sit inside thecondon the re-read row, besidecurrent.reset_requested_at -> :sandbox_reset_pending, and decide whetherforce: truealso overrides that fence or only the execution one.sandbox.reset_requested's metadata.Either way, and independent of the answer:
_unsafe_sandbox_open?/1moves into fix: fence sandbox resets until deletion succeeds #1768's lockedcondrather than sitting before it;terminated_at/duration_msfix has to be re-applied — fix: fence sandbox resets until deletion succeeds #1768 changed the retire sequence under it, so thesandbox_terminatedusage row needs re-checking against the new one;@pininconversation_server_size_test.exscomes down again:mainis at 2,567 while the stack ends at 2,729 andmain's pin still reads 2,774.State
All nine PRs (#1744–#1752) are approved and green, and out of draft. #1744 is
CONFLICTING; the other eight readMERGEABLEonly because each is measured against its parent branch, so nothing lands until this is settled. #1754 stays a draft reference.Blocks #1732.