Skip to content

feat(policy-engine): introducing Agent Control Spec (ACS) - #2747

Merged
MohammadHaroonAbuomar merged 145 commits into
microsoft:mainfrom
MohammadHaroonAbuomar:agent-policy-spec
Jun 2, 2026
Merged

MohammadHaroonAbuomar merged 145 commits into
microsoft:mainfrom
MohammadHaroonAbuomar:agent-policy-spec

Conversation

@MohammadHaroonAbuomar

@MohammadHaroonAbuomar MohammadHaroonAbuomar commented Jun 1, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Introduces the Agent Control Specification (ACS) as AGT's new policy engine for v5.0.

ACS is vendored from responsibleai/AgentControlSpecification into policy-engine/ and replaces the prior YAML / OPA / Cedar dispatcher with a single, deterministic, fail-closed runtime that ships with stock Rego and Cedar libraries, a normative wire schema, and consistent surface across the five language SDKs.

What's in this PR

Layer Path Highlight
Spec policy-engine/spec/ SPECIFICATION.md + SPECIFICATION-AGT-DELTA.md (D1–D7) + per-IP shape docs (AGT-MANIFEST-1.0, AGT-SNAPSHOT-1.0, AGT-EVIDENCE-1.0, AGT-RESOLUTION-1.0) — RFC-2119 normative, with conformance tests
Engine policy-engine/core/ Rust core, stable C FFI, OPA + Cedar built-in (feature-gated, default = ["opa", "cedar"]), 373 cargo tests with --all-features
Stock policy libs policy-engine/policy/lib/, policy-engine/policy/cedar-lib/ 10 Rego packages (101 OPA tests) + 10 Cedar packages (60 cedar-CLI tests) covering budgets, patterns, IFC, redact, approval, drift, confidence, content_hash, agt_default, agt_ifc
SDKs policy-engine/sdk/{rust,python,node,dotnet} All four on the same wire schema; .NET surface upgraded for Decision.Transform, Verdict.Evidence, bisected identity, FromPathAsync
AGT-side wrapper agent-governance-python/agt-policies/ AgtRuntime, SnapshotBuilder, governance-chain manifest resolution, GovernancePolicy → ACS manifest bridge, agt migrate v4-to-v5 CLI
Adapters agent-governance-python/agent-os/src/agent_os/integrations/ All 17 framework adapters now route through AgtRuntime via the new AdapterRuntimeBridge (openai, langchain, anthropic, autogen, crewai, gemini, llamaindex, guardrails, google_adk, mistral, pydantic_ai, semantic_kernel, maf, smolagents, a2a, agentshield, bedrock)
Demo policy-engine/scripts/demo_end_to_end.sh Cross-language end-to-end: builds, packs, installs Rust + Python + Node + .NET; exercises allow / deny / escalate / transform per language

AGT deltas vs upstream ACS (normative, see SPECIFICATION-AGT-DELTA.md)

  • D1 Transform verdict — adds the transform decision (5-decision surface). effects[] array is removed; verdicts containing effects fail closed with runtime_error:policy_output_invalid. Multi-step rewriting moves to annotators per D1.3.
  • D1.4 Bisected identity — input_identity (pre-transform) and enforced_identity (post-transform) on every result. Approval resolver binds to enforced_identity.
  • D2 Evidence — ≤4 KiB opaque artefact + sorted verification_pointers keys (no URLs) on every verdict, propagated to telemetry events.
  • D3 Cedar as built-in — policies.type: cedar is a built-in (not a custom adapter); stock Cedar mirror at policy/cedar-lib/.
  • D5 Approval section — top-level approval: in the manifest, structurally validated against ApprovalSection with deny_unknown_fields. Empty {} is the documented minimum (host wires the resolver via AgtRuntime(approval_resolver=...)).
  • D6 Reserved reasons — 7 new AGT-reserved runtime_error: reasons cover transform_target_forbidden, transform_invalid, approval_resolver_missing, resolution_path_traversal, resolution_cycle, resolution_invalid_governance, resolution_merge_conflict.
  • D7 Cargo feature split — default = ["opa", "cedar"]; users can opt in/out per build.

Migration story for existing AGT users

agt migrate v4-to-v5 (in agent-governance-python/agt-policies/src/agt/cli/migrate.py) walks a v4 project, lists every legacy artefact, and (with --write) rewrites the project to the v5 shape: flat ACS manifests + generated Rego bundles. Dry-run by default, idempotent, deterministic, stdlib + pyyaml only.

agt migrate v4-to-v5 ./my-agent-project                                # dry-run
agt migrate v4-to-v5 ./my-agent-project --write-report MIGRATION.md     # report
agt migrate v4-to-v5 ./my-agent-project --write                         # apply

Test matrix (last verified locally on this branch)

Suite Result
cargo test --workspace --all-features --release 373 / 0
agt-policies pytest 283 passed, 2 skipped
agent-os/tests/test_integrations.py + test_v5_bridge.py 257 / 0
OPA Rego (stock libs) 101 / 101
Cedar CLI (stock libs) 60 / 60
Cross-language end-to-end demo 6 / 6 suites green (core, python-sdk, python-demo, rust-demo, agt-policies-demo, migration; node + dotnet covered locally and skipped in this run for speed)

Reviewer trail

The branch went through four rounds of multi-model code review (Claude Opus + GPT-5.4) reaching consensus LGTM before the AAIF technical review. Liam Crumm picked up the work and added 39 commits on top of the original 103, including:

  • Consolidating AGT deltas into the authoritative SPECIFICATION.md
  • Hardening ACS integration wiring
  • Failing closed on unbound resolved governance rules (76a05f9d)
  • Validating Azure API base by hostname (0ea6871e)
  • Expanding ACS conformance coverage (24d81de4)
  • Documenting Agent Control Specification across AGT (d63711fe)

Type of Change

  • New feature (non-breaking change that adds functionality) — adds the ACS policy engine
  • Breaking change — effects[] is removed from verdict; Decision::Effects becomes Decision::Transform; v4 GovernancePolicy users should run agt migrate v4-to-v5
  • Documentation update — see policy-engine/spec/, docs/adr/0029-policy-distribution-and-registries.md, and the per-package docs
  • Maintenance — re-architects the policy layer; consolidates per-language packages

Package(s) Affected

  • agent-os-kernel — integrations rewritten to use AdapterRuntimeBridge
  • agent-mesh — unchanged on this branch except for upstream-merged fixes
  • agent-runtime
  • agent-sre
  • agent-governance / agt-policies — new wrapper crate + manifest-resolution + migrate CLI
  • docs / root — ACS spec docs, ADRs, README and per-package READMEs updated

Checklist

  • My code follows the project style guidelines (ruff check)
  • I have added tests that prove my fix/feature works (373 cargo, 283 agt-policies, 257 agent-os, 101 OPA, 60 Cedar, 35 example Rego)
  • All new and existing tests pass (pytest + cargo) — last verified after merging current main
  • I have updated documentation as needed (spec docs, README, ADR-0029)
  • I have signed the Microsoft CLA (all commits include DCO Signed-off-by:)

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

Substantial portions of this branch were drafted and reviewed with the assistance of GitHub Copilot CLI (Claude Opus + GPT-5.4). The multi-model review trail kept consensus discipline before each milestone shipped. Every claim in commit messages is grounded in code or spec evidence.

Convergence path (per AGT_AAIF.docx proposal)

This PR is the AGT-side of a planned two-layer governance stack. The RAI team's AgentShield semantic / deterministic content-control layer will replace AGT's policy service in a follow-up ADR before AAIF submission, giving every AGT integration AgentShield capabilities without breaking changes. AGT remains the infrastructure-governance layer (identity, trust, authorization, audit, lifecycle, policy evaluation).

Notes for reviewers

  • Branch is currently 143 commits ahead of microsoft:main (verified after merging current main); 0 behind.
  • All MohammadHaroonAbuomar-authored commits are SSH-signed; the public signing key has been registered against the account (verification badge should be active across the diff).
  • Kept as draft while AAIF Technical Committee review (PR-deck v2 in OneDrive/AGT/Presentations/AAIF_AGT_Technical_Review_v2_AGT5.pptx) is in progress; will be flipped to ready-for-review after the TC vote and after the AgentShield convergence ADR lands.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

Welcome to the Agent Governance Toolkit! Thanks for your first pull request.
Please ensure tests pass, code follows style (ruff check), and you have signed the CLA.
See our Contributing Guide.

@github-actions github-actions Bot added size/XL Extra large PR (500+ lines) documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests labels Jun 1, 2026
Comment thread policy-engine/generator/acs_generator/llm.py Fixed
MohammadHaroonAbuomar and others added 24 commits June 1, 2026 23:15
Vendors responsibleai/AgentControlSpecification@318dbca into the new
policy-engine/ directory. ACS becomes the AGT-owned policy engine per
the AGT 5.0 redesign documented in architecture-exploration.md.

Headline divergences from upstream ACS (to be implemented in M1-M2):
- Effects removed from verdict; transform verdict type introduced (Q2).
- Optional evidence field on verdict + telemetry events (Q4).
- Cedar promoted to a built-in policy type (Q10).
- approval top-level manifest section (Q13).
- AGT folder discovery, scope filter, and merge layer pre-resolves
  manifests before they reach this engine; engine never sees extends
  from an AGT host (Q6).

Original ACS LICENSE preserved at policy-engine/LICENSE.acs.
Original ACS README preserved at policy-engine/README.vendored-acs.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
…t/evidence specs

5 normative spec docs implementing user decisions Q2-Q14:

  - SPECIFICATION-AGT-DELTA.md: section-by-section deltas from upstream ACS
    spec — drop effects, add transform verdict, add evidence field, promote
    Cedar to built-in policy type, add approval section, add reserved reasons,
    document cargo feature split.

  - agt/AGT-MANIFEST-1.0.md: full manifest surface AGT hosts author including
    new top-level approval and limits sections.

  - agt/AGT-RESOLUTION-1.0.md: AGT-side folder discovery + scope filtering +
    merge layer that pre-resolves manifest chains before the engine sees them
    (preserves AGT v4 folder discovery while keeping ACS engine simple).

  - agt/AGT-SNAPSHOT-1.0.md: per-intervention-point snapshot shape so
    AGT-authored Rego/Cedar rules are portable across SDKs.

  - agt/AGT-EVIDENCE-1.0.md: proof_artefact + verification_pointers
    convention for high-assurance dispatchers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Synthesized fixes addressing 5 unique blockers and 5 warnings from the
multi-model review by claude-opus-4.7-1m-internal and gpt-5.5.

Blockers:
  1. (Opus) restore result_labels to D1 verdict members; IFC propagation
     was silently dropped.
  2. (Opus) renumber approval to §24 to avoid colliding with upstream §22
     Versioning and §23 References; patch summary-of-impacts.
  3. (Opus) AGT-RESOLUTION emitted policy_set on a type:rego policy; rego
     only accepts bundle. Rewrote §2.5 to materialize a Rego bundle on
     disk and bind type:rego with bundle path.
  4. (GPT)  AGT-RESOLUTION path traversal returned an empty manifest that
     evaluates to allow; replaced with fail-closed
     runtime_error:resolution_path_traversal. §5 empty-manifest fallback
     removed; missing governance now MUST fail closed or substitute a
     host-registered default.
  5. (GPT)  D1.4 action identity bound only to pre-transform input; auditor
     could not replay the executed action. Bisected into input_identity
     and enforced_identity; approval binding moves to enforced_identity.

Warnings:
  - Cedar default mapping aligned to envelope.agent.id per AGT-SNAPSHOT §1
    (was snapshot.agent.id).
  - Telemetry event names standardized on upstream
    intervention_point.{allowed,denied,warned,escalated} plus the new
    intervention_point.transformed; removed invented intervention_point.decided.
  - Six new runtime_error:resolution_* reasons added to D6.
  - Cedar advice schema specified in D3.3.
  - AGT-SNAPSHOT §2.2 clarifies IFC paths are input.ifc.* and
    response.ifc.*, not snapshot.ifc.*.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
…eld, AGT reserved reasons

M2.S1 and M2.S3 from plan v3. Implements SPECIFICATION-AGT-DELTA D1 and D2
without removing the upstream effects path (kept for parity through M2.S5
when the workspace split lands and effects can be feature-gated off).

verdict.rs:
  - Decision::Transform variant added; permits() helper bisects allow/warn/
    transform from deny/escalate.
  - Transform struct parses {path, value}; rejects transforms whose path is
    not rooted at  (TransformTargetForbidden) or whose path
    fails JsonPath parse.
  - Evidence struct parses {artefact, verification_pointers}; sorted
    pointer_keys() helper for telemetry per AGT-EVIDENCE-1.0 §3.
  - normalize_policy_output rejects transform on non-transform decisions and
    transform decisions without a body.
  - 12 new unit tests; lib suite 43 → 55.

error.rs:
  - 3 new variants for D6 reserved reasons:
      TransformTargetForbidden -> runtime_error:transform_target_forbidden
      TransformInvalid         -> runtime_error:transform_invalid
      ApprovalResolverMissing  -> runtime_error:approval_resolver_missing
  - reason() and detail() updated; AGENTS.md house style preserved.

Test suite: 130 tests pass, 0 failures (was 118).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
…eview

Round-2 review by claude-opus-4.7-1m-internal and gpt-5.5 reached
consensus: 0 blockers, no disagreements. Both reviewers raised 2
warnings each, all converging on these 4 fixes.

  - DELTA summary-of-impacts §5 Modes was 'Unchanged' but D1's effects
    removal changes evaluate_only validation semantics. Now describes the
    transform-shaped validation.
  - DELTA §11 IFC was 'Unchanged' but AGT-SNAPSHOT diverges from upstream
    on the path (input.ifc.* vs input.snapshot.ifc.*). Now flagged as
    path-clarified and the upstream policy/lib/ifc.rego replacement is
    called out so M4 doesn't ship a fail-closed-on-every-call default.
  - DELTA §19 omitted the removal of intervention_point.effect_applied.
    Now stated explicitly and points consumers at intervention_point.transformed.
  - AGT-EVIDENCE §4 used SHOULD store while DELTA D1.4 used MUST be in
    every audit record. Tightened to MUST.

No code changes; spec docs only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
….S4)

Add ApprovalSection, ApprovalResolverConfig, and ApprovalOnTimeout types
to the vendored ACS manifest parser. The new field on Manifest is
optional and backwards compatible; manifests without an `approval` block
continue to parse and validate as before.

The Manifest::approval() accessor exposes the parsed section. The
runtime treats resolver configuration as opaque per
SPECIFICATION-AGT-DELTA D5; only the section shape is validated.

Validation rules per D5:
  - on_timeout must be one of deny|allow|suspend
  - default_resolver must match a key in resolvers when both are set
  - timeout_seconds, fatigue_threshold, fatigue_window_seconds when
    present must be > 0
  - bad shapes fail closed with runtime_error:manifest_invalid

10 unit tests cover all five validation rules plus three positive
parse-and-round-trip cases. Test suite grows from 130 to 140 passing.

This commit was originally landed with the wrong subject line during a
worktree coordination overlap; the reword corrects the history without
changing any file content.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Promote Cedar to a built-in policy type alongside rego, test, and custom,
implementing the manifest-side surface from AGT M2.S2 per
`policy-engine/spec/SPECIFICATION-AGT-DELTA.md` §D3.1.

The new `PolicyConfig::Cedar(CedarPolicyConfig)` variant accepts the
fields fixed by D3.1: `policy_set` xor `policy_path` (exactly one
required), optional `entities_path`, optional `schema_path`, and an
optional `query` object whose shape is open for AGT v5. Unknown fields
are rejected via `serde(deny_unknown_fields)` so a manifest that mixes
rego-shaped fields (e.g. `bundle`) into a cedar policy is caught at
deserialization. Relative cedar paths resolve against the declaring
manifest's directory in `resolve_relative_paths`, matching the
rego.bundle behaviour.

`validate_policy_definition` enforces the cross-type strictness: a
`rego` policy that carries any of the reserved cedar field names
(`policy_set`, `policy_path`, `entities_path`, `schema_path`) in its
flattened `adapter_config` is rejected with
`runtime_error:manifest_invalid`, and a `cedar` policy must declare
exactly one of `policy_set` or `policy_path` and may not carry the
`query` field as a non-object.

The prepared-invocation surface for cedar lands in the next commit
(M2.S2 D2). To keep this commit compilable and to preserve the
fail-closed contract in the interim, `prepare_policy_invocation`
returns `runtime_error:policy_invocation_failed` for any cedar binding
that reaches it. No existing rego, test, or custom path changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Mohammad Haroon Abuomar <MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Add policy-engine/spec/schema/approval.schema.json (draft 2020-12)
describing the AGT D5 top-level approval section shape: default_resolver,
timeout_seconds, on_timeout enum, fatigue_threshold,
fatigue_window_seconds, and named resolvers with a required type
discriminator plus open additional properties.

Reference the new schema from manifest.schema.json as an optional
approval property so manifest validators (current and future) load it
through the existing schema tree. The engine still treats resolver
configuration as opaque per SPECIFICATION-AGT-DELTA D5; the schema
validates shape only.

Refs M2.S4, AGT 5.0 architecture-exploration Q13.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Wire the cedar branch of `prepare_policy_invocation` to its own
`PreparedPolicyInvocation::Cedar(CedarPolicyInvocation)` variant rather
than the M2.S2 D1 placeholder error. The prepared invocation carries
the resolved cedar policy source (`policy_set` xor `policy_path`), the
optional `entities_path` / `schema_path` artefacts, the optional
request-template `query` from the policy definition, the final policy
input the runtime built for this intervention point, and the canonical
JSON serialization of that input.

`engine_type()` returns the new `cedar` constant and `policy_input()`
exposes the input for the cedar arm, keeping the prepared-invocation
surface symmetrical across all four policy types.

The dispatcher trait and the CedarTestDispatcher reference
implementation land in the next commit (M2.S2 D3). The existing
OpaPolicyDispatcher continues to reject non-rego invocations with
`runtime_error:policy_invocation_failed` per SPECIFICATION.md §12.3,
so a cedar binding bound to the OPA dispatcher still fails closed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Mohammad Haroon Abuomar <MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Reads input.snapshot.envelope.budgets per AGT-SNAPSHOT-1.0.md §1 and
emits AGT deny verdicts (SPECIFICATION-AGT-DELTA.md §D1) when any host
tracked counter has reached its configured limit. Provides individual
predicates (max_tool_calls_exceeded, max_tokens_exceeded,
timeout_exceeded, max_cost_exceeded) and a combined
deny_if_budget_exceeded helper for the M6 GovernancePolicy migration
path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
PII regex constants track the canonical source list in
agent-os/src/agent_os/integrations/base.py::PII_PATTERNS (SSN, email,
phone, credit card, secret). Provides matches_any, first_match (which
returns the earliest matching span across all patterns), and a
deny_if_pattern helper that yields the AGT deny verdict shape from
SPECIFICATION-AGT-DELTA.md §D1. The earliest selector breaks ties on
pattern index so verdicts are deterministic across SDKs and OPA
versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Reads input.tool.content_hash (manifest tool catalog) and
input.snapshot.tool_call.content_hash (per AGT-SNAPSHOT-1.0.md §2.5)
and denies with reason tool_content_hash_mismatch when the snapshot
hash is missing or differs from the manifest-declared hash. Returns no
verdict when the manifest did not declare a hash, so the helper is
safe to include unconditionally in the default policy.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Reads input.tool.security_labels as an allowlist of permitted egress
hosts (per SPECIFICATION §11 a tool entry MAY set security_labels) and
resolves the call destination from a small list of common snapshot
paths under input.snapshot.tool_call.args plus the
input.annotations.egress.destination override. Hosts may pass their
own destination_paths and allowlist via the rules argument.
glob.match handles wildcard entries such as *.example.com without
requiring authors to spell out every subdomain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Reads input.annotations.drift_score (host-supplied annotator output,
range 0..1) and produces an AGT warn verdict with reason
drift_detected when the score reaches the configured threshold. The
helper returns nothing when the annotation is absent or non-numeric so
callers can chain it into a default policy without false positives.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Reads input.annotations.confidence.score (host-supplied annotation,
range 0..1) and emits an AGT deny verdict with reason
confidence_below_threshold when the score falls below the manifest
configured minimum. Returns nothing when the annotation is absent or
non-numeric so the policy falls through to allow on missing signal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Combines agt.patterns.first_match with the AGT transform verdict shape
(SPECIFICATION-AGT-DELTA.md §D1.1). The returned verdict carries
transform.path equal to $policy_target and a fully replaced value, so
the dispatcher applies the substitution without host side logic. The
substitution runs in Rego via a single regex.replace over the
combined pattern alternation, which keeps redaction deterministic
across SDKs and avoids the recursive rule restriction in OPA.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Produces AGT escalate verdicts (SPECIFICATION-AGT-DELTA.md §D1.2) that
the host approval path (§17.1) resolves through the resolver declared
in the approval manifest section (§D5). escalate_if guards a verdict
on a host supplied condition; escalate_if_approver_required emits the
approval_required reason when the manifest names a non-empty approver
list; escalate_with_message carries a free form human message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
AGT stock IFC label-flow library. The function surface (dominates,
max_sensitivity, flow_allowed, allow, deny, verdict,
verdict_propagating, and their _with_lattice variants) mirrors the
upstream agent_control_specification.lib.ifc package so policies
written against the AGT helpers stay familiar, but the snapshot paths
are AGT-correct per AGT-SNAPSHOT-1.0.md §2.2 and §2.7. The library
exposes source_labels and result_labels convenience accessors that
read input.snapshot.input.ifc.source_labels and
input.snapshot.response.ifc.result_labels respectively, plus an
allow_if_dominates shorthand for the no write down policy. The
upstream library reads input.snapshot.ifc.* which AGT does not
populate, so AGT users MUST import data.agt.ifc instead.

The upstream policy/lib/ifc.rego and policy/lib/ifc_test.rego are kept
in place because examples/ifc_agent and the spec-18-ifc conformance
case references still depend on the upstream package name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Default verdict rule for hosts that do not author their own Rego. The
manifest binds its rego policy to data.agt.defaults.verdict and
supplies thresholds, allowlists, and pattern lists under
data.agt.defaults.config (loaded as an OPA data document). The rule
chains every AGT stock helper in severity order: ifc deny > confidence
deny > budgets deny > content_hash deny > egress deny > pattern deny
> approval escalate > redact transform > drift warn > allow. The cfg
helper avoids a self recursive rule by reading
data.agt.defaults.config from outside the package namespace. This is
the GovernancePolicy auto translation target for the M6 migration
tool.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Invokes opa test against every library file and its sibling _test.rego
in policy-engine/policy/lib. Honors OPA_BIN override, falls back to
~/.local/bin/opa when opa is not on PATH, and exits non-zero on test
failure so CI can gate on the result.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
…stDispatcher

Land the dispatcher surface for the AGT D3 built-in cedar policy type:

- `CedarPolicyDispatcher` is the host-facing trait parallel to the rego
  dispatcher path in `opa.rs`. Implementations evaluate a
  `CedarPolicyInvocation` and return a verdict-shaped JsonValue that
  the runtime normalizes through `normalize_policy_output`.
- `build_cedar_request` implements the AGT D3.2 default mapping. The
  principal is `Agent::"<envelope.agent.id>"`, the action is
  `Action::"<intervention_point>"`, the resource is `Tool::"<name>"`
  when a tool is projected and `PolicyTarget::"<kind>"` otherwise, and
  the context keys are the snapshot keys (minus envelope) plus the
  `annotations.*` keys. The source paths follow
  `spec/agt/AGT-SNAPSHOT-1.0.md` §1.
- `CedarTestDispatcher` is the deterministic test double tests can drive
  per D3.3. It parses `policy_set` as a small JSON pseudo-cedar document
  (rules with `effect`, `principal`, `action`, `resource`, optional
  `reason`, optional `advice`), builds the cedar request, matches rules
  by entity equality (forbid wins, then first permit), and emits an
  allow, deny, or advice-translated verdict.
- `translate_advice` validates the AGT D3.3 advice shape (verdict in
  warn / escalate / transform, transform body required for transform,
  string-typed reason and message) and converts advice JSON into the
  verdict JSON the runtime expects. Path-in-$policy_target validation
  remains in `verdict::Transform::from_value` so a transform advice with
  a path outside `$policy_target` fails closed with
  `runtime_error:transform_target_forbidden` exactly like any other
  transform verdict, keeping the error contract centralized.

Both dispatchers also implement `PolicyDispatcher` so a host can swap a
cedar dispatcher in directly behind the runtime, the same way
`OpaPolicyDispatcher` does for rego.

The feature-gated `CedarBuiltinDispatcher` backed by the upstream
`cedar-policy` 4.x crate is deferred to a follow-up milestone. I could
not validate that build path in the current dev environment: the `cc`
on `PATH` is a `zig cc` wrapper that rejects the
`--target=x86_64-unknown-linux-gnu` target query that `cc-rs` passes
when compiling the `psm` transitive dependency of `cedar-policy`'s
`stacker` dep. The prompt explicitly allows this fallback. The trait
surface, the test dispatcher, and the manifest plumbing land now;
hosts that need real cedar evaluation today implement
`CedarPolicyDispatcher` themselves and link `cedar-policy` at the host
crate level. The builtin lands once the dev container ships a real
`gcc` or once we pin to a cedar-policy version whose deps avoid the
`stacker` / `psm` chain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Mohammad Haroon Abuomar <MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Land the normative JSON schema for the AGT D3.3 cedar advice payload at
`policy-engine/spec/schema/cedar_advice.schema.json`. The schema is
draft 2020-12, matches the artefact set under `spec/schema/wire/`, and
captures the contract `SPECIFICATION-AGT-DELTA.md` §D3.3 fixes:

- `verdict` is required and limited to `warn`, `escalate`, or
  `transform`. The `allow` and `deny` decisions never come from
  advice; cedar's own authorization result drives those.
- `reason` is optional and MUST NOT use the reserved
  `runtime_error:` prefix.
- `message` is optional and free form.
- `transform` is the AGT D1.1 single target replacement body. It is
  required when `verdict` is `transform` and forbidden otherwise. The
  conditional is expressed with an `allOf` / `if` / `then` / `else`
  block so that a malformed advice fails closed at validation time
  rather than at `Transform::from_value` time. `transform.path` MUST
  be rooted at `$policy_target`; the runtime still enforces the
  path-in-target invariant inside `normalize_policy_output` and emits
  `runtime_error:transform_target_forbidden` for a violating path.

The cedar dispatcher (see `core/src/cedar.rs::translate_advice`)
performs the same shape validation in Rust to keep the manifest-loader
and dispatcher boundaries independent of the JSON schema artefact at
runtime; the schema artefact is the canonical documentation source for
SDKs and policy authors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Mohammad Haroon Abuomar <MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
…spatcher

Add the AGT M2.S2 D5 test coverage for the new cedar surface.

Manifest validation in `policy.rs::cedar_manifest_tests`:

- rego policy with `policy_set` is rejected with
  `runtime_error:manifest_invalid`
- rego policy with `policy_path` is rejected with the same reason
- cedar policy with the rego-shaped `bundle` field is rejected at
  deserialization via `deny_unknown_fields`
- cedar policy with neither `policy_set` nor `policy_path` is rejected
- cedar policy with both `policy_set` and `policy_path` is rejected
- positive coverage: cedar with only `policy_set` or only
  `policy_path` (plus optional entities and schema paths) parses cleanly
- cedar with an unknown field is rejected by `deny_unknown_fields`
- cedar with a non-object `query` is rejected

Dispatcher behaviour in `cedar.rs::tests`:

- AGT D3.2 default mapping: principal is
  `Agent::"<envelope.agent.id>"`, action is
  `Action::"<intervention_point>"`, resource is
  `Tool::"<name>"` when a tool is projected and
  `PolicyTarget::"<kind>"` otherwise, context keys exclude envelope
- a missing envelope agent id fails closed with
  `runtime_error:policy_invocation_failed`
- AGT D3.3 allow path: a permit rule with no advice produces a normalized
  `Decision::Allow` verdict
- AGT D3.3 deny path: a forbid rule wins over a permit rule and the
  rule reason flows through to the verdict reason
- no matching rule emits `deny` with `no_matching_policy`
- AGT D3.3 advice translation produces `transform`, `escalate`, and
  `warn` verdicts with reason and message preserved
- malformed advice fails closed with
  `runtime_error:policy_output_invalid` for: missing `verdict`,
  unknown `verdict`, transform without body, and warn / escalate
  carrying a transform body
- AGT D1.1 confinement: transform advice with a path outside
  `$policy_target` fails closed with
  `runtime_error:transform_target_forbidden` after
  `normalize_policy_output` re-validates the dispatcher output, proving
  the existing path-in-target check covers the cedar advice path with
  no duplicate logic
- dispatcher error paths: missing inline `policy_set`, invalid policy
  set JSON, and non-cedar invocations routed through the
  `PolicyDispatcher` trait all fail closed with
  `runtime_error:policy_invocation_failed`

All 169 `agent_control_specification_core` tests pass (29 added by this
commit, 140 pre-existing). `cargo clippy --all-targets -- -D warnings`
on the core crate is clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Mohammad Haroon Abuomar <MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
…1.0 (M3.S1)

M3.S1 from plan v3. New top-level Python package agt-policies (5.0.0a1)
that hosts AGT 5.0 host-side primitives over the vendored ACS engine.

This commit lands the manifest resolution layer:

  agt.manifest_resolution.discover   - §2.1 governance.yaml walk with
                                       path-traversal fail-closed (not
                                       v4's empty-list-allow)
  agt.manifest_resolution.scope      - §2.3 glob-based scope filter
  agt.manifest_resolution.merge      - §2.4 rule merge preserving the
                                       deny-immutability invariant
                                       across chains; same-name-without-
                                       override drops the child
  agt.manifest_resolution.build      - §2.5 end-to-end resolve_manifest
                                       that materializes a generated Rego
                                       bundle under .agt/resolved-bundle/
                                       and emits a flat ACS manifest with
                                       extends:[] ready for the engine
  agt.manifest_resolution.errors     - D6 reserved resolution reasons
                                       wired as a ResolutionError class

29 pytest tests covering: path-traversal fail-closed; root-first
ordering; scope glob normalization; rule merge with deny-immutability
and same-name-no-override drops; top-level section merges; end-to-end
bundle materialization with sha256 sidecar; intervention point
annotations union; inherit:false truncation; reserved reason strings
matching D6 byte for byte.

29 passed in 0.12s.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: AGT 5.0 ACS merge <agentgovtoolkit@microsoft.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
liamcrumm and others added 2 commits June 2, 2026 08:07
The compose `test` service ran agent-os's suite but the root Dockerfile
never installed `agt-policies` (import name `agt`), which agent-os imports
at runtime but does not declare as a pip dependency. Non-v5 tests that
touch the policy layer failed at runtime with `ModuleNotFoundError: No
module named 'agt'`.

Install it alongside the other editable sibling packages. The v5 native
runtime (`agent_control_specification`) is still absent in this image, but
those bridge tests are guarded by an importorskip/skipif and the bridge
imports the binding lazily, so they skip cleanly rather than error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
The docker-compose-test image installed agt-policies but not the
agent_control_specification native binding its v5 runtime bridge
hard-requires. Every adapter that routes a check through the bridge
raised ModuleNotFoundError, failing 232 tests. Add the maturin-based
SDK build (Rust stable + maturin, gcc already present) to the dev
stage, mirroring the test (agent-os) CI matrix job, with an in-layer
import smoke test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
liamcrumm and others added 6 commits June 2, 2026 08:56
ACS (the vendored policy-engine/ runtime) was invisible in the
top-level docs. Surface it as an AGT module:

- Root README: add ACS to the Packages and Specifications tables.
- Docs site: new packages/agent-control-specification.md page, wired
  into the mkdocs nav, the packages overview, and the homepage cards
  and Specifications table.
- policy-engine/README.md: reshape into an AGT-module README that
  leads with what ACS is (why, what, core idea, example) and how the
  AGT integration works (host adapters, agt-policies bridge, native
  runtime), folding in the integration framing. Fix two stale doc
  links and remove the redundant README.vendored-acs.md.
- Python SDK README: add a What is ACS intro.
- Glossary: add Agent Control Specification, Intervention Point,
  Transform Verdict, and Verdict.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Rework the ACS package page header so it renders on the MkDocs site
(markdown-enabled centered block, real badges, and a styled Public
Preview admonition instead of a raw GitHub blockquote).

Remove the upstream repository provenance sentence and the Original ACS
source attribution row from the policy-engine README so ACS reads as
AGT-owned source. MIT attribution is preserved via LICENSE.acs and the
License section.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Remove the demo_multisdk directory and its Rust example. These were
live-demo scratch artifacts that hardcoded absolute developer paths
(/home/liamcrumm/...) in the Rust and .NET entrypoints and are not wired
into CI, docs, or Cargo example targets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Remove workspace/notes.txt and report.md, which are produced by the
coding_agent example at runtime, and gitignore them so example runs do
not redirty the tree. The hello.txt input fixture is kept.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Inline the seven AGT divergences (D1-D7) from SPECIFICATION-AGT-DELTA.md
directly into SPECIFICATION.md so the policy-engine ships a single
authoritative contract, then delete the delta file and repoint every
reference to it.

Spec changes:
- Replace the upstream effects model with the transform verdict (new
  section 14, fifth decision, transform_target_forbidden and
  transform_invalid reasons).
- Add evidence object on verdicts (section 13.3) and its telemetry
  propagation (evidence_artefact plus evidence_verification_pointer_keys,
  never the pointer URL values).
- Add the cedar policy type (section 12.4) with request and verdict
  mapping that matches core/src/cedar.rs.
- Bisect action identity into input_identity and enforced_identity
  (section 13.1); approval binds to enforced_identity.
- Document the optional approval manifest section (section 24) with the
  full ApprovalSection shape, and the approval_resolver_missing reason.
- Add the AGT host-side extends resolution note (section 2.2) and the
  resolution_* reserved reasons (section 16).
- Section 16 now carries the full 18 canonical runtime-error reasons and
  drops effect_invalid and effect_target_forbidden.

Tests and references:
- parity_canonical.rs now reads only SPECIFICATION.md.
- Repoint all source, doc, schema, rego, and cedar references from the
  delta file to the relevant SPECIFICATION.md sections.

Verified: cargo test -p agent_control_specification_core green,
otel integration green, docs link check 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Wire agt-policies into Python CI and Docker tests, verify OPA downloads,
and pin the new workflow tooling installs.

Move resolved policy bundles out of governed workspaces, clean up runtime-owned
bundles, fix adapter budget accounting, and harden manifest-resolution policy
rendering against invalid fields and operator drift.

Remove the unpublished ACS SDK as a bare generator dependency and keep the
dependency-confusion scanner aware that the SDK name is local-only.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@liamcrumm
liamcrumm force-pushed the agent-policy-spec branch from 9aaa8cd to 19fc61f Compare June 2, 2026 15:45
liamcrumm and others added 4 commits June 2, 2026 15:53
Ensure resolved AGT governance rules are actually bound to the generated
agt_legacy_rules policy before emitting the ACS manifest. A governance chain
with rules but no matching intervention-point binding now fails closed instead
of producing rules that never execute.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add missing conformance cases for evaluate-only and transform semantics,
restore release-claim coverage, and update the Python conformance runner for
bisected action identity fields.

Add a coverage guard for ACS-backed Agent OS adapter scenario tests and harden
workflow generator tests around OPA checksum verification and pinned tooling.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use parsed URL hostnames to detect Azure OpenAI endpoints instead of substring
matching the full API base URL. This prevents path text such as .azure.com from
selecting Azure authentication mode.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
@liamcrumm
liamcrumm force-pushed the agent-policy-spec branch from 19fc61f to 1b58405 Compare June 2, 2026 16:03
liamcrumm and others added 5 commits June 2, 2026 16:13
Add the Open Policy Agent download host token to the repository spelling terms
so generated workflow URLs pass changed-line spell checking.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a step-by-step ACS tutorial that builds a direct policy enforcement point
with a manifest, Rego policy, and Python host flow covering allow, transform,
deny, and fail-closed behavior.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring 4 new upstream commits into the AGT 5.0 / ACS branch:
- docs: polish docs site (UX enhancement, clean IA) (microsoft#2771)
- fix(mesh-sdk): close client-side gap with POP-aware relay/registry (v4.0.0) (microsoft#2772)
- fix(attestation): make verify_evidence strict by default (CVSS 8.1) (microsoft#2769)
- fix(ci): harden JS lint steps - fix mastra-agentmesh TS errors (microsoft#2745)

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After Liam's ACS sync (`e5b64ea4`), the EndpointAnnotator's URL-config
validation message changed from "missing required field 'url'" to
"missing required field 'endpoint' or 'url'" — both names are now
accepted, with `endpoint` preferred. The
bad_endpoint_config_reserved_reason_and_oversize_outputs_fail_closed
test in policy-engine/integrations/annotators/tests/moderation_flow.rs
was still asserting the old wording and was the only failing test in
the workspace.

Updated assertion to match the new message. Verified by running
`cargo test -p agent_control_specification_annotators --test
moderation_flow --release --all-features` — 6/6 pass.

Full workspace --all-features: 373/0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Exclude the dynamic Discord badge image from markdown-link-check because its
Cloudflare challenge returns 403 to automated checkers while the actual Discord
invite link remains checked separately.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MohammadHaroonAbuomar MohammadHaroonAbuomar changed the title Introducing Agent Control Spec (ACS) feat: Introducing Agent Control Spec (ACS) Jun 2, 2026
@MohammadHaroonAbuomar MohammadHaroonAbuomar changed the title feat: Introducing Agent Control Spec (ACS) feat(policy-engine): Introducing Agent Control Spec (ACS) Jun 2, 2026
@MohammadHaroonAbuomar
MohammadHaroonAbuomar marked this pull request as ready for review June 2, 2026 17:34
@MohammadHaroonAbuomar MohammadHaroonAbuomar changed the title feat(policy-engine): Introducing Agent Control Spec (ACS) feat(policy-engine): introducing agent control spec (acs) Jun 2, 2026
@jackbatzner Jack Batzner (jackbatzner) changed the title feat(policy-engine): introducing agent control spec (acs) feat(policy-engine): introducing Agent Control Spec (ACS) Jun 2, 2026
@amolr Amol Ravande (amolr) self-assigned this Jun 2, 2026
@MohammadHaroonAbuomar
MohammadHaroonAbuomar merged commit 484724c into microsoft:main Jun 2, 2026
129 of 135 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation scripts/ci/cd size/XL Extra large PR (500+ lines) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants