chore(release): 0.18.0 — close approval-row orphan via auto-consume on success + exception paths (ADR-047) - #109
Merged
Merged
Conversation
…n success + exception paths (ADR-047)
- Add Runtime.consume_approval() + Transport.consume_approval() hitting
the new POST /api/v1/approvals/{approval_id}/consume endpoint (backend
2.8.0+). Best-effort: catches all exceptions, surfaces at DEBUG, never
blocks the success path.
- Add Runtime.lookup_pending_approval_id_for_execution() +
Runtime._mark_approval_resolved_for_execution() — RLock-guarded
reverse-index (execution_id -> approval_id) populated by the WS push
handler.
- check_workflow_budget on outcome=approved: auto-call consume_approval
+ pop reverse index BEFORE return. Closes the structural orphan where
mode=inline tools left approval rows at status=APPROVED past expires_at.
- _safe_cancel_active_execution: after cancel_execution, ALSO call
consume_approval if a pending approval_id was captured for this
execution_id. Idempotent — if no approval was captured the lookup
returns None and this is a no-op.
- Bump version 0.17.1 -> 0.18.0 across pyproject.toml, src/nullrun/__version__.py,
uv.lock stamp (line 2873); CHANGELOG entry + README section
'Closing orphan grants (v0.18+)'.
- Add 6 pin tests in tests/test_v3_wire_contract.py:
TestConsumeApprovalEndpoint (4), TestCheckWorkflowBudgetConsumeOnApproved (1),
TestSafeCancelCallsConsumeApproval (2).
Verified: ruff check src tests all-green, mypy src/nullrun clean (37 source
files), pytest 1814 passed / 4 skipped (was 1807 baseline + 6 new + 1 prior).
Wire-format unchanged (additive endpoint). SDK_MIN_VERSION unchanged.
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
Minor release — closes the structural orphan where the
approvalsrow stayed atstatus='APPROVED'pastexpires_atbecause the only path that flipped it toCONSUMEDwas the orchestrator's Step 6 inline atbackend/src/proxy/http/gate/orchestrator.rs:713, whichmode="inline"tools bypass entirely. The fix wires the SDK to call a new structurally-distinct endpointPOST /api/v1/approvals/{approval_id}/consumefrom both the success path (after WS approval resolves tooutcome=approved) and the exception path (`_safe_cancel_active_execution`). Operator-initiated/cancelon an approval envelope ALSO consumes the row in spawned Step 4e. Audit emits distinguish operator-cancel from SDK-consume via distinctmatched_rulestrings.Behaviour change: outbound HTTP call from the SDK success branch (
check_workflow_budgetafter WS approval) and exception path. Wire-format unchanged (additive endpoint). SDK_MIN_VERSION unchanged.Fixed
ADR-047 — close approval-row orphan via auto-consume on success + exception paths (
03cd6ed, 9 files, +584/-9).Runtime.consume_approval(approval_id, execution_id=None)+Transport.consume_approval()hit the newPOST /api/v1/approvals/{approval_id}/consumeendpoint (backend 2.8.0+). Sibling to/api/v1/cancel; structurally distinct fromconsume_approved(omitsexecution_idbinding per ADR-046, carriesorganization_idfilter, optionally filters byapi_key_id). Three response shapes:consumed/already_consumed/not_approved— all return 200 (idempotent).check_workflow_budgetauto-calls on theoutcome=approvedbranch._safe_cancel_active_executionALSO calls aftercancel_execution. Best-effort posture: catches all exceptions, surfaces atlogger.debug, never blocks the success or exception path. Theapproval_expiry_sweeperatbackend/src/workers/approval_expiry.rswill close any rows that slip through within ~5 min.ADR-047 (audit attribution) — two distinct
matched_rulestrings on theaudit_eventstable:lifecycle.consume_approved_via_sdk+consume_reason="sdk_consume_endpoint"for SDK-side,lifecycle.consume_approved_via_cancel+consume_reason="operator_cancel"for operator-initiated cancel. Sameaudit_kind(approval.consumed) andstatus(success) so theapprovalslifecycle view aggregates them correctly while the audit deep-dive filters onmatched_rule+consume_reason.Cleanup
No pre-flight scrub needed —
.gitignorealready covereddist_local/andsrc/**/*.defect*since the0.16.7cleanup commit;git grep -nE "dist_local|\.defect"returns only.gitignore+ CHANGELOG references (no committed artifacts).Verification
ruff check src testsmypy src/nullrunpytest -qTestConsumeApprovalEndpoint/TestCheckWorkflowBudgetConsumeOnApproved/TestSafeCancelCallsConsumeApproval+ 1 prior)origin/masterdist_local/, no*.defect*)nullrun.__version__0.18.0consumeendpoint only)Commits included