chore(release): 0.16.6 — gate-first execute + error-propagation hardening + kill-migration test pins - #98
Merged
Merged
Conversation
The 0.16.6 release prep (fix(reservations) + fix error propagation
on master) moved WorkflowKilledInterrupt onto the NullRunError MRO
(see breaker/exceptions.py:1354 - 2026-09-08 migration). That migration
is intentional - Sentry/OTel 'except Exception' handlers should now
record kill events - but it had three knock-on defects that block a
green test suite + mypy run:
1. _handle.py / guarded() swallowed the kill signal. handle() catches
NullRunError to translate SDK failures into a friendly sys.exit
print; post-migration WorkflowKilledInterrupt is a NullRunError
subclass, so handle() was catching the kill and converting it
into sys.exit(1) - the exact thing the docstring promised NOT to
do. Fix: explicit 'isinstance(exc, WorkflowKilledInterrupt)'
re-raise before the catalog print + sys.exit. guarded() inherits
the behaviour for free via the same handle() context manager.
2. tests + mypy assumed the pre-migration shape.
- tests/test_handle.py used the (BaseException) wording that
predates the migration - now stale, refreshed to 'must NOT
be swallowed' without the BaseException claim.
- tests/test_preflight_fail_policy.py,
tests/test_observability.py,
tests/test_v3_wire_contract.py expected check_workflow_budget
to raise WorkflowKilledInterrupt on decision=block. The
runtime raises NullRunBudgetError (NullRunBlockedException
subclass) for budget blocks - kill is reserved for the
control-plane path. Assertions updated, imports updated.
- tests/test_ws_push.py expected the deprecated
WorkflowKilledException alias; check_control_plane raises
the typed NullRunWorkflowKilledError post-migration.
3. mypy: NullRunExecutionNotFoundError.__init__ re-declared
self.endpoint as 'str | None' after super().__init__ had set it
to a non-None 'str' (parent NullRunTransportError.endpoint is
typed 'str'). Narrowed to 'str' to match the parent's contract;
the runtime value is always 'endpoint or "/api/v1/execute"'.
Plus the missed __version__.py bump: pyproject.toml was already
0.16.6 in fix(reservations) but __version__.py still said 0.16.5.
This is the matching bump.
No wire-format change. Verified: pytest -q 1658 pass / 4 skipped;
ruff check src tests clean; mypy src/nullrun no issues reported in
37 source files.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three independent fixes closing SDK↔backend drift introduced by backend
DEF-SDKK-022-EXEC-BYPASS(2026-09-04) and the 2026-09-08WorkflowKilledInterruptmigration. No wire-format change on any ofthe three.
Fixed
Gate-first execute (
src/nullrun/runtime.py,src/nullrun/decorators.py,src/nullrun/instrumentation/langgraph.py):runtime.execute()reuses the server-minted execution_id from_server_minted_execution_id_varwhen a prior/gateminted it(was unconditionally minting a fresh
uuid7_str(), causing404
EXECUTION_NOT_FOUNDon every@protect @sensitivecallafter the backend's 2026-09-04 ownership-binding existence
check landed).
_enforce_sensitive_tooldisplays the API key's bound workflowvia
runtime._resolve_workflow_id(...)instead of themisleading
__nullrun_unknown__sentinel.NullRunCallback.on_llm_startfiresruntime.check_workflow_budget()(fail-OPEN,
except BaseExceptionguard) so the matchingllm_callcost event has a reservation and routes via/track_singleinstead of being silently dropped byruntime._route_track.Transport.executedocstring rewritten to reflect thepost-
DEF-SDKK-022-EXEC-BYPASScontract ("/execute MUST bepreceded by /gate for the same execution_id").
tests/test_2026_09_08_gate_first_execute.py.Error propagation (
src/nullrun/breaker/exceptions.py,src/nullrun/messages.py,src/nullrun/transport.py):NullRunExecutionNotFoundError(NR-EX01) now exposesfirst-class
.execution_idand.endpointattributes forcookbook introspection without indexing into
details.self.endpoint: strmatches parent's contract).Kill-migration test pins (
src/nullrun/_handle.py):WorkflowKilledInterrupt → NullRunErrormigration was correct but broke
handle()/guarded()(thekill was being swallowed into
sys.exitbecausehandle()catches
NullRunErrorunconditionally).handle()now re-raisesWorkflowKilledInterruptexplicitlybefore the catalog print +
sys.exit.guarded()inheritsfor free.
exception names (
NullRunBudgetErrorfor budget blocks,NullRunWorkflowKilledErrorfor control-plane kill).Verification
pytest -q: 1658 pass / 4 skippedruff check src tests: cleanmypy src/nullrun: 0 issues in 37 source filesCompatibility
Pure reliability fixes — no wire-format change on any of the three.
/gate,/track,/execute,/cancelpayloads byte-identical to0.16.5.
Commits included
b654d0dchore(release): 0.16.6 — kill-propagation post-migration + version bump9e1afc1fix error propagation35a3670fix(reservations)Tag
v0.16.6will be created on the merge commit.