Skip to content

feat(dotnet): add approval chain parity#3363

Open
carloshvp wants to merge 1 commit into
microsoft:mainfrom
carloshvp:dotnet-approval-chain-parity
Open

feat(dotnet): add approval chain parity#3363
carloshvp wants to merge 1 commit into
microsoft:mainfrom
carloshvp:dotnet-approval-chain-parity

Conversation

@carloshvp

@carloshvp carloshvp commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the .NET language-parity slice for action-bound require_approval approval chains tracked in #3083.

The new AgentGovernance.Approvals surface provides:

  • deterministic action binding and SHA-256 digests
  • a durable store contract with a thread-safe in-memory implementation
  • ordered, versioned approval chains with deny short-circuiting
  • advisory LLM entries that cannot authorize or deny execution
  • fail-closed timeout, cancellation, malformed response, and transport-error handling
  • linked, tamper-evident approval entries and structured audit events
  • execution-time action, policy, chain, integrity, expiry, and one-time-consume checks
  • a versioned webhook transport with bound-response validation, independently verified approve identities, and metadata endpoint blocking

The zero-required-non-advisory-stage path intentionally denies with no_required_approval_stage. This avoids the current Python vacuous-allow behavior and matches the safer Go parity implementation in #3242. The behavior is documented in the .NET README and covered by regression tests.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Maintenance (dependency updates, CI/CD, refactoring)
  • Security fix

Package(s) Affected

  • agent-os-kernel
  • agent-mesh
  • agent-runtime
  • agent-sre
  • agent-governance
  • docs / root

Scope: agent-governance-dotnet/ only.

Checklist

  • My code follows the project style guidelines (ruff is not applicable; scoped dotnet format --verify-no-changes passed)
  • I have added tests that prove my fix/feature works
  • All new and existing tests pass (dotnet test AgentGovernance.sln)
  • I have updated documentation as needed
  • I have signed the Microsoft CLA

Additional validation:

  • dotnet format AgentGovernance.sln --no-restore --verify-no-changes --include src/AgentGovernance/Approvals tests/AgentGovernance.Tests/ApprovalProtocolTests.cs
  • dotnet build AgentGovernance.sln succeeds
  • dotnet test AgentGovernance.sln passes 806 tests

Attribution & Prior Art

  • This contribution does not contain code copied or derived from other projects without attribution
  • Any external projects that inspired this design are credited in code comments or documentation
  • If this PR implements functionality similar to an existing open-source project, I have listed it below

Prior art / related projects:

AI Assistance

  • I can explain every meaningful change in this PR: what it does, why, and what tradeoffs were considered
  • I have run tests and verification appropriate for this change
  • No part of this PR was autonomously submitted by an AI agent without my review
  • I have not used AI to generate review comments on others' PRs

OpenAI Codex assisted with implementation, regression tests, and validation. The draft remains open for contributor and maintainer review.

IP, Patents, and Licensing

  • This contribution does not implement patent-pending or patent-encumbered techniques
  • This contribution does not require an NDA or licensing agreement to understand or use
  • Any AI tools used have terms compatible with the MIT License

Related Issues

Part of #3083.

Signed-off-by: Carlos Hernandez <carloshvp@gmail.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions

Copy link
Copy Markdown

PR Review Summary

Check Status Details
🔍 Code Review ⚠️ Missing No current-run comment
🛡️ Security Scan ⚠️ Missing No current-run comment
🔄 Breaking Changes ⚠️ Missing No current-run comment
📝 Docs Sync ⚠️ Missing No current-run comment
🧪 Test Coverage ⚠️ Missing No current-run comment

Verdict: ⚠️ AI review incomplete; ready for human review

AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests size/XL Extra large PR (500+ lines) labels Jul 17, 2026
@carloshvp
carloshvp marked this pull request as ready for review July 18, 2026 06:56
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the .NET slice against the Python reference coordinator. Strong work. Every ambiguous or unexpected path fails closed: ResolveCoreAsync denies on null transport, timeout, transport protocol error, and generic exceptions, and ValidateForExecution rebinds the action digest, policy version, and chain version, consumes exactly once (verified by the 24-thread concurrency test), and verifies the hash-linked entry chain. LLM entries are correctly advisory-only. Tests meaningfully exercise deny, timeout, transport failure, tamper, expiry, cancel, and unverified-webhook paths. Scope stays within agent-governance-dotnet, no secrets.

Notes before this is relied on in production:

  1. WebhookApprover.ValidateEndpoint blocks cloud metadata and link-local hosts but not localhost, 127.0.0.0/8, or private ranges, and does not guard against DNS rebinding. Since this is advertised as an SSRF control, please tighten it or document the limits clearly.
  2. ResolveAsync eagerly consumes the approval, so a caller that later calls ValidateForExecution is denied as approval_consumed. Consider documenting ResolveAsync as terminal, or not consuming there.
  3. The no_required_approval_stage divergence from Python vacuous-allow is deliberate, documented, and matches Go. Good call.

Approving on the logic; item 1 is worth a follow-up.

@imran-siddique

Copy link
Copy Markdown
Collaborator

Filed the SSRF hardening follow-up from my review as #3369 so it does not get lost. Not a blocker for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/XL Extra large PR (500+ lines) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants