chore(release): 0.15.0 — ADR-009 P1 governance audit read surface + sprint cleanup - #88
Merged
Merged
Conversation
… dup CHANGELOG P1 — dead code: - Remove deprecated start_recording/stop_recording no-op stubs from runtime.py (replaced by direct return-value gates; tests for them removed). - Delete breaker/__main__.py stub (was a no-op CLI entry point). - Delete unused import warnings in runtime.py after the deprecated stubs. P2 — redundant tests: - Delete one-shot fix-dump tests (test_<fix_name>.py) whose only purpose was to bump coverage for a single audit/fix commit: test_blocker_fixes, test_high_reliability_fixes, test_medium_hygiene_fixes, test_release_polish, test_drift_fixes_2026_07_04, test_kill_deprecation. - Delete obsolete tests: test_dead_code_removed (the audited code is gone), test_breaker_main (its stub target was deleted), test_grpc_removed (no gRPC code exists), test_kill_contract, test_legacy_key_warning. - Consolidate test_X_branches.py into test_X.py: test_runtime_branches, test_transport_branches, test_protect_branches, test_actions_context_init. - Consolidate test_v3_server_minted.py and test_v3_38_drift_fixes.py into test_v3_wire_contract.py. P3 — memoir comments: - Strip historical-context / fix-narrative / ADR-reference / pre-fix commentary from 90% of files (transport.py / runtime.py / decorators.py / breaker/exceptions.py / observability/__init__.py / test_runtime.py / test_protect.py / test_actions.py / test_transport.py / test_v3_wire_contract.py / conftest.py). Docstrings compressed to 1-2 lines per method; inline marker comments (T4 (...), P0-4, FIX-F3, PR #N, 2026-07-02, ADR-008, observed: ..., pre-fix, ...) collapsed to a single short line. - Replace 'Merged from X.py' section markers with semantic headers. P4 — CHANGELOG deduplication: - src/nullrun/__version__.py: 1192 -> 9 lines (kept just the version constants; the full release history lives in CHANGELOG.md). - pyproject.toml: removed ~180 lines of inline release-history comments duplicated from __version__.py; only the current version is pinned. Verification: 1341 passed, 7 skipped, 2 warnings in 77.86s.
…ghten CHANGELOG
Dockerfile:
- Drop the broken ENTRYPOINT [python, -m, nullrun.breaker]: nullrun.breaker
is a package with no __main__.py and no console_scripts entry in
pyproject.toml. The SDK is a library, not a service. Image now
ships as a base layer; 'docker run <image> python -m your_agent'
covers normal usage. No CI workflow ever built this image (orphan).
Dockerfile.dev:
- Delete. 404 B, CMD 'tail -f /dev/null' antipattern, no CI consumer.
docs/assets/banner.svg:
- Delete. 151 KB; 139 KB of that is a single base64-embedded PNG of
the logo on line 102. Nothing in the tracked repo (README, docs/,
pyproject, CI, mkdocs) references this file. Original is
recoverable from git history if needed.
CHANGELOG.md:
- Drop 126 KB -> 52 KB (-59%), 2035 -> 865 lines. Three trimming
passes:
1. Lift verbose '### Tests' subsections into a one-liner; strip
'### Refs' entirely (external report URLs go stale).
2. Compress '### Compatibility' to first bullet + soft-truncate
bullets > 180 chars.
3. Cap each release entry to max 35 lines. The 8 most-recent
releases (0.14.x + 0.13.13/0.13.12) keep their full ~30-line
detail; older entries get a 'see git log <version>' pointer
for the full change set.
Total: 4 files changed, 96 insertions(+), 1516 deletions(-).
…comments
CATEGORY 2 (memoirs / dangling comments + Cyrillic scrub):
- runtime.py: -289 lines
- 32-line 'Readme correction (2026-07-04)' trimmed to 8 lines
- 4 dangling '2026-07-04 (v0.12.0 wiring fix -- ):' comments
replaced or removed
- 38-line _route_track RFC-style docstring compressed to 13
- Local enforcement / approval pending / GIL / Hot path /
_fetch_remote_state / check_workflow_budget / _auth_headers /
chain_end / _check_local_limits / NullRunBlockedException /
_build_v3_track_payload trailing date comments all trimmed
- extractor.py: -155 lines
- 154-line module docstring compressed to ~40-line 'Validation
contract' summary (kept the unit-discriminator / fail-CLOSED
invariants)
- context.py: -61 lines
- 62-line 'Server-minted execution_id' audit block compressed
to 14-line summary
- tests/test_runtime.py: -57 lines
- All Cyrillic (header, docstrings, inline comments) replaced
with English
- tests/test_v3_wire_contract.py: -5 lines
- Audit comment in test_default_value_is_none rewritten
- CHANGELOG.md: -2 lines
- 'Разрыв 2' -> 'Breakpoint-2', 'Разрыв 1c' -> 'approval field'
No semantic change. python -c imports OK, pytest --collect-only
collects 1336 tests, smoke test of 30 affected tests passes.
Follow-up: dead-code, duplication, CHANGELOG bloat, CI/build, docs.
#1 Dead code - extractor: drop _cached_signature (lru_cache helper, never called) and compute_impact_digest (thin alias, no callers); remove unused imports (functools, Optional, Union). - transport_websocket: drop duplicate compute_hmac_signature + verify_hmac_signature (byte-identical to transport.py); re-export from transport. Update test imports. - transport: verify_hmac_signature accepts str|bytes body for parity with the deleted websocket copy. - _singleton: drop install_module_proxy module-proxy shim (never installed; __all__.append now removed). - _registry: drop replace_for_test (no callers). - context: drop set_trace_id / reset_trace_id / clear_trace_id (legacy contextvar helpers, never imported). - runtime: drop _start_transport, _trigger_action, get_org_status, _workflow_start_time (test-only or unreferenced). #3 Duplicated logic - instrumentation/langgraph: collapse 5-branch usage extraction into _read_token_attrs + _apply_usage, single sources-loop. - instrumentation/auto: hoist shared _rebuild_response out of sync + async transports; hoist shared _build_llm_call_event so the dedup fingerprint stays identical across sync/async httpx paths. - decorators: consolidate _stamp_extractor_on_innermost + _find_extractor_in_chain behind _walk_wrapped_chain generator with cycle guard. - decorators: extract _protect_body context manager so sync/async wrappers share the four pre-execution gates and span_end emission; unify_block=False preserves the async-path behaviour of propagating WorkflowKilledInterrupt unchanged (asyncio task cancellation relies on the original BaseException subtype). Tests: 1334 pass, 2 skip (pre-existing).
#5 CHANGELOG bloat - Drop WIP [0.10.0] stub (Unreleased work-in-progress, never shipped as standalone release; 0.11.0 became the canonical v3.0 cut). - Drop 13 Trimmed-stub lines pointing at git log; close one dangling sub-bullet left by the removal. - Reorder release blocks in strict descending version order: was 0.9.1 -> 0.11.0 -> 0.9.0 (lower: 0.3.1 -> 0.5.2 -> 0.4.0); now 0.11.0 -> 0.9.1 -> 0.9.0 (lower: 0.5.2 -> 0.4.0 -> 0.3.1). Net: -29 lines, semver -> date sort invariant holds. #6 CI/build artifacts - Drop Makefile run-example target (referenced examples/basic.py; examples/ was deleted in 0.3.1 alongside the gRPC transport). Local smoke testing now goes through smoke-test (wheels the SDK and verifies `from nullrun import protect`). - Rewrite Makefile coverage target to match CI: was `coverage run -m pytest tests/` (only traced xdist coordinator, so parallel runs uploaded 0 hits); now `pytest tests/ --cov=src/nullrun --cov-branch --cov-report=xml:coverage.xml --cov-report=term`, matching .github/workflows/ci.yml:82. - clean target now also removes coverage.xml. #7 Documentation gaps - Add 9 missing error-code docs (codes declared in source without a per-code page): NR-A004, NR-B003, NR-C000, NR-C004, NR-CH001, NR-O001, NR-P001, NR-R002, NR-W004. - Add three new catalogue categories: Protocol (NR-P), Chain (NR-CH), Overbudget (NR-O). README.md catalogue now covers all 23 documented codes. NR-X001 stays in the README fallback table (no separate page; it's the generic unknown-code fallback). Verified via cross-check: all source-referenced codes are documented. Tests: 23/23 exception hierarchy pass; full suite remains green.
Bump __version__ 0.14.9 -> 0.14.10 and add the matching CHANGELOG entry. Patch release; strictly internal cleanup with no behavioural change, no SDK_MIN_VERSION bump, no wire-format change. Backward-compatible drop-in for 0.14.9. This release consolidates the three sprint-5 cleanup commits on cleanup/p1p2-dead-code-tests: - #1 Dead code (383 lines, 6 files): extractor cache helpers, duplicate HMAC signatures, install_module_proxy, replace_for_test, context set/reset/clear_trace_id, runtime._start_transport + _trigger_action + get_org_status + _workflow_start_time. - #3 Duplicated logic (~250 lines, 4 files): shared _rebuild_response + _build_llm_call_event across sync/async transports; _protect_body context manager for sync/async @Protect; _read_token_attrs + _apply_usage in langgraph usage extraction; _walk_wrapped_chain generator for decorator chain walks. - #5 CHANGELOG bloat (-29 lines): dropped WIP [0.10.0] stub + 13 Trimmed placeholders; fixed descending-version sort order. - #6 CI/build: dropped Makefile run-example (missing examples/basic.py); rewrote coverage target to match CI's pytest --cov pipeline. - #7 Documentation gaps: 9 new error-code docs (NR-A004, NR-B003, NR-C000, NR-C004, NR-CH001, NR-O001, NR-P001, NR-R002, NR-W004); three new catalogue categories (Protocol, Chain, Overbudget). Tests: 1334 pass, 2 skip (pre-existing); 23/23 exception hierarchy pass. No public API change.
…r (DEF-ERRHDL-AUTH-PATH-CODE-PIN-01, RUN_ID 20260811-1)
Pre-fix, /auth/verify raised NullRunAuthenticationError (NR-A001) for
ANY non-200 status, including 5xx (500/502/503/504). The canonical
dispatcher at transport._parse_v3_error_envelope (used by /check and
/track) correctly maps 5xx -> NullRunBackendError (NR-B002) and 401
with wire envelope -> NullRunAuthError (NR-A003, wire_code set per
v3.38). The auth path open-coded its own (incorrect) mapping, producing
a class-misclassification that misleads operators to rotate valid keys
during backend outages.
Fix: route non-200 auth responses through _parse_v3_error_envelope,
matching the dispatcher /check and /track use. Lazy import inside the
else arm keeps runtime.py's top-level import graph stable.
Mapping after the fix:
401 + envelope -> NullRunAuthError (NR-A003, wire_code set)
401 + empty body -> NullRunAuthenticationError (back-compat fallback)
5xx (500..504) -> NullRunBackendError (NR-B002, retryable)
429 -> RateLimitError (NR-R001, retry_after honored)
other 4xx -> NullRunBackendError with status_code set
NullRunAuthError is a subclass of NullRunAuthenticationError, so existing
'except NullRunAuthenticationError' clauses still match. No wire
contract changes (response shapes unchanged); SDK-side taxonomy
additions only.
Tests: 5 new regression tests in tests/test_runtime.py pin the
per-status mapping. test_authenticate_5xx_raises_backend_error_not_auth_error
(parametrized [500/502/503/504]) verifies the 5xx->NullRunBackendError
classification. test_authenticate_401_with_wire_envelope_surfaces_wire_code
verifies the v3.38 wire_code contract for /auth/verify.
Verification: pytest tests/test_runtime.py 63/63 PASS (+5 new);
pytest tests/ 1339 PASS, 2 SKIP (Windows-specific), 2 deprecation
warnings (unrelated).
Also closes: DEF-ERRHDL-5XX-MISCLASS-01 (RUN_ID 20260810-2),
DEF-ERRFLOW-5XX-MISCLASS-01 (RUN_ID 20260809-1 / S10 cycle-1),
and the 401 wire-code granularity gap from v3.38 in the auth path.
Re-test: S10 cycle-1 retest should attempt /auth/verify with mock
500/502/504 and confirm NullRunBackendError (NR-B002) - not
NullRunAuthenticationError. Plus attempt 401 with
'{"error_code": "API_KEY_REVOKED"}' envelope and confirm
NullRunAuthError.wire_code == 'API_KEY_REVOKED'.
…pe parser (DEF-ERRHDL-AUTH-PATH-CODE-PIN-01, RUN_ID 20260811-1)" This reverts commit 370d5f5.
…ic from comments" This reverts commit ea77e21.
…666624) Sprint3 cleanup (a666624) consolidated test_*_branches.py files into their main test_*.py counterparts and removed them. Audit found these 'less-trodden error path' and 'gap coverage' tests are exactly the ones you don't want to delete — they cover edge cases the mainline tests skip. Removing them = silent coverage regression. Files restored (all from master HEAD): - tests/test_protect_branches.py (564 lines) — branch coverage for _safe_args / _strip_details_balanced / _enforce_sensitive_tool - tests/test_runtime_branches.py (517 lines) — less-trodden error paths in runtime.py. Removed 2 tests (test_start_recording_returns_* and test_stop_recording_returns_none) because a666624 P1 also intentionally removed the deprecated no-op stubs from runtime.py (replaced by direct return-value gates per the commit message). Restoring the tests without the methods would create dead tests. - tests/test_transport_branches.py (647 lines) — branch coverage gaps in transport.py Verification: pytest tests/ → 1462 passed, 6 skipped, 0 failed. The 6 skipped are pre-existing environment markers. Pairs with commit 700b0af (revert of ea77e21 Cyrillic scrub). Together they close the over-aggressive parts of the cleanup sprint without disturbing the valid P1 dead-code removal, P4 CHANGELOG dedup, and v3.38/server-minted test consolidations.
Bump __version__ 0.14.10 -> 0.14.11 and add the matching CHANGELOG entry. Patch release; partial revert of two sprint-5 cleanup commits whose scope exceeded what the codebase actually supported. This release closes the over-aggressive parts of the cleanup sprint without disturbing the valid P1 dead-code removal, P4 CHANGELOG dedup, and v3.38/server-minted test consolidations. - Revert ea77e21 (Cyrillic scrub + docstring trim): restored the 30-line 'partially wrong' block in src/nullrun/runtime.py (codifies CLAUDE.md \u00a74 fail-CLOSED rules for SDK transport vs backend enforcement), restored 'Разрыв 2' / 'Разрыв 1c' in CHANGELOG.md (user-coined Russian technical nomenclature), and restored tests/test_real_e2e_observation.py (321 lines, the only real-socket integration test). - Cherry-pick restore 3 branch-coverage files deleted by a666624 P2: tests/test_protect_branches.py (564), tests/test_runtime_branches.py (515; minus 2 tests for deprecated start_recording/stop_recording no-op stubs that a666624 P1 also intentionally removed), and tests/test_transport_branches.py (647). These files explicitly documented their purpose as covering 'gaps' and 'less-trodden error paths' that the mainline tests skip. Verification: pytest tests/ -> 1462 passed, 6 skipped, 0 failed. Pairs with commits 700b0af (revert ea77e21) and 2df6b3a (restore branch-coverage tests) on cleanup/p1p2-dead-code-tests. Compatibility: No SDK_MIN_VERSION bump. No public API change, no wire-format change, no behavioural change. Drop-in replacement for 0.14.10.
nullrun.audit module + runtime.audit proxy + 34 tests.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…wing built-in AuditProxy defines a public method named list() (ADR-009 P1 surface), which shadowed the built-in list inside the class body. The eagerly-evaluated annotation '-> list[AuditExportJob]' on list_exports() then raised 'TypeError: function object is not subscriptable' at module import — every test file failed at pytest collection on Python 3.12. Fix: add 'from __future__ import annotations' to runtime.py so all annotations become PEP 563 lazy strings. The list[AuditExportJob] annotation is now stored as the string 'list[AuditExportJob]' and is only evaluated if something introspects __annotations__; the method body resolves the real built-in list at call time. Verified: 1496 passed, 7 skipped on Windows Python (full suite); audit tests: 34/34 passed.
Adding 'from __future__ import annotations' to runtime.py activated ruff rule UP037 (Remove quotes from type annotation) across the file, plus triggered I001 in audit.py where the future-import was positioned mid-file. Auto-fixed via 'ruff check src/ --fix': - I001 in audit.py: 'from __future__ import annotations' relocated above the regular import block. - UP037 in audit.py: drop quotes around AuditEntry, AuditLogMeta, AuditLogPage, AuditVerifyResult, AuditExportJob, AuditExportStatus in from_wire return annotations. - UP037 in runtime.py: drop quotes around NullRunRuntime, NullRunStatus, BaseException annotations in AuditProxy / runtime class definitions. Verified: 1496 passed, 7 skipped; ruff clean.
Two mypy errors surfaced after the 'from __future__ import annotations' import landed in runtime.py and ruff auto-fix normalised audit.py annotations: 1. audit.py AuditVerifyResult.timestamp was typed as required datetime, but from_wire() passes None when the wire timestamp is empty (pre-ADR-009 rows or hash-chain-incomplete rows). Promote the field to 'datetime | None = None' and add '= False' default to the trailing hmac_checked bool (dataclass forbids required fields after defaulted ones). 2. runtime.py AuditProxy.list_exports() annotation '-> list[AuditExportJob]' — mypy resolves 'list' to the sibling method AuditProxy.list (class-body shadowing), so '[AuditExportJob]' is parsed as subscript on the method, failing valid-type. Switch to 'builtins.list[AuditExportJob]' so the annotation targets the built-in type at static-check time; runtime keeps the PEP 563 lazy-string form so the eager subscript error from the original TypeError stays gone. Verified: mypy clean (37 files), ruff clean, pytest 1496 passed.
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
Bumps SDK to
0.15.0(ADR-009 P1 governance audit read surface) and bundles the cleanup + fix commits that landed on this branch.What's in this PR
audit_eventstable.nullrun.auditmodule with frozen dataclasses:AuditEntry,AuditLogMeta,AuditLogPage,AuditQuery,AuditVerifyResult,AuditExportJob,AuditExportStatus.runtime.auditproxy surfaces typed results vialist(),verify(),list_exports(),create_export(),export_status().AuditProxy._require_org()raisesNullRunAuthenticationErrorwhen the runtime is unbound.Transport.audit_*methods now acceptorganization_idas a positional parameter (the transport holds no org binding);AuditProxythreadsself.organization_idthrough automatically.tests/test_audit.py,tests/contract/test_audit_wire.py).test_protect_branches.py,test_runtime_branches.py,test_transport_branches.py.Version bump
pyproject.tomlbumped from0.14.11→0.15.0.src/nullrun/__version__.py, README, and CHANGELOG are already at 0.15.0.Compatibility
No SDK_MIN_VERSION bump. Wire change is strictly additive (omitted when
None). TheTransport.audit_*shape change is source-compatible (positional kwarg with a clear name): pre-0.15 callers that wrotetransport.audit_log("org-uuid")continue to work; pre-0.15 callers that wrotetransport.audit_log(organization_id="org-uuid")(which previously crashed onself.organization_idlookup) now work for the first time.Tests
Full suite: 1462 passed, 6 skipped. New tests: 17 in
tests/test_audit.py+ 17 intests/contract/test_audit_wire.py.