Preserve control exceptions across Future await #163

Closed
lost-rob0t wants to merge 3 commits from fix/162-async-control-exceptions into fix/158-prompt-compiler-closed-dicts
lost-rob0t commented 2026-08-21 23:04:35 +00:00 (Migrated from github.com)

Closes #162 after #159 lands.

The restored aggregate suite proved the canonical async boundary was swallowing domain control exceptions. MCP correctly rethrows cancellation/time-limit signals internally, but rlm_async converted every worker exception into an ordinary printable async_error, so synchronous facades awaiting the same Future could no longer preserve control flow.

TDD

The branch first adds rlm_async_control_test.pl and registers it in the aggregate runner. The red contract requires:

  • time_limit_exceeded to survive submit -> await;
  • rlm_cancelled(Token) to survive submit -> await;
  • ordinary exceptions to remain structured rather than thrown.

Fix

  • keep explicit rlm_async_cancelled(FutureId) handling unchanged for rlm_future_cancel/2;
  • classify a closed allow-list of runtime control exceptions at the worker boundary;
  • store those as async_error{kind:control_exception, exception:Safe, exception_term:Exception,...} so status/callback/continuation inspection remains inert data;
  • make only rlm_future_await/[2,3] rethrow the preserved term;
  • keep ordinary exceptions on the existing kind:exception stringified path.

This keeps sync facades on canonical async submission; no direct synchronous bypass is added.

Keep draft until exact-head CI proves the new Future tests and the previously hidden MCP cancellation/time-limit regressions pass under the restored aggregate suite.

Closes #162 after #159 lands. The restored aggregate suite proved the canonical async boundary was swallowing domain control exceptions. MCP correctly rethrows cancellation/time-limit signals internally, but `rlm_async` converted every worker exception into an ordinary printable `async_error`, so synchronous facades awaiting the same Future could no longer preserve control flow. ## TDD The branch first adds `rlm_async_control_test.pl` and registers it in the aggregate runner. The red contract requires: - `time_limit_exceeded` to survive submit -> await; - `rlm_cancelled(Token)` to survive submit -> await; - ordinary exceptions to remain structured rather than thrown. ## Fix - keep explicit `rlm_async_cancelled(FutureId)` handling unchanged for `rlm_future_cancel/2`; - classify a closed allow-list of runtime control exceptions at the worker boundary; - store those as `async_error{kind:control_exception, exception:Safe, exception_term:Exception,...}` so status/callback/continuation inspection remains inert data; - make only `rlm_future_await/[2,3]` rethrow the preserved term; - keep ordinary exceptions on the existing `kind:exception` stringified path. This keeps sync facades on canonical async submission; no direct synchronous bypass is added. Keep draft until exact-head CI proves the new Future tests and the previously hidden MCP cancellation/time-limit regressions pass under the restored aggregate suite.
lost-rob0t commented 2026-08-24 08:54:07 +00:00 (Migrated from github.com)

Verified against exact current main ab426de54a54101ca3a6d8197db77e4c1f9d009f: this behavior is already present and the stale PR head would regress/remove newer aggregate-runner, OpenRouter, Agent Zero adaptor, and async cancellation-worker work. Focused async/control/MCP/canonical gate: 48/48 passed. Fresh aggregate: 74 suites, 767/767 passed, 0 failed/timeout/blocked/fixme. test/load_all.pl and git diff --check also passed. Closing as superseded; no code from this stale head is needed.

Verified against exact current main `ab426de54a54101ca3a6d8197db77e4c1f9d009f`: this behavior is already present and the stale PR head would regress/remove newer aggregate-runner, OpenRouter, Agent Zero adaptor, and async cancellation-worker work. Focused async/control/MCP/canonical gate: 48/48 passed. Fresh aggregate: 74 suites, 767/767 passed, 0 failed/timeout/blocked/fixme. `test/load_all.pl` and `git diff --check` also passed. Closing as superseded; no code from this stale head is needed.
nsaspy changed title from WIP: Preserve control exceptions across Future await to Preserve control exceptions across Future await 2026-08-27 20:02:12 +00:00
Some checks failed
CI / Deterministic unit and load checks (push) Has been cancelled
CI / REAL OpenRouter integration (push) Has been cancelled
Nix flake / Flake package and clean runtime load (push) Has been cancelled
Clean SWI pack install / Install and load copied pack (push) Has been cancelled
Runner integrity / runner-integrity (push) Has been cancelled
Tree-sitter FFI / Direct SWI-Prolog Tree-sitter FFI (push) Has been cancelled

Pull request closed

Sign in to join this conversation.
No description provided.