[AAASM-3106] 🔒 (runtime): Fail closed when runtime unreachable or query errors#138
Conversation
Under enforce, a raising query_policy, an error-sentinel decision, or an unreachable runtime (with the native extension present) now denies the tool instead of allowing it. observe / disabled keep failing open. Refs AAASM-3106
So build_governance_interceptor can pick the fail-closed vs fail-open posture per the agent's enforcement_mode. Refs AAASM-3106
Cover enforce + raising query, enforce + error-sentinel decision, enforce + unreachable runtime (deny-all wrapper), and the observe fail-open counterparts. Refs AAASM-3106
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
🔎 Claude Code review — Wave 1 security remediationCI: All checks green (Analyze, docs build, unit/integration/contract tests, CI Success pass; e2e/release-deploy skipped as expected). codecov/Sonar advisory, out of scope per review policy. |



Description
The SDK pre-execution check (
RuntimeQueryInterceptor/build_governance_interceptor) fell open when the native runtime authority could not produce a verdict: an unreachable runtime socket returned the bare client (nocheck_tool_start), a raisingquery_policyreturnedallow, and native error-sentinel decisions (query_failed/channel_closed/shutdown) fell through toallow. Underenforcement_mode="enforce"the SDK is a security control, so a denied action could silently execute when the runtime was unavailable.This PR makes the interceptor fail closed under
enforcewhile preserving the existing fail-open behavior underobserve/disabled:enforcement_modeis threadedinit_assembly→_register_adapters→build_governance_interceptor→RuntimeQueryInterceptor.enforce, a raisingquery_policyand error-sentinel decisions map todeny.enforce, when the native extension is present but the runtime socket is unreachable, a new deny-all_FailClosedInterceptoris returned (delegating non-check attributes to the gateway client) instead of the bare client.Type of Change
Breaking Changes
Behavior changes only on the failure path under
enforce(deny instead of silent allow), which is the intended security fix.Related Issues
Closes AAASM-3106
Testing
Added regression tests: enforce + raising query → deny, enforce + error-sentinel decision → deny, enforce + unreachable runtime → deny-all wrapper, plus observe/disabled fail-open counterparts and an end-to-end callback-handler block. Validated locally:
ruff checkclean (one pre-existingCallablewarning unrelated),mypyno new errors (the 4_coreimport errors pre-exist — native ext not built locally), fullpytest test/= 477 passed, 13 skipped (native-core gated).Checklist
🤖 Generated with Claude Code