Skip to content

[AAASM-4898] 🐛 (adapters): Fail-closed on pending approval + hermetic init tests + formatter docs#283

Merged
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-4898/fix/pending_failclosed_and_test_hermeticity
Jul 20, 2026
Merged

[AAASM-4898] 🐛 (adapters): Fail-closed on pending approval + hermetic init tests + formatter docs#283
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-4898/fix/pending_failclosed_and_test_hermeticity

Conversation

@Chisanan232

@Chisanan232 Chisanan232 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Description

Resolves three grouped sub-findings in the Python SDK (one commit each):

  1. [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 on status == "deny" only. A terminal "pending" verdict — approval timed out, or the resolver returned pending again 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).

  2. [test hermeticity] Deterministic init tests. test/unit/test_assembly.py and test/unit/core/test_spawn_context.py stubbed _register_adapters / _start_network_layer but not the native gRPC registration path, so a stray native _core extension made init_assembly() dial a real gateway and fail. An autouse fixture now forces _native_core_available() to False (the CI default), keeping these tests deterministic whether or not the native extension is built.

  3. [doc drift] Formatter gate. .claude/CLAUDE.md documented .venv/bin/ruff format . as the formatter, but .pre-commit-config.yaml runs black (plus isort + autoflake), with no ruff hook. Docs now point at black. (The repo-root CLAUDE.md named in the ticket is an untracked local-only file, not in the repo, so it is not part of this branch.)

Type of Change

  • 🔧 Bug fix
  • 📚 Documentation update

Breaking Changes

  • No

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

  • Unit tests added/updated
  • Manual testing performed

Added regression tests: test/unit/adapters/crewai/test_patch.py::test_terminal_pending_blocks_tool_and_does_not_run and test/unit/adapters/_shared/test_tool_governance.py. Full suite green locally (ruff check, black via pre-commit, mypy, pytest test/ — 1184 passed; the two test/bench/test_latency_contracts.py timing benchmarks are flaky under a loaded parallel run and pass in isolation, unrelated to these changes).

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Comments added for complex logic
  • Documentation updated if needed
  • All tests passing

Chisanan232 and others added 3 commits July 20, 2026 09:53
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>
@sonarqubecloud

Copy link
Copy Markdown

@Chisanan232

Copy link
Copy Markdown
Contributor Author

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). mergeable: MERGEABLE; mergeStateStatus: BLOCKED reflects only the required Pioneer approval, not any failing check. No blocking Codecov or SonarCloud issues. Commit history is clean — one commit per finding (37689a8 fail-closed, 3c8efe9 hermetic tests, 3972dc8 formatter doc).

Scope — all three findings addressed, matches the ticket AC:

  1. Fail-closed on terminal pending_shared/tool_governance.py and crewai/patch.py now gate on if status != "allow" after the approval round-trip, so a terminal pending blocks (pending-flow → rejected message/error; else denied) instead of falling through to run the tool. Matches the LangChain reference (!= "allow": raise). Both sync and async paths covered by genuine regression tests that assert the tool is never invoked — these would fail on the old == "deny" gate.
  2. Hermetic init tests — autouse _native_core_available → False fixtures in test/unit/test_assembly.py and test/unit/core/test_spawn_context.py pin the pure-Python registration path so init tests are deterministic whether or not native _core is built. Verified locally: pytest on the 4 touched test files → 91 passed (native _core not built).
  3. Formatter doc.claude/CLAUDE.md corrected ruff formatblack. Confirmed against .pre-commit-config.yaml (isort + autoflake + black + mypy; no ruff hook). The repo-root CLAUDE.md named in the ticket is genuinely untracked (git ls-files shows only .claude/CLAUDE.md), so its exclusion is correct.

Side-effects — safe, no regression introduced. The deny → != "allow" change only adds blocking for non-allow terminal states; normal allow proceeds and deny/pending already blocked or now block. The fixture change forces the CI-default code path and does not mask real init behavior (the stubbed adapter/network layers are unchanged). No new risk.

Two sibling-instance follow-ups (NOT blockers for this PR — outside its AC, and the ticket itself flags them):

  • The ticket's "Regression risk / related" note says "cross-check the other adapters for the same deny-only gate." Seven adapters still gate on status == "deny" after the pending round-trip and carry the identical fail-open on a terminal pending: smolagents/patch.py:156, llamaindex/patch.py:288,330, openai_agents/patch.py:552, mcp/patch.py:287, microsoft_agent_framework/patch.py:332, haystack/patch.py:262, agno/patch.py:197,220. Low reachability (needs a custom interceptor returning pending), same as this ticket, but it's the exact sibling-instance class prior sweeps flagged. Recommend a follow-up to sweep the whole pattern.
  • Formatter-doc drift persists in two other tracked files: CONTRIBUTING.md:133 (uv run ruff format .) and docs/guides/authoring-adapters.md:330 (ruff format --check). Same class as finding ⬆ Bump typescript from 5.6.3 to 6.0.3 in /docs #3; the ticket only named the two CLAUDE.md files, so out of scope here, but worth a follow-up.

Neither follow-up blocks this PR. Not approving — leaving the merge decision to a human reviewer.

— Claude Code (automated review)

@Chisanan232
Chisanan232 merged commit 6153212 into master Jul 20, 2026
26 checks passed
@Chisanan232
Chisanan232 deleted the v0.0.1/AAASM-4898/fix/pending_failclosed_and_test_hermeticity branch July 20, 2026 02:26
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