Skip to content

chore(release): 0.16.6 — gate-first execute + error-propagation hardening + kill-migration test pins - #98

Merged
maltsev-dev merged 3 commits into
masterfrom
release/0.16.6
Sep 9, 2026
Merged

chore(release): 0.16.6 — gate-first execute + error-propagation hardening + kill-migration test pins#98
maltsev-dev merged 3 commits into
masterfrom
release/0.16.6

Conversation

@maltsev-dev

Copy link
Copy Markdown
Member

Summary

Three independent fixes closing SDK↔backend drift introduced by backend
DEF-SDKK-022-EXEC-BYPASS (2026-09-04) and the 2026-09-08
WorkflowKilledInterrupt migration. No wire-format change on any of
the 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_var when a prior /gate minted it
      (was unconditionally minting a fresh uuid7_str(), causing
      404 EXECUTION_NOT_FOUND on every @protect @sensitive call
      after the backend's 2026-09-04 ownership-binding existence
      check landed).
    • _enforce_sensitive_tool displays the API key's bound workflow
      via runtime._resolve_workflow_id(...) instead of the
      misleading __nullrun_unknown__ sentinel.
    • NullRunCallback.on_llm_start fires runtime.check_workflow_budget()
      (fail-OPEN, except BaseException guard) so the matching
      llm_call cost event has a reservation and routes via
      /track_single instead of being silently dropped by
      runtime._route_track.
    • Transport.execute docstring rewritten to reflect the
      post-DEF-SDKK-022-EXEC-BYPASS contract ("/execute MUST be
      preceded by /gate for the same execution_id").
    • 9 new regression tests in 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 exposes
      first-class .execution_id and .endpoint attributes for
      cookbook introspection without indexing into details.
    • mypy-clean (self.endpoint: str matches parent's contract).
  • Kill-migration test pins (src/nullrun/_handle.py):

    • The 2026-09-08 WorkflowKilledInterrupt → NullRunError
      migration was correct but broke handle()/guarded() (the
      kill was being swallowed into sys.exit because handle()
      catches NullRunError unconditionally).
    • handle() now re-raises WorkflowKilledInterrupt explicitly
      before the catalog print + sys.exit. guarded() inherits
      for free.
    • 5 existing tests updated to assert the post-migration
      exception names (NullRunBudgetError for budget blocks,
      NullRunWorkflowKilledError for control-plane kill).

Verification

  • pytest -q: 1658 pass / 4 skipped
  • ruff check src tests: clean
  • mypy src/nullrun: 0 issues in 37 source files

Compatibility

Pure reliability fixes — no wire-format change on any of the three.
/gate, /track, /execute, /cancel payloads byte-identical to
0.16.5.

Commits included

  • b654d0d chore(release): 0.16.6 — kill-propagation post-migration + version bump
  • 9e1afc1 fix error propagation
  • 35a3670 fix(reservations)

Tag v0.16.6 will be created on the merge commit.

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.
@maltsev-dev
maltsev-dev merged commit 9877c34 into master Sep 9, 2026
4 checks passed
@maltsev-dev
maltsev-dev deleted the release/0.16.6 branch September 9, 2026 08:53
@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.43689% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/nullrun/runtime.py 59.09% 7 Missing and 2 partials ⚠️
src/nullrun/breaker/exceptions.py 96.00% 1 Missing and 1 partial ⚠️
src/nullrun/instrumentation/auto.py 0.00% 2 Missing ⚠️
src/nullrun/instrumentation/langgraph.py 88.88% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant