Refuse conversation release while remote execution is unresolved - #1752
Conversation
There was a problem hiding this comment.
Posted under the wrong account by mistake (this repo has two authenticated identities and the CLI reverted mid-run). The review itself is on this PR from @BinaryBourbon — read that one; this placeholder carries no findings.
BinaryBourbon
left a comment
There was a problem hiding this comment.
Review: the fence is right, but it takes away a release that used to always work
Commenting, with one thing I would like changed before this merges.
_unsafe_release_parent/2 is well built — lock the parent, check running turns and open executions under that lock, let the caller supply the writer, refuse without retiring or interrupting anything. Threading both the live-server and no-server paths through it so the same durable check applies either way is exactly right, and it fixes a real gap: the old handle_call(:release_conv, ...) dropped the connection and terminated the row before checking anything.
The regression: the previous no-server path was unconditional.
conv ->
{:ok, _} = Conversations.update_conversation(conv, %{status: "terminated"})
:okNow it refuses with :busy whenever a turn row says running — and a conversation with no server and a running turn is a normal state in this system, not a pathological one. wake_for_interrupt/1's own docstring says so: "The process can have exited (deploy, Horde rebalance, a plain {:stop, :normal, _}) while a turn was still marked running." Those are precisely the conversations an owner reaches for release on, and after this PR release is refused until something else reconciles the orphan. The docstring change ("{:error, :busy} while a turn runs or remote execution remains unresolved... The same durable check applies with no server alive") states the new behaviour accurately but does not flag that it is a change.
Layered on the open_execution?/1 half, and on the fact that uncertain and awaiting_identity never resolve (see #1744), release joins reset_sandbox/2 as a second escape hatch that can be closed permanently. I would rather see the running-turn check stay soft on the no-server path (wake and reconcile, then release, as wake_for_interrupt/1 already does), with the hard refusal reserved for the durable execution fence.
Smaller: notify_parent_change/1 correctly falls through on the error tuple, so the |> case do after it reads a little oddly — {:ok, _} -> :ok discards the conversation the broadcast just used. Not wrong, just worth a shape that makes the discard obvious.
Same rebase and the same red hex-audit-gate as #1751 — neither is this PR's doing.
8381e6a to
62969ec
Compare
fbbae0a to
2df81ab
Compare
|
Force-pushed A An unresolved bounded execution refuses either way, as I rewrote your test rather than deleting it. "an absent actor does not authorize release of a running legacy turn" is now "an orphaned running turn does not block release", with a comment saying why the opposite was wrong — that is a direct reversal of stated intent and it should be arguable from the test, not just from a PR comment. Two companions: a live actor's running turn still refuses The On the size pin. The docstring and comments this needed pushed Verification. 13 release/size tests pass. Full core suite: 6 doctests, 5,092 tests, 0 failures, 2 skipped. Format, That is the whole stack — #1744 through #1752 are all rebased onto each other in order and each has its own green suite. #1754 I have left alone; it is still labelled a draft reference and my review on it stands. |
62969ec to
37e3965
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
2df81ab to
30618f8
Compare
BinaryBourbon
left a comment
There was a problem hiding this comment.
Review: release is a fact again, and the two things it was conflating are now named separately
Re-reviewed at 30618f8. This is exactly the split I asked for.
_unsafe_release_parent/3 takes actor_alive?, release_conversation/2 passes false on the branch where whereis/1 already returned nil, and the durable fence stays unconditional under its own name. A running turn row is evidence only when something is there to run it; without a server it is as likely the orphan wake_for_interrupt/1 exists for, and that is the state an owner reaches for release in. The comment says so at the site, which is where the next person will be standing.
Rewriting the old test rather than deleting it, with the reason the opposite was wrong, is the right way to retire an assertion — and the two companions (a live actor's running turn still refuses; the durable fence refuses with and without an actor) pin both halves of the split instead of just the new one. :execution_fenced as a distinct atom from :busy is worth having on its own: one means a turn that ends by itself, the other means remote work Fountain cannot yet account for, and they call for different things from the caller.
One thing to pick up
:execution_fenced has no FallbackController clause. Team.rotate/4 returns it straight out of ConversationServer.release_conversation/2, and team_controller.ex passes anything that is not :busy through untouched — so it lands on the terminal safety net: 422 {"error": "execution_fenced"} with no message, plus a fallback: unmapped error atom warning logged on every refusal. Same for the copy of it that admission raises in #1749.
The shape is already #1431-compliant, so this is polish rather than a defect: one clause gives it a message worth reading ("a remote command from an earlier turn is still unaccounted for; it clears on its own") and stops the warning treating a designed refusal as a miss.
Still open from last time and still trivial: _unsafe_release_conversation/2's |> notify_parent_change() |> case do {:ok, _} -> :ok discards the conversation the broadcast just used, without the shape making the discard obvious.
Approving — checks are green on this head, and this one closes the last of the "a fence with no way out" family across the stack.
37e3965 to
b5534e7
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
30618f8 to
06345ed
Compare
b5534e7 to
cad1e44
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
06345ed to
e5581a5
Compare
cad1e44 to
1e18e2f
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
e5581a5 to
b763ad0
Compare
1e18e2f to
a01e6c9
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
99efa82 to
0a58be2
Compare
e861173 to
30fa08c
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
0a58be2 to
e35d657
Compare
30fa08c to
815f38f
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
e35d657 to
a244b67
Compare
815f38f to
91b414a
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
a244b67 to
2614dd9
Compare
91b414a to
26931d4
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
2614dd9 to
5abec69
Compare
26931d4 to
ca72e26
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
5abec69 to
5bb64d4
Compare
ca72e26 to
ec3453b
Compare
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
5bb64d4 to
44919bc
Compare
Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
Review of #1752 found that the release fence took away a release that always worked. The no-server path was unconditional before this branch; it now refused `:busy` whenever a turn row said `running` — and a conversation with no server and a `running` turn is a normal state, not a pathological one. `wake_for_interrupt/1`'s own docstring says so: "the process can have exited (deploy, Horde rebalance, a plain `{:stop, :normal, _}`) while a turn was still marked `running`". Those are exactly the conversations an owner reaches for release on, and refusing there fenced them out of their own recovery with nothing able to un-fence it — the same shape as the permanent reset refusal on #1744. Split into the two things that were conflated: * a `running` turn row refuses only when a **live** server makes it authoritative. `_unsafe_release_parent/3` takes `actor_alive?`, and `release_conversation/2` passes `false` on the branch where `whereis/1` already returned nil. * an unresolved bounded execution refuses either way, as `:execution_fenced` rather than `:busy`. That is a durable fact rather than an inference from a row, and it is bounded: the coordinator writes an obligation off once nothing can resolve it. The distinct name is worth having on its own. `:busy` means a turn a live actor is running and ends by itself; `:execution_fenced` means remote work Fountain cannot yet account for. The test that asserted the old behaviour is rewritten rather than deleted, and says why the opposite was wrong. Two companions: a live actor's running turn still refuses, and the durable fence refuses with and without an actor. Size pin held at 2733 by trimming the two comments this added rather than raising it. Full core suite: 6 doctests, 5,092 tests, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9reKpmXUUJf4eUMULogie Signed-off-by: Jake Gaylor <jhgaylor@gmail.com>
44919bc to
27ddae3
Compare
Release could succeed with no actor even while its cancelled spawn remained unresolved. Both live and absent-actor paths now check persisted turns and execution journals under the parent lock before changing state or closing anything. Unresolved work returns
busywithout interruption; confirmed cleanup permits release. Unrelated co-tenants keep running on the unchanged sandbox.Stacked on #1751; draft, public execution controls disabled. Tracks #1732. Machine transfer/park/reset/rehome/deletion, provider incarnation and live acceptance remain separate gates.
Full precommit passes 4,765 tests + 6 doctests, zero failures. The focused suite passes 103 tests, including ten new regressions and a real-actor refusal. Sixty independent PostgreSQL races cover release/admission, release/recovery and release/cleanup, plus twenty earlier deadline races. Provider identities and acknowledgments are synthetic; no provider operations occurred. Evidence:
decisions/evidence/release-fence.json.Known CI blocker inherited from #1751: the EEF feed for CVE-2026-32686 currently flags Decimal 3.1.1, while the maintainer advisory identifies 3.0.0 as patched. The dependency audit has not been waived.
CI at the exact release head finished: 18 checks pass, four skip, and the dependency audit plus required rollup fail. Test partitions, coverage, SDKs and release boot pass. The only root failure is EEF-CVE-2026-32686 on Decimal 3.1.1: the maintainer advisory identifies 3.0.0 as patched, but the current EEF feed omits that fixed boundary. The audit has not been waived.
Part of #1732 — held. Superseded on main by #1773–#1793; the residual hunks are being re-cut as focused PRs tracked by #1864, which keeps these frozen until each has a replacement or an explicitly linked deferral. Do not close: they are the reference for that mapping.