Repository navigation
feat(agent-mesh): legacy ApprovalHandler to protocol compatibility adapter (ADR-0030 step 3) - #3096
Conversation
…y adapter Step 3 of the ADR-0030 migration. Extract the legacy-handler-to-coordinator bridge that step 2 placed inline in govern() into a reusable governance/approval_bridge.py LegacyHandlerAdapter, so other approval paths (Agent OS escalation, MCP gateway, framework adapters) can drive a protocol chain entry from an existing synchronous ApprovalHandler without reimplementing the mapping. The adapter asks the handler, maps approved/approver/reason onto a single submit_entry call, and returns an AdapterResult (the vote, the submitted entry or None, and a fail-closed error on unpermitted identity / expired request / unknown stage). govern()'s require_approval coordinator path is refactored to use it; behavior is identical. The approval_protocol foundation package is unchanged and does not depend on the bridge. Refs microsoft#2478
🤖 AI Agent: test-generator — `agentmesh/governance/approval_bridge.py`
|
🤖 AI Agent: code-reviewer — Action Items:
TL;DR: 0 blockers, 1 warning. The PR introduces a compatibility adapter for legacy approval handlers, which is well-implemented and tested, but the error messages could be more structured for better debugging.
Action Items:None. Warnings:
|
🤖 AI Agent: docs-sync-checker — Docs Sync
Docs SyncDocumentation is in sync. |
🤖 AI Agent: security-scanner — View details
No security issues found. |
🤖 AI Agent: breaking-change-detector — API Compatibility
API Compatibility
|
PR Review Summary
Verdict: AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims. |
Imran Siddique (imran-siddique)
left a comment
There was a problem hiding this comment.
Clean extraction of the inline approval bridge into a reusable LegacyHandlerAdapter. Fail-closed design is correct: any ApprovalProtocolError from submit_entry returns entry=None with an error string, letting callers deny without special-casing. The govern.py diff confirms the adapter slots in cleanly. 122-line test file covers the key paths. LGTM.
2456d36
into
microsoft:main
#3097) Step 4 of the ADR-0030 migration (section 5). The legacy WebhookApproval sends a thin payload with no request id, action digest, version, or expiry and trusts a body-supplied approver string. Add governance/approval_webhook.py with the versioned, action-bound contract: build_webhook_request() emits a schema-versioned payload carrying the request id, action digest, policy version, chain version, and expiry; parse_webhook_response() requires the response to echo the binding and honours an approve only when the approver identity is verified (deny is always honoured, malformed denies); VersionedWebhookApproval is the protocol-native transport (POSTs with caller-supplied auth headers, reuses the SSRF guard, fails closed on timeout/transport/malformed/binding-mismatch). Standalone and additive: no changes to govern, the legacy WebhookApproval (kept as the old-payload legacy path), or the approval_protocol foundation. Independent of the step-3 PR (#3096); wiring the transport into the govern/bridge flow is a follow-up. 11 tests, no network (HTTP is injectable). Refs #2478
Summary
Step 3 of the ADR-0030 migration: a reusable compatibility adapter so existing synchronous
ApprovalHandlerimplementations (callback, console, webhook, auto-reject) drive the action-bound approval protocol.Step 2 (#3076) bridged the legacy handler into the coordinator inline inside
govern(). This PR extracts that intogovernance/approval_bridge.py, so every approval path (Agent OS escalation, MCP gateway, framework adapters) can reuse one handler-to-protocol mapping instead of reimplementing it.What changed
governance/approval_bridge.py:LegacyHandlerAdapter(handler, *, approver_kind=HUMAN, identity_assurance="approval-handler")..collect(coordinator, request, legacy_request, *, stage_index=0) -> AdapterResult: asks the handler, mapsapproved/approver/reasononto onesubmit_entry, fail-closed onApprovalProtocolError(unpermitted identity, expired request, unknown stage).AdapterResult(approval, entry, error)with a.submittedconvenience.govern.py: therequire_approvalcoordinator path now uses the adapter; behavior is unchanged (net -17 lines there).approval_protocolfoundation package is untouched and never depends on the bridge.Tests
tests/test_approval_bridge.py(6 tests): approve submits an ALLOW entry and resolves allow; reject submits DENY; an unpermitted identity fails closed (entry=None, error set, vote still reported); an expired request fails closed; reason and approver propagate; custom approver kind and assurance. Existing govern coordinator and approval suites still pass (85 total).Scope
Single-stage via the synchronous handler, same as step 2. Multi-stage orchestration and async / webhook transport remain steps 4 and beyond.
Refs #2478. ADR-0030 step 3.