[AAASM-4898] 🐛 (adapters): Fail-closed on pending approval + hermetic init tests + formatter docs#283
Conversation
The CrewAI sync patch and the shared async governance flow gated tool execution on `status == "deny"` only, so a terminal `"pending"` verdict (approval timed out or the resolver returned pending again) fell through and RAN the tool. Require an explicit `"allow"` to proceed and block any other terminal decision, matching the LangChain callback handler. Refs AAASM-4898 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test_assembly and test_spawn_context stubbed `_register_adapters` / `_start_network_layer` but not the native gRPC registration path, so a stray native `_core` extension made init dial a real gateway and fail. Add an autouse fixture forcing `_native_core_available()` to False (the CI default) so these tests are deterministic in both build modes. Refs AAASM-4898 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`.pre-commit-config.yaml` runs black (with isort + autoflake), not ruff format, so the documented `.venv/bin/ruff format .` formatter step was wrong. Point it at black to match the real gate. Refs AAASM-4898 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude Code review — merge-readiness (AAASM-4898, 19th sweep)Verdict: merge-ready against its acceptance criteria. All CI green (19/19 checks: CI Success, CodeQL, docs, pip-audit, codecov/patch, SonarCloud all SUCCESS). Scope — all three findings addressed, matches the ticket AC:
Side-effects — safe, no regression introduced. The Two sibling-instance follow-ups (NOT blockers for this PR — outside its AC, and the ticket itself flags them):
Neither follow-up blocks this PR. Not approving — leaving the merge decision to a human reviewer. — Claude Code (automated review) |



Description
Resolves three grouped sub-findings in the Python SDK (one commit each):
[security] Fail-closed on terminal
pending. The CrewAI sync patch (adapters/crewai/patch.py) and the shared async governance flow (adapters/_shared/tool_governance.py) gated tool execution onstatus == "deny"only. A terminal"pending"verdict — approval timed out, or the resolver returnedpendingagain after the round-trip — fell through and ran the tool. Both now require an explicit"allow"to proceed and block any other terminal decision, matching the LangChain callback handler (adapters/langchain/callback_handler.py). Regression tests cover the sync and async paths (terminal pending blocks and never invokes the tool).[test hermeticity] Deterministic init tests.
test/unit/test_assembly.pyandtest/unit/core/test_spawn_context.pystubbed_register_adapters/_start_network_layerbut not the native gRPC registration path, so a stray native_coreextension madeinit_assembly()dial a real gateway and fail. An autouse fixture now forces_native_core_available()toFalse(the CI default), keeping these tests deterministic whether or not the native extension is built.[doc drift] Formatter gate.
.claude/CLAUDE.mddocumented.venv/bin/ruff format .as the formatter, but.pre-commit-config.yamlruns black (plus isort + autoflake), with no ruff hook. Docs now point at black. (The repo-rootCLAUDE.mdnamed in the ticket is an untracked local-only file, not in the repo, so it is not part of this branch.)Type of Change
Breaking Changes
The fail-closed change only affects a verdict that previously ran a tool the operator never approved; that path was a security defect, not intended behavior.
Related Issues
Testing
Added regression tests:
test/unit/adapters/crewai/test_patch.py::test_terminal_pending_blocks_tool_and_does_not_runandtest/unit/adapters/_shared/test_tool_governance.py. Full suite green locally (ruff check, black via pre-commit, mypy,pytest test/— 1184 passed; the twotest/bench/test_latency_contracts.pytiming benchmarks are flaky under a loaded parallel run and pass in isolation, unrelated to these changes).Checklist