[AAASM-3402] ✨ (python-sdk): Wire native gateway check/register into SDK lifecycle#146
Conversation
Expose connect_runtime_client() and register_agent() over the native RuntimeClient (AAASM-3399), and let build_governance_interceptor reuse a pre-connected client so register and query_policy share one client (and thus the issued credential token). native_available is threaded through so a missing extension (fail open) stays distinct from an unreachable socket (fail closed under enforce). Refs AAASM-3402 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
init_assembly now connects one native runtime client, registers the agent on it (storing the credential token), and reuses that client for the RuntimeQueryInterceptor — so a deny actually blocks a tool. Registration failure propagates under enforce (fail closed) and is swallowed under observe/disabled (fail open), honoring the existing enforcement semantics. Refs AAASM-3402 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
register_agent() and check_policy_compliance() issued raw HTTP to the core, which ADR 0004 forbids — registration and the policy check now go through the native gRPC path. Removes the methods plus the unit/integration tests that asserted their REST wire shape; report_edge()/dispatch_tool() (genuinely HTTP) and the GatewayClient topology-field storage tests are kept. Topology lineage over the native register call is a tracked follow-up. Refs AAASM-3402 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Public API section advertised the removed REST register_agent/ check_policy_compliance and claimed registration + policy checks go over HTTP. Restate per ADR 0004: those go SDK → aa-sdk-client → core over gRPC/UDS; only topology edges and secret dispatch remain HTTP routes. Refs AAASM-3402 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FakeRuntimeClient records register calls and returns a canned query_policy decision; install_fake_core swaps a fake agent_assembly._core into sys.modules so init_assembly exercises the native register/query path without a built extension or a running aa-runtime. Refs AAASM-3402 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes the unwired-enforcement gap with four cases: init_assembly calls native register; a native deny makes the adapter interceptor's check_tool_start block; observe swallows a registration failure; enforce propagates it (fail closed). Refs AAASM-3402 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Chisanan232
left a comment
There was a problem hiding this comment.
Claude Code — PR review (AAASM-3402)
Verdict: ✅ ready to merge. CI all green (CI Success gate, unit + integration, SonarCloud, codecov, CodeQL; only e2e/deploy intentionally skipped). Scope clean: gateway.py, core/assembly.py, core/runtime_interceptor.py, README + 5 test files.
Scope reviewed
- Root cause confirmed & fixed: the public SDK never called native
register/query_policy(from AAASM-3399), so the pre-exec check was a no-op — a denied action was not blocked at the SDK layer. ✅ Nowinit_assemblyconnects one nativeRuntimeClient, registers on startup (token stored on the shared client), and the same client backs theRuntimeQueryInterceptor, sodenyactually blocks the tool. - Legacy raw-REST
GatewayClient.register_agent()/check_policy_compliance()(ADR-0004 violations) removed. ✅ - Enforcement semantics preserved: enforce = fail-closed, observe/disabled = fail-open. ✅
- README + docstrings corrected to native gRPC path. Regression tests: register-on-init + deny-blocks. 565 pass / 13 skip (native
_corenot built on macOS host — expected).
Follow-up filed (not a blocker)
The native register() does not yet forward the topology/lineage fields (parent_agent_id, team_id, …) the old REST register sent — they're still stored on GatewayClient but not carried over the native path. This matters because team_id drives team-budget attribution and the topology graph. Tracked as a separate ticket under Epic AAASM-3395 (likely needs the Register proto to carry these fields → affects all SDKs). Closing the unwired-enforcement gap here is correct and self-contained.
Good to merge.



Description
Wires the SDK's pre-execution policy check and agent registration to the native
aa-sdk-clientpath and retires the legacy raw-REST register/check, closing the unwired-enforcement gap (AAASM-3021 family).What changed
init_assemblynow connects a single nativeRuntimeClient, calls itsregister()(AAASM-3399) on startup so the agent registers with the gateway and the issued credential token is stored on the shared client, and reuses that same client for theRuntimeQueryInterceptor'squery_policychecks (so every check authenticates).denydecision now actually blocks the tool call before it runs, via the adaptercheck_tool_startcontract.GatewayClient.register_agent()andGatewayClient.check_policy_compliance()— these issued raw HTTP to the core, which ADR 0004 forbids.report_edge()/dispatch_tool()(genuinely HTTP routes) are retained.Why
Previously the public SDK never called the native
register/query_policy; the in-process check was effectively a no-op, so a denied action was not blocked at the SDK layer even with a reachable core. This PR closes that gap.How it works (enforcement semantics preserved)
enforce: register failure or an unreachable/erroring runtime → fail closed (init aborts on register failure; queries deny).observe/disabled(or no mode): register failure is swallowed and queries proceed → fail open.Follow-up (documented, out of scope): the native
register()signature does not yet carry the topology/lineage fields (parent_agent_id,team_id,delegation_reason,spawned_by_tool,depth) that the old REST register forwarded. Those fields are still stored onGatewayClient; forwarding them over the native register call is a tracked follow-up.Type of Change
Breaking Changes
GatewayClient.register_agent()andGatewayClient.check_policy_compliance()are removed. These were internal-facing helpers (not part of the supportedinit_assemblyflow); the public entrypointinit_assemblyis unchanged and now performs registration automatically over the native path.Related Issues
register()/query_policy()shim)Closes AAASM-3402
Testing
Describe the testing performed for this PR:
New regression tests (
test/unit/core/test_init_registration.py+_fake_core.pyfakes):init_assemblycalls nativeregisteron startup.denymakes the adapter interceptor'scheck_tool_startblock (the core of the fixed bug).observeswallows a registration failure;enforcepropagates it (fail closed).Retired the REST register/check unit + integration tests; kept the topology-field-storage tests.
Local validation (native
_corenot built on this macOS host, so native-only suites skip as designed):.venv/bin/python -m pytest test/→ 565 passed, 13 skipped.venv/bin/mypy(CI config,--ignore-missing-imports) → cleanpre-commit run(black / isort / autoflake / mypy) on changed files → cleanCI for python-sdk is minimal (mypy type-check workflow; no ruff gate in pre-commit). Validated locally as above.
Checklist
🤖 Generated with Claude Code