Repository navigation
fix(escalation): record votes in approve/deny; fix quorum always evaluating to 0 - #3127
Imran Siddique (imran-siddique) wants to merge 1 commit into
Conversation
🤖 AI Agent: security-scanner — View details
No security issues found. |
🤖 AI Agent: docs-sync-checker — Docs Sync
Docs Sync
|
🤖 AI Agent: test-generator — `agent-governance-python/agent-os/src/agent_os/integrations/escalation.py`
|
🤖 AI Agent: code-reviewer — View details
TL;DR: 0 blockers, 1 warning. Changes improve vote tracking and quorum logic but require additional test coverage for edge cases.
Action items:
|
🤖 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. |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
DCO sign-off is needed on the 8 commits this PR presents to GitHub. The commits themselves are correct; the sign-off is purely a procedural fix. Fastest path to unblock: git checkout fix/escalation-quorum-votes-3126
git pull --rebase origin fix/escalation-quorum-votes-3126
# Create clean 2-commit version from main
git checkout -b fix/escalation-quorum-votes-3126-dco main
git cherry-pick -s 6b4788ff # fix(escalation)
# resolve edu-k12 conflict: just git add examples/policy-templates/edu-k12.yaml
git cherry-pick -s b3904c0b # fix(tests)
git push origin fix/escalation-quorum-votes-3126-dco:fix/escalation-quorum-votes-3126 --force-with-leaseThis reduces the PR to 2 commits, both with |
b3904c0 to
17daa77
Compare
MohammadHaroonAbuomar
left a comment
There was a problem hiding this comment.
The quorum fix itself is correct, idempotent, and thread-safe.
The examples/policy-templates/edu-k12.yaml hunk needs to be dropped from this PR. Main now has newer fixes to the same three regexes (lazy .*?, optional (to\\s+|an?\\s+)?, the extra |information keyword) and merging this would overwrite them. It is also unrelated to the escalation fix.
Separately, this fix surfaces a pre-existing issue worth a follow-up: resolve() wakes on the first vote, runs the quorum check once, and falls through to default_action without waiting the remaining timeout for vote 2 of N (escalation.py:176, :424-431). Not introduced here, but now reachable.
Minor: approver: str = "" default (escalation.py:166,184) means one anonymous vote counts toward quorum. Consider rejecting empty when quorum is in play.
|
Thanks for the detailed review MohammadHaroonAbuomar. edu-k12.yaml: Dropped from this PR. Reverted to main's version -- the newer regex fixes already in main are preserved. Empty approver: Added resolve() timing issue: Acknowledged -- |
|
MohammadHaroonAbuomar please merge this when u have time. All tests passing. |
… fix quorum (#3126) Signed-off-by: Imran Siddique <imran.siddique@opaque.co>
993115b to
9be72c4
Compare
|
MohammadHaroonAbuomar thanks for the review. Addressed your change request: the Jack Batzner (@jackbatzner) Ricky Gummadi (@Ricky-G) the quorum fix is correct-in-substance per Mohammad's review and now CI-clean. The only remaining gate is |
|
Closing this as superseded. The fix is already present on main: escalation.py, test_escalation.py, and test_security_hardening.py on main are identical to this branch, and the vote recording in approve/deny, the quorum fix, and the empty-approver rejection are all in place (escalation.py:171/175/191/195). No separate merge is needed here. Thanks to the reviewers for the look. |
Summary
InMemoryApprovalQueue.approve()anddeny()were settingreq.decisionandreq.resolved_bybut never appending toreq.votesEscalationHandler.resolve()evaluates quorum by summingreq.votes-- with an always-empty list the count was 0, resetting a correctly-resolved decision back toPENDINGand falling through to the timeout default(approver, verdict, timestamp)toreq.votesbefore any decision logic; deduplicate by approver so a single reviewer cannot satisfy quorum multiple timesFixes #3126
Changes
agent-governance-python/agent-os/src/agent_os/integrations/escalation.pyapprove(): append(approver, "ALLOW", datetime.now(...))toreq.votesbefore setting decision; skip duplicate votes from the same approver; allow additional votes to be recorded after decision is already finaldeny(): same pattern for"DENY"verdictagent-governance-python/agent-os/tests/test_escalation.pytest_double_approve_failstotest_double_approve_same_approver_rejected(clarifies the dedup semantics, not "already resolved")test_second_approver_vote_recorded-- second approver's vote appended, both visible inreq.votestest_votes_recorded_on_approve/test_votes_recorded_on_deny-- assertvoteslist populated with correct approver and verdictTestQuorumResolutionclass: quorum met resolves ALLOW, quorum not met times out to DENY, duplicate approver vote rejected and does not satisfy quorumTest plan
pytest tests/test_escalation.py)test_quorum_met_resolves_allow: quorum=1, single approver, resolves ALLOWtest_quorum_not_met_times_out: quorum=2, 1 approver, times out to DENYtest_duplicate_approver_does_not_satisfy_quorum: second call from same approver returns False,voteslist length stays at 1🤖 Generated with Claude Code