Skip to content

refactor(v4-removal)!: move the Rust SDK onto ACS v5 manifests - #3445

Merged
MohammadHaroonAbuomar merged 1 commit into
liamcrumm/v4-removal-examplesfrom
liamcrumm/v4-removal-rust-sdk
Jul 29, 2026
Merged

refactor(v4-removal)!: move the Rust SDK onto ACS v5 manifests#3445
MohammadHaroonAbuomar merged 1 commit into
liamcrumm/v4-removal-examplesfrom
liamcrumm/v4-removal-rust-sdk

Conversation

@liamcrumm

@liamcrumm liamcrumm commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the Rust agentmesh crate onto ACS v5 manifests so it evaluates policy through the same engine as the Python runtime. PR 2 of 4 in the v4 policy-language removal.

Problem

The Rust SDK still built policy input in the v4 shape and evaluated it with a vendored copy of the old logic. That meant a policy could behave differently in Rust than in Python, and the crate had no way to consume the ACS manifests that the rest of the toolkit now produces.

Changes

File What changed
agent-governance-rust/agentmesh/Cargo.toml Depends on the agent_control_specification workspace crate.
agent-governance-rust/Cargo.toml, Cargo.lock Declares the ACS path dependency and regenerates the lockfile.
agent-governance-rust/agentmesh/src/lib.rs, integration_support.rs Policy evaluation goes through ACS instead of the v4 code path.
agent-governance-rust/agentmesh/MIGRATION_V5.md Migration notes for crate consumers.
docs/dependency-audits/2026-07-27-rust-acs-dependency.md Dependency audit for the lockfile change.
scripts/v4_ratchet_baseline.json Regenerated, as every PR in this stack must.

| integration_support.rs | with_host_config returns Result instead of panicking on an out-of-range config. new stays infallible because the default is valid by construction. |
| integration_support.rs | Removes FrameworkExecutionResult::matched_patterns. ACS returns a verdict and reason rather than a match list, so the field had no source and returned an empty vector on every call. Removing it gives callers a compile error instead of silently no matches. |
| integration_support.rs | Budgets are no longer hard-coded to zero. Elapsed time is tracked from construction and record_usage lets a host report tokens and cost, which is the only place that data exists. Without it, manifest budget rules on tokens or cost never fired. |
| MIGRATION_V5.md | Documents all three. |

Testing

  • cargo check --offline --workspace: compiles, with the crate's four pre-existing warnings unchanged
  • cargo metadata --offline --locked: the committed lockfile matches the manifests
  • scripts/check_v4_ratchet.py: passes, marker count drops from 656 to 629
  • scripts/tests/test_check_v4_ratchet.py: 35 passed

Stack

Based on the Python runtime PR. Merges cleanly once that lands, without a branch update.

@github-actions github-actions Bot added documentation Improvements or additions to documentation size/XL Extra large PR (500+ lines) labels Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 3 package(s) with unknown licenses.
  • ⚠️ 3 packages with OpenSSF Scorecard issues.

View full job summary

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

📦 Dependency diff (SBOM)

Comparing liamcrumm/v4-removal-examplesliamcrumm/v4-removal-rust-sdk.

Summary: ➕ 0 added · ➖ 0 removed · 🔄 8 bumped

🔄 Bumped

cargo (8)

Package From To
bitflags 2.11.0 2.13.1
cc 1.2.61 1.2.67
libc 0.2.184 0.2.189
log 0.4.29 0.4.33
memchr 2.8.0 2.8.3
syn 3.0.2 2.0.119
zerocopy 0.8.48 0.8.55
zerocopy-derive 0.8.48 0.8.55

@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-python-core branch from f0845b6 to 123154f Compare July 27, 2026 19:50
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from 9ad1f2d to ff84d28 Compare July 27, 2026 19:50
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-python-core branch from 123154f to 4e9276b Compare July 27, 2026 20:17
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from ff84d28 to 1fe2b37 Compare July 27, 2026 20:17
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-python-core branch from 4e9276b to f3e6563 Compare July 27, 2026 21:57
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch 5 times, most recently from 3ebf36c to 0e960e8 Compare July 28, 2026 01:50
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-python-core branch from 593c6dd to 1f7437b Compare July 28, 2026 01:50
@liamcrumm
liamcrumm marked this pull request as ready for review July 28, 2026 02:04

@MohammadHaroonAbuomar MohammadHaroonAbuomar 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.

  • agent-governance-rust/agentmesh/src/integration_support.rs:230: with_host_config panics via .expect() on an invalid FrameworkHostConfig; validate() already returns Result, and a public constructor in a breaking-change PR should be fallible (Result<Self, RuntimeError>), not a runtime panic.
  • agent-governance-rust/agentmesh/src/integration_support.rs:197: FrameworkExecutionResult.matched_patterns is retained but now always empty (also lines 379, 406). Drop the field (the PR is already breaking) or document it as vestigial; silently returning [] to consumers who keyed on it is worse than a compile error.
  • agent-governance-rust/agentmesh/src/integration_support.rs:314-341: the snapshot hard-codes token_count/elapsed_seconds/cost_usd to 0 and EnforcementMode::Enforce; manifest budget rules on tokens/cost silently never trigger through this adapter. Note it in MIGRATION_V5.md at minimum; ideally expose budgets on FrameworkHostConfig.
  • CI process: build-rust and the v4 ratchet never ran at this head (workflows trigger only on pull_request to main, and retargeting fires edited, which they don't listen for). Push a no-op commit after retarget or widen the trigger types; confirm build-rust is a required check so retarget cannot auto-merge a vacuous green board. (Local verification at head: cargo check clean, 377/377 agentmesh tests pass, ratchet 629 OK.)

Minor:

  • #3420 lockfile conflict confirmed: same Cargo.toml/Cargo.lock; whichever lands second regenerates the lockfile. Merge-order coordination only, no design conflict.

Comment thread docs/dependency-audits/2026-07-27-rust-acs-dependency.md Outdated
Comment thread agent-governance-rust/agentmesh/MIGRATION_V5.md
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from 8def1a6 to 96fd5fc Compare July 28, 2026 05:17
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from bf5a5be to d5f057c Compare July 28, 2026 09:20
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from 96fd5fc to b7e7f11 Compare July 28, 2026 09:20
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from d5f057c to 5e1c4f2 Compare July 28, 2026 18:00
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch 2 times, most recently from 3b845ec to f16b367 Compare July 28, 2026 18:52
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from 5e1c4f2 to 7d9cbb9 Compare July 28, 2026 18:52
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from f16b367 to 3a329c6 Compare July 28, 2026 22:36
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from 7d9cbb9 to c345ccb Compare July 28, 2026 22:36
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from 3a329c6 to e8fa354 Compare July 29, 2026 00:27
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from c345ccb to 60b66de Compare July 29, 2026 00:27
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from e8fa354 to 8035bec Compare July 29, 2026 00:51
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch 2 times, most recently from 69d6e72 to e67f66f Compare July 29, 2026 15:23
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from 8035bec to 2c48764 Compare July 29, 2026 15:23
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from e67f66f to 5fd280c Compare July 29, 2026 15:40
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch 2 times, most recently from 30b908e to dbbcc41 Compare July 29, 2026 15:49
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from 22b84fc to a96ce09 Compare July 29, 2026 16:20
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from dbbcc41 to 891fc76 Compare July 29, 2026 16:21
…nt deny

The audit claimed nothing was pulled from crates.io that was not already in the
graph. Cargo.lock gains 66 entries: 2 first-party and 64 third-party crates
that were not in the workspace before. It now lists them and names the two
clusters.

The second cluster is the one that matters: ureq, rustls, ring and
webpki-roots link an HTTP and TLS stack into agentmesh. They arrive because the
ACS Rust SDK pins its core features to include the remote annotators and
because Manifest::from_url exists, and a consumer cannot opt out since the
feature set is fixed inside the SDK crate. Nothing in agentmesh calls a remote
annotator or from_url, so no egress is added in practice, but for a governance
library the linked capability belongs in the security record. Narrowing the
SDK's features is a follow-up on that crate.

MIGRATION_V5.md also now documents that the runtime denies an intervention
point the manifest does not declare. Failing closed is right, but the adapter
evaluates input on every execute() and pre_tool_call on every tool call, so a
manifest binding only one denies the other path with no obvious cause.

Links: 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-rust-sdk branch from 891fc76 to cee8c52 Compare July 29, 2026 17:35
@liamcrumm
liamcrumm force-pushed the liamcrumm/v4-removal-examples branch from a96ce09 to 8b0e96e Compare July 29, 2026 17:35
@MohammadHaroonAbuomar
MohammadHaroonAbuomar merged commit 49a2745 into liamcrumm/v4-removal-examples Jul 29, 2026
33 of 35 checks passed
@MohammadHaroonAbuomar
MohammadHaroonAbuomar deleted the liamcrumm/v4-removal-rust-sdk branch July 29, 2026 17:38
MohammadHaroonAbuomar added a commit that referenced this pull request Jul 30, 2026
* refactor(v4-removal)!: move the examples onto ACS v5 manifests

Example policies and bundles move to ACS manifests, and the demos that existed
only to show v4 syntax are deleted with their fixtures. The package-local
examples, docs, tutorials, benchmarks and notebooks under agent-os follow the
same move.

docs/compliance/nist-rfi-2026-00206.md moves here too, because it links to the
native A2A runtime example this PR adds and the link would dangle if the two
landed separately.

Also carries two review fixes:

  sync_atr_rules interpolated third-party ATR patterns into a Rego backtick
  raw-string literal. Raw strings have no escape mechanism, so a pattern
  carrying a backtick closed the literal and the rest parsed as policy source.
  The weekly sync compiles that corpus from npm unattended, so a poisoned
  upstream rule could flip a verdict on benign input. Patterns are now emitted
  as JSON-quoted strings and the validator rejects backticks. atr-import shares
  the emission path through compile_patterns.

  The demos README documented AgtRuntimeMiddleware, which is
  RuntimeGovernanceMiddleware, and advertised real LLM calls with Ollama setup
  instructions. The example it points at makes no model call, so that section
  described a mode that does not exist.

Ratchet: 656. Links: 0 new broken. atr tests: 5 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs(rust): correct the dependency audit and document the unbound-point deny (#3445)

The audit claimed nothing was pulled from crates.io that was not already in the
graph. Cargo.lock gains 66 entries: 2 first-party and 64 third-party crates
that were not in the workspace before. It now lists them and names the two
clusters.

The second cluster is the one that matters: ureq, rustls, ring and
webpki-roots link an HTTP and TLS stack into agentmesh. They arrive because the
ACS Rust SDK pins its core features to include the remote annotators and
because Manifest::from_url exists, and a consumer cannot opt out since the
feature set is fixed inside the SDK crate. Nothing in agentmesh calls a remote
annotator or from_url, so no egress is added in practice, but for a governance
library the linked capability belongs in the security record. Narrowing the
SDK's features is a follow-up on that crate.

MIGRATION_V5.md also now documents that the runtime denies an intervention
point the manifest does not declare. Failing closed is right, but the adapter
evaluates input on every execute() and pre_tool_call on every tool call, so a
manifest binding only one denies the other path with no obvious cause.

Links: 0 new broken.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

* docs(v4-removal): rewrite the documentation for ACS v5 (#3446)

* docs: use the real class names in the integration guides

A mechanical rename left several component names that resolve to nothing:

  AgtRuntimeMiddleware and AgtRuntimeMW are RuntimeGovernanceMiddleware, and
  CapabilityGuardMW, AuditTrailMW and RogueDetectionMW are the corresponding
  *Middleware classes in maf_adapter.

  AgtRuntimeChecker never existed in any SDK. The Haystack integration ships
  TrustGate and AuditLogger, which is what the tables now list.

  PolicyEvaluation.* in the .NET tutorial was the rename applied where it does
  not belong: the .NET engine still defines PolicyAction and no PR in this
  stack touches dotnet source. Reverted, and the file is allowlisted in the
  ratchet so documenting that engine's own enum does not read as v4 usage.

Ratchet: 94, unchanged. Links: 0 new broken. Ratchet tests: 35 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor(v4-removal)!: confine the v4 policy language to the migration tool (#3447)

* docs: scope the breaking-changes record to what each PR removes

The Python entry listed the whole stack's removals in one place, so it
overlapped the record the first PR now carries and named 33 symbols twice.
It also claimed agent_os.kernel keeps a PolicyRule; the surviving one is
re-exported from agent_control_plane.

What is left here is what could only go once the rule model went: manifest
resolution, the evaluators and decision types that read the rule model, the
framework integrations' local policy surfaces, and the unreleased cedarling
backend. Names that survive under the same spelling in an unrelated module
are called out, since agent_os still exports a PolicyDecision and a
PolicyRule that have nothing to do with the v4 language.

The langflow README documented GovernanceComponent, which this stack deletes.
Its section, the YAML policy that only configured it, its table row, and its
node in the flow diagram are gone, matching what the haystack and openshell
READMEs already do.

ratchet 0, links 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor!: delete agt.policies and evaluate through ACS directly (#3449)

* fix(acs): drop the dead manifest-timeout branch and correct the docs it claimed

_resolve_approval_timeout read approval.timeout_seconds off
getattr(control, "manifest", None). AgentControl never stores that attribute,
so the branch was dead in production and only ran in tests because the stub
defined one. BREAKING_CHANGES then documented behaviour the code could not
deliver.

The core treats the approval section as opaque host configuration
(SPECIFICATION §17.1) and does not surface it across the native boundary, so
honouring it needs an SDK accessor that does not exist. The branch is removed,
the docstring and BREAKING_CHANGES say a caller must pass the value, and the
300 second default that closes the original fail-open stays.

The tests that covered the branch went with it. A replacement drives a resolver
that never returns through a real HostSession and asserts it denies in about a
second, which the removed tests could not have caught.

Also repairs what earlier mechanical renames left behind:

  AgentControlMiddleware and AgentControlMW in the Python and MAF docs are
  RuntimeGovernanceMiddleware. The .NET class of that name is real but belongs
  to the ACS dotnet SDK, so the name resolved while meaning the wrong thing.

  AgentControlChecker and HaystackAdapter exist nowhere; the Haystack package
  ships TrustGate and AuditLogger. NoOpAgentControl and the compat.py path
  beside it were both deleted by this stack.

  Ten `the ACS manifest` substitutions where a symbol name used to be, across
  AGENTS.md, three compliance docs and openclaw-sidecar.md.

  Fourteen compliance citations pointing at agt/policies and agent_os/policies
  files this stack deletes, repointed at the ACS SDK.

  The agt-policies README still described the deleted wrapper, including two
  submodules that never existed and a snippet calling AgentControl.evaluate.
  Rewritten for what the package now is, a migration tool, and every command
  and symbol in it was run.

ACS host: 25 passed. agent-os: 2683 passed. Ratchet 0. Links: 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs: cite the live Haystack integration, not the deprecated package

The framework tables in docs/FAQ.md and agent-mesh/README.md pointed at the
deprecated haystack_agentmesh package and its TrustGate/AuditLogger
components. The maintained module is agentmesh.integrations.haystack, whose
exports are TrustGateComponent, TrustAgentComponent, TrustedPipeline, and
PipelineAuditEntry.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* test: delete the dead __init_result__ stub from _RecordingControl

The method is not a dunder Python calls, nothing in the suite invokes it,
and its body only raises NotImplementedError. Verified by AST that
_RecordingControl keeps __init__ and evaluate_intervention_point; the PyO3
_native extension is not built in this environment, so the module was
checked with py_compile rather than by running pytest.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs: describe the post-removal world with AgentControl, not AgtRuntime

Two sentences in BREAKING_CHANGES.md used AgtRuntime for what code takes
after the removal, contradicting the file's own removed-symbols table,
which maps AgtRuntime(path) to AgentControl.from_path. The mentions that
remain describe the removed v4 surface and stay as they are.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(openai-agents-trust): note the hooks path carries no tool arguments

The openai-agents lifecycle hook signature (on_tool_start/on_tool_end) does
not include the tool-call arguments, so GovernanceHooks evaluates
pre_tool_call and post_tool_call with args={}. Policy rules conditioned on
tool arguments therefore never match on this path. Say so at both call
sites and in the README, pointing at the guardrail integration for
arg-level enforcement.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* fix(openshell): stop pointing the deprecation at a package that ships this shim

The DeprecationWarning and README told users to install
agent-governance-toolkit-integrations[openshell], but that extra
force-includes this same package, whose skill (GovernanceSkill,
ShellPolicyViolation, governed_shell) this stack removes - the pointer was
circular. Say what is true: the skill is removed in the v5 ACS migration
with no OpenShell-specific replacement; build an AgentControl from an ACS
manifest and evaluate intervention points in the host. Packaging and
force-includes are unchanged.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(examples): remove the openshell-governed demo

The v4-removal stack deletes openshell_agentmesh.skill (GovernanceSkill),
which was this demo's only entry point. The example could no longer run;
remove it with the rest of the retired openshell surface.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs: retire the openshell integration page

The v4-removal stack deletes the openshell governance skill this page
documents (its quickstart imports the removed GovernanceSkill). Remove
the page and the openclaw-sidecar cross-link along with the example
removed in the previous commit.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
MohammadHaroonAbuomar added a commit that referenced this pull request Jul 30, 2026
…ntmesh integrations (#3451)

* refactor(v4-removal)!: replace the v4 policy language with ACS v5 across the Python runtime

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(agent-os): remove dead branches and duplicate assignments in the adapters

Review findings on the native adapter rewrite, all on paths the rewrite
carried across rather than introduced:

  openai_adapter.health_check branched on has_clients to return "healthy"
  either way. Collapsed to match the sibling adapters.

  openai_adapter tool mediation had an "escalate" branch after the
  not-allowed guard. escalate is not a permitting verdict, here or on main,
  so the guard always raised first and the branch never ran. The docstring
  already documents the surviving behaviour: an escalate the resolver
  refuses surfaces as a deny.

  agentshield_adapter called _get_or_create_context() twice in a row at
  three sites.

  ToolCallInterceptor.intercept used a bare "..." body; the sibling
  AdapterRuntime protocol in this package uses a docstring.

  test_policy_replay_metadata imported agt.policies both ways. It now uses
  the module form only, which is the one patch.dict(vars(policies)) needs.

The maf_adapter PolicyViolationError re-export stays. It looks unused, but
test_adapter_exception_identity asserts every adapter module resolves the
canonical error type, so a comment now records why it is there.

agent-os 2683 passed, agent-compliance 457 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* ci: fail policy validation on a manifest that omits its ACS version

The manifest scan keyed only on the agent_control_specification_version
substring, so a manifest that omits that field was not linted at all. For a
policy gate, skipping silently is the wrong direction: the file most likely to
be malformed is the one missing its version.

Files declaring intervention_points without a version are now reported.
intervention_points is the ACS-specific tell; keying on policies or agents
instead flags 22 unrelated config files in this repo that happen to use those
names, which is why the check is narrow.

Verified against the tree at every layer of this stack: no new findings, and a
probe manifest with intervention_points and no version is caught.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* ci: let BREAKING_CHANGES.md name the v4 symbols it removes

The ratchet counts any mention of a v4 name as a use of it. A breaking-changes
record has to name each removed symbol so a reader can find its replacement,
so the file is allowlisted. Naming a symbol in order to say it is gone is not
a use of it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: stop treating a transform verdict as plain permission

A transform verdict permits the call, but it permits a rewritten one: the
verdict carries the replacement the caller is expected to apply. Several
gates read only whether the verdict permits, so they ran the original value
while the policy believed it had been rewritten. A policy that redacts a
credential did not redact it, and nothing reported a failure.

Three groups of sites, three different resolutions:

The five sandbox providers now refuse a transform outright at the
pre_tool_call gate. That gate gets code it is about to execute and cannot
rewrite it, so refusing is the only honest answer. This gate is new in this
change, so the gap came in with it rather than predating it.

The four adapters that called post_execute and dropped the return value now
raise PolicyViolationError. main evaluated no output policy at all here, so
these call sites only became load-bearing once this change added it.

base.py returns (False, "transform_not_applicable") from the tuple helpers
instead of silently discarding the replacement, and NativeAdapterResult grows
permits_unchanged for callers that cannot apply one. allowed stays as it was;
a site that can apply a transform should keep using it.

Tests drive the real execute_code gate on all five providers with a real
PolicyEvaluation. The five transform cases fail without the guards and the
deny and allow cases pass either way, so they discriminate.

agent-sandbox 499 passed, agent-os 2827 passed, ratchet 1415, lint clean on
every changed file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs: record this PR's removed exports where the removal happens

The stack documented every Python removal in the last PR, six merges after
the removals land. Anyone on main between the two saw 75 public names
disappear with nothing to read.

The list is computed from the export diff against the merge base rather than
assembled by hand. That matters: the hand-written version I started from
claimed 51 names, and of those 31 were still exported at this point in the
stack because their removal happens in a later PR. The computed set is 75,
and every one of them is gone here.

The budget-counting change is recorded with them, since a deployed policy
now permits one more tool call than it used to.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: an unbound output point is not a denial

Making the adapters honour the output verdict broke every manifest that does
not bind an output intervention point. The engine reports an unconfigured
point as runtime_error:intervention_point_unknown, which is the right answer
to a request naming an unknown point. The adapter is not answering a request
though. It evaluates output after every call whether or not the host asked
for output governance, so reading that error as a denial blocked every
response under a manifest binding only input or only tool calls. Seven
adapter scenarios caught it, and the previous code hid it by discarding the
verdict.

post_execute now treats that one reason as "no output policy configured",
permits, and records completion. Completion mattered: it was already gated on
allowed, so budgets drifted on those manifests before this change too. Every
other reason still denies, including the other runtime errors.

Tests pin both directions, since the risk in relaxing this is that output
enforcement quietly stops working. Only the unconfigured case fails without
the fix; the six covering deny, transform, allow, and the other runtime
errors pass either way.

agt-policies 350, agent-os 2834, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: apply the unbound-point permit at the runtime, not the base class

The previous commit put the carve-out in BaseIntegration.post_execute. Only
six adapters reach output evaluation that way. Roughly eleven others call
evaluate_output directly (langchain, semantic_kernel, smolagents,
pydantic_ai, agentshield, google_adk, guardrails, crewai, llamaindex, openai,
openai_agents_sdk) and still hard-failed, so one manifest behaved two ways
inside one package. Reported by Haroon on #3444.

The permit now lives on NativeAdapterResult behind permit_if_unconfigured,
which NativeAdapterRuntime.evaluate_output sets. Every caller goes through
that method, so there is one place to reason about instead of 26 call sites.
point_not_configured is exposed so callers can tell an unconfigured point
from a denial. base.py drops its copy and reads allowed like everything else.

The flag is set for output only. input and pre_tool_call stay fail-closed:
omitting them omits governance of an action about to happen, and the fix
leans on pre_execute staying strict, so a test pins that.

The skip warns once per runtime rather than staying silent, since an operator
otherwise cannot see that output enforcement is off.

BREAKING_CHANGES records the split, including that the bridge used to rewrite
any unconfigured point to an allow.

Tests build the real NativeAdapterResult rather than a stand-in, because a
stand-in for the class carrying the permit would test the stand-in. Removing
the flag from the chokepoint fails exactly the direct-adapter test and no
other, so the evidence discriminates.

agent-os 2839, agt-policies 349, agent-sandbox 499, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: permit every unconfigured post-hoc point, not just output

Fixing output alone left the same bug at post_tool_call. Thirteen scenario
manifests bind pre_tool_call and none bind post_tool_call, so
openai_agents_sdk.py:521 raised on the configuration every scenario uses.
Nothing caught it because that adapter has no scenario test.

The line is not output versus the rest, it is whether the point gates an
action or runs after one. input, pre_tool_call and pre_model_call still deny:
the action has not happened, so refusing protects it. output, post_tool_call
and post_model_call permit: the tool already ran and the model already
answered, so refusing protects nothing and only breaks the caller.

One helper carries this now, so a fourth post-hoc point cannot pick up the
wrong behaviour by being written somewhere else. Each point warns once rather
than once per call.

Tests cover all three post-hoc points and pin that both pre-points still deny,
since the split is the whole design.

agent-os 2843, agt-policies 349, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: refuse an unapplied transform everywhere, and stop permitting unbound points

Two findings from re-reviewing this PR at its own head.

The unbound-point permit was wrong and is reverted. I had argued a post-hoc
point cannot prevent anything because the tool already ran, so refusing an
unconfigured one only breaks the caller. That is false. The SDK says it
plainly in AgentControlBlocked: "a block prevents the result from
propagating, it does not undo the side effect." A post_tool_call block stops
tool output reaching the model and an output block stops it reaching the
caller, so permitting those forwarded results no policy was consulted about,
on the exfiltration boundary. It also contradicted this stack's own Rust
guidance, which already says a manifest must bind every point the adapter
evaluates. The gemini and mistral scenario manifests now bind output, which
is what langchain and llamaindex already did and what made them pass.

Thirteen gates still read bare `allowed`, which is true for a transform.
They had nowhere to put the replacement, so they ran the original value while
the policy believed it was rewritten. They now gate on `permits_unchanged` and
refuse. maf keeps reading `allowed` because it applies the transform fifteen
lines further down; a first pass changed it and its tests caught the mistake.

to_policy_violation now says the integration cannot apply the transform
rather than reporting the policy's own reason, which described the policy
instead of the problem. post_execute no longer records completion for a
transform it is about to report as refused, which was charging the budget and
seeding the drift baseline from output the caller is told not to use. The
unused to_legacy_tuple is deleted; it returned bare `allowed` and carried the
same trap.

The gate test is anchored on class and method and asserts the whole set of
thirteen, so a fourteenth cannot be added by copying a naive one.

agent-os 2854, agt-policies 349, agent-sandbox 499, ratchet 1415, links 0 new
broken, no-stubs clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: refuse a transform in the MCP gateway and the trust root

A re-review at this PR's own head found two more consumers that dropped a
transform, both migrated to the native runtime by this PR and both outside
integrations/, which is why the earlier sweep missed them: it was scoped to a
directory rather than to the diff.

MCPGateway._evaluate answers with a bool and a reason, so it has nowhere to
put rewritten tool arguments; it permitted and forwarded the originals.
TrustRoot.validate_action returns a TrustDecision that carries no
replacement, so it downgraded a transform to a plain allow and charged the
call. This is the final authority, so that is the worst place for it. Both
now refuse, as the five sandbox providers already do.

The gate test is replaced. The old one listed the known sites by name, so it
could not catch a consumer written somewhere new, and it asserted on source
shape, which a no-op gate would satisfy. The new one is a census: it walks
agent_os, finds every function that evaluates a policy and branches on the
verdict, and requires each to either read transformed_value or gate on
permits_unchanged. Forwarders that hand the result back are excluded, since
they judge nothing. It reports 42 applying and 24 refusing, and it fails on
both new findings when their guards are removed, so it would have caught them.
Two behavioural tests drive the gateway and the trust root with a real
transform and assert they refuse rather than forward.

The unconfigured-point test also now asserts that a refused transform records
no completion, which it previously left open.

agent-os 2903, agt-policies 349, agent-sandbox 499, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: make the transform census see through delegation and past docstrings

The census had two holes, both found by re-review rather than by the census
itself.

It only looked at functions that both evaluated a policy and branched on the
verdict. Four consumers split those across two functions, so neither half was
classified and all four went unchecked: pre_execute with _tuple_for,
guardrails _validate with _apply_bridge_result, and agentshield's two
validate_* entries with _merge_bridge_verdict. All four are correct today, but
_tuple_for is the base-class transform contract that an earlier commit in this
PR had to fix, so leaving it uncovered by the test written to cover exactly
that class was not defensible. The census now follows one level of same-file
delegation and judges the pair together.

It also matched raw source, so a docstring counted. That was live, not
hypothetical: deleting _tuple_for's transform check left the suite green
because its docstring mentions transformed_value. Matching now runs on
executable code with docstrings stripped and comments gone, and the same
deletion fails the census.

guardrails now fails validation on a non-string transform instead of passing
the original through, since it validates strings and has nowhere to put a
payload of another shape.

I tried the same guard in agentshield and it was wrong. Its tool-call path
applies a dict replacement to params before merging, so a non-string payload
there has already been applied. A scenario test caught it; the merge keeps its
string check and now records why.

agent-os 2907, agt-policies 349, agent-sandbox 499, ratchet 1415, links 0 new
broken. Diff-wide consumer census: 42 apply, 24 refuse, 0 naive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: remove the pydantic-ai tool wrapper this PR orphaned

_wrap_single_tool was called from _wrap_tools_for on main. This PR removed
that wiring but kept the function, and its body calls
kernel.intercept_tool_call, which this PR also deleted. It would raise
AttributeError if anything reached it. Nothing does: it has no caller and is
not exported.

Found by re-reviewing this PR's own deletions for survivors, not by a test,
since dead code passes every suite.

agent-os 2907, agt-policies 349, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: a replacement of the wrong shape no longer falls through

Forty-three sites applied a transform only when the replacement matched the
shape the surface takes, and did nothing when it did not, so the original
value the policy meant to rewrite was forwarded. ACS lets a transform value be
any JSON value, so a manifest can return a dict for a string target and reach
this; it is the same silent drop as ignoring a verdict, one level down.

I had deferred this as pre-existing, since main carries 30 of these and this
PR only renamed the accessor. That was not a defensible reason. It is a
fail-open in a governance library, every one of the sites is in a file this PR
already rewrites, and no new file is added, so the 300-file display limit does
not come into it either.

Twenty-six sites already raised on a denial and now raise the same way when
the replacement will not fit. Sixteen with other refusal styles fold
applies_to into the deny check they already had, so an unusable replacement
takes that site's own path rather than a duplicated one. agentshield's
validate_tool_call folds it into the returned verdict.

Two sites needed individual treatment. autogen's on_send takes a dict or a
string and swallows a failed write, so applies_to could not express it; it
tracks whether the rewrite happened and drops the message if not.
agentshield's _merge_bridge_verdict is exempt with a comment: it runs after
validate_tool_call has already written a dict replacement, so refusing there
would reject a correct rewrite. A first attempt did exactly that and a
scenario test caught it.

A second census covers the class: every guarded application must have a path
for the shape it cannot take. Removing one fold fails it.

Still open and reported rather than changed: 25 sites swallow a failed rewrite
with `except Exception: pass`, so a type-correct replacement that cannot be
written also forwards the original. Unlike the shape mismatch that is not
demonstrably reachable, and whether a failed best-effort write should abort
the call is a behaviour decision across every adapter.

agent-os 2923, agt-policies 349, agent-sandbox 499, ratchet 1415, links 0 new
broken. Consumer census 42 apply, 24 refuse, 0 naive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: fold the unconfigured-point tests into the transform census file

Both modules cover the same thing: what an adapter must do with a verdict it
cannot act on. Keeping them apart pushed this PR to 301 files, one past the
point where GitHub stops rendering the diff, and the split bought nothing.

No coverage is lost. The unconfigured-point classes move across whole and
reuse the census file's stubs rather than carrying their own near-duplicates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: move the framework SDK stubs into conftest

They were a module three test files imported by hand. conftest is where
pytest already looks, so the separate file bought an import line and a file.

This does not change the file count, which I claimed in the first draft of
this message before checking: deleting the module drops one file but conftest
becomes modified, so the diff stays at 300. That is at GitHub's rendering
limit rather than past it. Keeping the change because it is the right home
for the stubs, not because it buys headroom.

agent-os 2923 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: catch the transform drops hiding behind a third condition

Two sites survived the previous pass and the census written to guard it,
both for the same reason: their guard ANDs a third condition, and the fixing
scripts and the census all keyed on a two-operand shape with the transform
check first.

maf's input middleware applied a redaction only when the replacement was a
string AND a message existed, so a dict replacement forwarded the original
text with call_next proceeding. langchain's wrap_tool_call applied only when
the replacement and the tool call were both dicts, so anything else ran the
original arguments. Both were reachable and neither denied.

The census now matches an AND of any arity where any operand is the transform
check and any is an isinstance call. Broadening it first made it fail on
exactly these two and nothing else, which is what says it would have caught
them.

Both use the applied-flag form rather than applies_to, because both wrap the
write in except Exception: pass. That closes the write-failure drop at these
two sites as well as the shape drop.

Verified after: 26 inserted guards still leave their apply body reachable, 16
applies_to folds still match their site's function, variable and type, census
42 apply / 24 refuse / 0 naive.

agent-os 2923, agt-policies 349, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: audit and surface a refused transform the way a denial is

Both refusal paths added in the previous commit were incomplete.

maf raised MiddlewareTermination and nothing else, while the deny path beside
it logs, sets a user-visible context.result and writes an audit record. A
block that leaves no record is not acceptable in a governance runtime, so the
refusal now does all three.

langchain logged a refused transform as "Policy DENY" and printed the
transform's own reason, so an operator would read "DENY: pii_redaction" for a
call that was refused because the replacement could not be written. The line
now distinguishes the two. The raised exception was already correct, since
to_policy_violation branches on transform.

A behavioural test drives the maf middleware with a dict replacement and
asserts it refuses, does not call the agent, leaves the message intact and
records the block. It fails on the pre-fix shape. It lives with the other maf
scenarios rather than in agent-os, where driving the middleware would have
needed a stub of everything around it.

agent-os 2923, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken. Census 42 apply, 24 refuse, 0 naive; 26 inserted guards reachable; 16
folds correctly placed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: a rewrite that fails to land is refused, not swallowed

The last open finding. Twenty-four sites applied a transform inside a try
whose handler only passed, so a write the framework object refused left the
original value in place and execution carried on while the policy believed it
had rewritten. The write targets objects the adapter does not own, and frozen
pydantic models and immutable message contents do raise on assignment, so
this was reachable rather than theoretical.

I had reported it rather than fixed it, on the grounds that it needed a
behaviour decision. The same reasoning was rejected for the shape mismatch
and it is no better here.

Eighteen sites whose function already raises to_policy_violation now raise it
from the write failure. maf raises MiddlewareTermination, matching its deny
path. crewai's before_tool and before_llm and autogen's on_send track whether
the rewrite landed and take the refusal each already had, because their
targets are searched for in a loop or written through two different shapes,
so a raise would be in the wrong place.

crewai's before_llm also refuses when no message takes the rewrite at all,
which had the same effect as a failed write.

A third census fails on any transform rewrite whose handler only passes,
unless the function tracks whether it landed. It reads both spellings, since
some sites hold the replacement in a local first. Reintroducing a swallow in
gemini fails it.

agent-os 2921, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken. Census 42 apply, 24 refuse, 0 naive; 26 inserted guards reachable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: a write target that takes nothing is refused, not passed over

An eighth finding, and a fourth way the same value gets dropped. The three
guards so far cover the verdict, the replacement's shape, and a write that
raises. This one is a write that is never attempted: a guard on what is being
written TO fails, no exception is raised, and the original value goes out.

Two were demonstrated end to end. autogen's _apply_content buried the swallow
in a helper that returned None, so on_send's rewritten flag could never go
false and on_publish had no check at all; a message whose content rejects
assignment forwarded the original with allowed=True. langchain's
wrap_tool_call wrote only when the result had .content, and a tool returning a
plain string has none, which the same function's own str(getattr(result,
"content", result)) shows is expected.

_apply_content now returns whether the write landed and both callers read it.
The rest refuse on the branch that cannot take the replacement: langchain's
model input loop, which could complete without writing anything, its response
path, anthropic's and mistral's input loops on a non-dict message, mistral's
tool call with no function to write to, google_adk's non-dict tool args, and
smolagents' tool call carrying neither argument attribute.

A fourth census covers the shape: an application guarded on the target with no
refusal, no flag and no else. It found a ninth site in mistral that was not in
the report, and reintroducing the anthropic guard fails it.

agent-os 2921, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken. Census 42 apply, 24 refuse, 0 naive; 26 inserted guards reachable; 20
raise and 5 flag-guarded rewrites, 0 swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: redact and continue where the value is the return, not an attribute

The previous commit over-corrected langchain. A tool returning a plain string
has no .content to write to, and I made that a hard denial, which turns a
redaction policy into a block for the most ordinary kind of tool. llamaindex
and semantic_kernel already handle the same case by returning the replacement
itself, which is right: the value IS the return, so handing back the redacted
one applies the policy rather than refusing it. Both langchain sites now do
the same. The comment I left there was also wrong; returning transformed_value
hands back the redaction, not the original.

Census 4 flagged the corrected form, since it only recognised a raise, an
else or a landed-flag. Returning the replacement is a third correct handling
and it now counts. Reintroducing the anthropic drop still fails the census, so
it did not go slack.

agent-os 2921, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken, ratchet self-test and tests/ci 108, no-stubs clean. Census 42 apply,
24 refuse, 0 naive; 26 guards reachable; 20 raise and 5 flag-guarded, 0
swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: make the verdict stubs read the same at every layer of the stack

The stubs modelled the pre-ACS evaluation, which reads verdict, transform and
reason off the evaluation itself. The ACS runtime reads them off a Verdict
object instead, so the same tests failed at the last PR in the stack with
'str' object has no attribute 'transform', and the session there routes every
intervention point through one evaluate() rather than a method per point.

The stubs now carry both shapes: a verdict that is still a string but also
answers .decision, .reason and .transform, and a session that accepts either
spelling. One version of the file passes at both ends of the stack, so the
tests do not have to be ported layer by layer as the runtime changes
underneath them.

agent-os 2921 at this layer and at the tip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: spell-check on the words this PR introduces

cspell runs on changed lines and flagged five. Three were mine to reword or
rename: a comment said "unrewritten", and two locals in the census helper were
funcname and fbody, now func_name and enclosing_src.

conftest and orelse go in the dictionary instead. The first is pytest's own
filename, which this PR imports from now that the framework stubs live there;
the second is the attribute name on ast.If, which the census reads to tell an
unguarded branch from one that refuses.

agent-os 2921, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: refuse a transform langchain cannot write, instead of returning a bare string

The previous commit had these two sites return post_result.transformed_value
when the target had no .content, on the grounds that llamaindex and
semantic_kernel do the same. That analogy was false and the change was wrong.

LangChain types wrap_tool_call as returning ToolMessage | Command and
wrap_model_call as ModelResponse | AIMessage | ExtendedModelResponse. A str is
none of those. It also breaks the premise I wrote in the comment: a tool that
returns a plain string is wrapped as a ToolMessage before middleware sees it,
and ToolMessage has .content, so what actually arrives without one is a
Command. Returning a str there makes ToolNode fold it into messages as a
HumanMessage, losing the command's updates and its tool_call_id; on the model
path the caller then reads .result off a str and raises AttributeError. The
siblings differ because their return type is Any by contract.

Both sites refuse again, which is protocol-safe, fail-closed, and the same
answer the sandbox providers, the MCP gateway and the trust root already give
when they cannot apply a replacement.

Refusing also removes a budget bug the early return had introduced: it skipped
self._ctx.call_count += 1, so a redacted tool call was never charged and
max_tool_calls undercounted.

A scenario test drives wrap_tool_call with a Command and a transform verdict
and asserts it refuses. It fails on the previous revision, which returned str.

Census 4 now reads the If node's own else branch rather than searching the
unparsed source for "else:", which also matched an else on a nested for, try
or inner if and could mark a dropped transform as handled.

agent-os 2921, agt-policies 351, ratchet 1415, censuses 42 apply / 24 refuse /
0 naive, 20 raise and 5 flag-guarded rewrites, 0 swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: close two holes in the target-guard census

Both found by re-reviewing the census itself rather than the code it guards.

A raise inside the guarded branch's own except handler was counted as
refusing. That raise fires when the write THROWS, which is the class census 3
covers; it says nothing about the branch never being entered. Counting it
exempted the repo's most common shape, a guarded write inside a try, which is
the likeliest way a skipped write gets back in. Only a raise reachable when
the guard is false counts now. Restoring main's shape at langchain's tool-post
site is caught, where before it passed.

The fallback clause stays scoped to the whole block, deliberately, and now
says why. llamaindex and semantic_kernel chain several target guards and end
the block with `return <result>.transformed_value`, so a guard falling through
reaches that fallback rather than carrying on with the original. Scoping it to
the branch flagged all four as drops. The two fixes interact and only one of
them is right.

agent-os 2921, agt-policies 351, agent-sandbox 499, ratchet 1415, links 0 new
broken, censuses 42 apply / 24 refuse / 0 naive, 26 guards reachable, 20 raise
and 5 flag-guarded rewrites, 0 swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(integrations): stop transform verdicts from proceeding unapplied

A transform verdict permits, so code that gates on is_allowed alone lets the
run continue with the original payload while the policy believed it had been
rewritten. A redaction policy silently does not redact. Neither surface here
can apply a replacement: the lifecycle hooks observe a run, and a guardrail can
only trip or not.

Both now refuse a transform rather than proceeding. The guardrail fix covers
its input and output sites, not just the one flagged, since the two shared the
defect.

Also in this package, the input guardrail passed a list of input items straight
to evaluate_input, which declares body as str or dict. The list branch now
encodes to a dict.

Two packaging faults that would ship:

  adk_agentmesh/__init__.py is a deprecation shim with no re-exports, so every
  import in its own README raised ImportError. The four documented names are
  re-exported, and each one was imported to confirm it.

  agent-governance-toolkit-integrations declared the console script
  openshell-agentmesh = openshell_agentmesh.cli:main while this stack deletes
  cli.py, so the wheel would install a command that crashes on first run. The
  entry is removed; only a deprecation shim remains in that package.

Tests cover every verdict on both surfaces. They skip where the openai-agents
SDK is missing, as the package's existing tests already do, so the behaviour
was additionally verified against stubbed SDK modules: allow and warn proceed,
deny, escalate and transform refuse, and the guardrail body encodes to a dict.

Ratchet 1180. Links: 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor(v4-removal)!: move the examples onto ACS v5 manifests (#3452)

* refactor(v4-removal)!: move the examples onto ACS v5 manifests

Example policies and bundles move to ACS manifests, and the demos that existed
only to show v4 syntax are deleted with their fixtures. The package-local
examples, docs, tutorials, benchmarks and notebooks under agent-os follow the
same move.

docs/compliance/nist-rfi-2026-00206.md moves here too, because it links to the
native A2A runtime example this PR adds and the link would dangle if the two
landed separately.

Also carries two review fixes:

  sync_atr_rules interpolated third-party ATR patterns into a Rego backtick
  raw-string literal. Raw strings have no escape mechanism, so a pattern
  carrying a backtick closed the literal and the rest parsed as policy source.
  The weekly sync compiles that corpus from npm unattended, so a poisoned
  upstream rule could flip a verdict on benign input. Patterns are now emitted
  as JSON-quoted strings and the validator rejects backticks. atr-import shares
  the emission path through compile_patterns.

  The demos README documented AgtRuntimeMiddleware, which is
  RuntimeGovernanceMiddleware, and advertised real LLM calls with Ollama setup
  instructions. The example it points at makes no model call, so that section
  described a mode that does not exist.

Ratchet: 656. Links: 0 new broken. atr tests: 5 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs(rust): correct the dependency audit and document the unbound-point deny (#3445)

The audit claimed nothing was pulled from crates.io that was not already in the
graph. Cargo.lock gains 66 entries: 2 first-party and 64 third-party crates
that were not in the workspace before. It now lists them and names the two
clusters.

The second cluster is the one that matters: ureq, rustls, ring and
webpki-roots link an HTTP and TLS stack into agentmesh. They arrive because the
ACS Rust SDK pins its core features to include the remote annotators and
because Manifest::from_url exists, and a consumer cannot opt out since the
feature set is fixed inside the SDK crate. Nothing in agentmesh calls a remote
annotator or from_url, so no egress is added in practice, but for a governance
library the linked capability belongs in the security record. Narrowing the
SDK's features is a follow-up on that crate.

MIGRATION_V5.md also now documents that the runtime denies an intervention
point the manifest does not declare. Failing closed is right, but the adapter
evaluates input on every execute() and pre_tool_call on every tool call, so a
manifest binding only one denies the other path with no obvious cause.

Links: 0 new broken.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

* docs(v4-removal): rewrite the documentation for ACS v5 (#3446)

* docs: use the real class names in the integration guides

A mechanical rename left several component names that resolve to nothing:

  AgtRuntimeMiddleware and AgtRuntimeMW are RuntimeGovernanceMiddleware, and
  CapabilityGuardMW, AuditTrailMW and RogueDetectionMW are the corresponding
  *Middleware classes in maf_adapter.

  AgtRuntimeChecker never existed in any SDK. The Haystack integration ships
  TrustGate and AuditLogger, which is what the tables now list.

  PolicyEvaluation.* in the .NET tutorial was the rename applied where it does
  not belong: the .NET engine still defines PolicyAction and no PR in this
  stack touches dotnet source. Reverted, and the file is allowlisted in the
  ratchet so documenting that engine's own enum does not read as v4 usage.

Ratchet: 94, unchanged. Links: 0 new broken. Ratchet tests: 35 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor(v4-removal)!: confine the v4 policy language to the migration tool (#3447)

* docs: scope the breaking-changes record to what each PR removes

The Python entry listed the whole stack's removals in one place, so it
overlapped the record the first PR now carries and named 33 symbols twice.
It also claimed agent_os.kernel keeps a PolicyRule; the surviving one is
re-exported from agent_control_plane.

What is left here is what could only go once the rule model went: manifest
resolution, the evaluators and decision types that read the rule model, the
framework integrations' local policy surfaces, and the unreleased cedarling
backend. Names that survive under the same spelling in an unrelated module
are called out, since agent_os still exports a PolicyDecision and a
PolicyRule that have nothing to do with the v4 language.

The langflow README documented GovernanceComponent, which this stack deletes.
Its section, the YAML policy that only configured it, its table row, and its
node in the flow diagram are gone, matching what the haystack and openshell
READMEs already do.

ratchet 0, links 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor!: delete agt.policies and evaluate through ACS directly (#3449)

* fix(acs): drop the dead manifest-timeout branch and correct the docs it claimed

_resolve_approval_timeout read approval.timeout_seconds off
getattr(control, "manifest", None). AgentControl never stores that attribute,
so the branch was dead in production and only ran in tests because the stub
defined one. BREAKING_CHANGES then documented behaviour the code could not
deliver.

The core treats the approval section as opaque host configuration
(SPECIFICATION §17.1) and does not surface it across the native boundary, so
honouring it needs an SDK accessor that does not exist. The branch is removed,
the docstring and BREAKING_CHANGES say a caller must pass the value, and the
300 second default that closes the original fail-open stays.

The tests that covered the branch went with it. A replacement drives a resolver
that never returns through a real HostSession and asserts it denies in about a
second, which the removed tests could not have caught.

Also repairs what earlier mechanical renames left behind:

  AgentControlMiddleware and AgentControlMW in the Python and MAF docs are
  RuntimeGovernanceMiddleware. The .NET class of that name is real but belongs
  to the ACS dotnet SDK, so the name resolved while meaning the wrong thing.

  AgentControlChecker and HaystackAdapter exist nowhere; the Haystack package
  ships TrustGate and AuditLogger. NoOpAgentControl and the compat.py path
  beside it were both deleted by this stack.

  Ten `the ACS manifest` substitutions where a symbol name used to be, across
  AGENTS.md, three compliance docs and openclaw-sidecar.md.

  Fourteen compliance citations pointing at agt/policies and agent_os/policies
  files this stack deletes, repointed at the ACS SDK.

  The agt-policies README still described the deleted wrapper, including two
  submodules that never existed and a snippet calling AgentControl.evaluate.
  Rewritten for what the package now is, a migration tool, and every command
  and symbol in it was run.

ACS host: 25 passed. agent-os: 2683 passed. Ratchet 0. Links: 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs: cite the live Haystack integration, not the deprecated package

The framework tables in docs/FAQ.md and agent-mesh/README.md pointed at the
deprecated haystack_agentmesh package and its TrustGate/AuditLogger
components. The maintained module is agentmesh.integrations.haystack, whose
exports are TrustGateComponent, TrustAgentComponent, TrustedPipeline, and
PipelineAuditEntry.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* test: delete the dead __init_result__ stub from _RecordingControl

The method is not a dunder Python calls, nothing in the suite invokes it,
and its body only raises NotImplementedError. Verified by AST that
_RecordingControl keeps __init__ and evaluate_intervention_point; the PyO3
_native extension is not built in this environment, so the module was
checked with py_compile rather than by running pytest.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs: describe the post-removal world with AgentControl, not AgtRuntime

Two sentences in BREAKING_CHANGES.md used AgtRuntime for what code takes
after the removal, contradicting the file's own removed-symbols table,
which maps AgtRuntime(path) to AgentControl.from_path. The mentions that
remain describe the removed v4 surface and stay as they are.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(openai-agents-trust): note the hooks path carries no tool arguments

The openai-agents lifecycle hook signature (on_tool_start/on_tool_end) does
not include the tool-call arguments, so GovernanceHooks evaluates
pre_tool_call and post_tool_call with args={}. Policy rules conditioned on
tool arguments therefore never match on this path. Say so at both call
sites and in the README, pointing at the guardrail integration for
arg-level enforcement.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* fix(openshell): stop pointing the deprecation at a package that ships this shim

The DeprecationWarning and README told users to install
agent-governance-toolkit-integrations[openshell], but that extra
force-includes this same package, whose skill (GovernanceSkill,
ShellPolicyViolation, governed_shell) this stack removes - the pointer was
circular. Say what is true: the skill is removed in the v5 ACS migration
with no OpenShell-specific replacement; build an AgentControl from an ACS
manifest and evaluate intervention points in the host. Packaging and
force-includes are unchanged.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(examples): remove the openshell-governed demo

The v4-removal stack deletes openshell_agentmesh.skill (GovernanceSkill),
which was this demo's only entry point. The example could no longer run;
remove it with the rest of the retired openshell surface.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs: retire the openshell integration page

The v4-removal stack deletes the openshell governance skill this page
documents (its quickstart imports the removed GovernanceSkill). Remove
the page and the openclaw-sidecar cross-link along with the example
removed in the previous commit.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

* fix(ratchet): restore enhanced scanner, dedupe BREAKING_CHANGES entry, add _migrate_resolution prefix, update baseline post-merge

Signed-off-by: GitHub <noreply@github.com>

* fix: restore what the conflict resolution dropped, and address review

The automatic resolution of the merge with main deleted 158 files that both
main and this branch still had. They were not deliberate removals: examples,
benchmarks, docs, and the ``agt.policies`` modules ``manifest``, ``session``
and ``_re2``, whose absence broke every agent-os test that loads a manifest.
All are restored from the pre-merge branch tip. The resolver had also taken
the downstream package's ``agt/__init__.py``, which assumes ``agt.policies``
is gone; the version here exports both surfaces and resolves ``cli`` lazily,
because importing the migrator eagerly pulls in the native SDK and made
``from agt.policies import AdapterRuntimeSession`` fail on a host without it.

Three moves the resolver got half-right are completed rather than reverted:
the resolution layer now lives only under ``agt.cli._migrate_resolution``, so
its last two importers point there, and the scenario harness that reached into
it from outside the CLI is gone along with its only consumer, which is what
the migrator-boundary test asks for. The ratchet baseline was recomputed on
the corrected tree; it had been written against the tree missing those files.

Review feedback: the ADK README's quickstart named a class the package does
not export, so it raised ImportError on the first line a reader runs; it names
``ADKPolicyEvaluator`` now and the snippet was executed. The lifecycle hooks
in openai-agents-trust called ``audit_record()`` on a result type that has no
such method, and handed the audit log a verdict object where it declares a
string and then hashes it, so every governed tool event raised before it could
decide anything; both writers now share one JSON-safe record builder.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: rebuild the merge on the stack's end state and green the CI gates

The automatic resolution merged #3519 — a fix branched from main — into the
stack and took main's side wherever they overlapped. That resurrected
``agt.policies``, which this stack deletes, leaving a tree that was neither
state: the wheel gate failed because the migration wheel shipped a second
policy layer, and the resolver package was half-present.

The tree is rebuilt as the stack's end state plus main's independent changes,
which is what the merge was supposed to produce. #3519's behavioural fix is
re-applied on top rather than dropped: the four adapter sites that evaluated
the output intervention point and discarded the verdict now raise on a deny,
so a blocked response is not disclosed, and the anthropic scenario manifest
binds the output point those tests now exercise.

The remaining gate failures were consequences of the same move. The crypto
allowlist still named ``manifest_resolution/build.py`` after the stack moved
it under the private migrator, so its long-standing SHA-256 exemption stopped
matching. Two integration packages lost their license headers. The spell
checker met crate names from the Rust dependency audit and two British
spellings. Two gitleaks findings are documentation placeholders, fingerprinted
the way this repository already handles that file.

Review feedback: the ADK README named a class the package does not export, so
its quickstart raised ImportError on the first line; it names
``ADKPolicyEvaluator`` now and the snippet was executed. The openai-agents
lifecycle hooks and guardrails called ``audit_record()`` on a result type that
has no such method and handed the audit log a verdict object where it declares
a string and then hashes it, so every governed event raised before deciding;
both writers share one JSON-safe record builder.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(ci): repair dependency pins, ACS SDK installs, and stale doc links

Fixes the CI failures left by the automatic merge-conflict resolution on
this branch. Six distinct root causes, not one:

- agent-governance-toolkit-core kept the stack's stale
  `agentrust-trace>=0.2.0,<0.3.0` instead of main's dependabot bump to
  `>=0.5.1,<0.6.0`, making agent-mesh and docker-compose-test unresolvable
  (ResolutionImpossible) and breaking the TRACE v0.2 sinks.
- agent-compliance, agent-marketplace and agent-sandbox now import
  `agent_control_specification` at module scope, but the CI gate that
  builds the native ACS SDK only covered agent-os and agt-policies.
- The Policy Validation workflow calls the ACS-backed manifest linter
  without ever building the ACS SDK.
- test-integrations resolved the published ACS 0.3.1b0 wheel, which
  predates `HostSession`; build the vendored SDK first so the resolver
  sees the requirement already satisfied by this commit's source.
- test_hooks_verdicts asserted a `HostSession.evaluate_input(body=...)`
  API that does not exist. The real intervention point is
  `HostSession.input(body: JsonValue)`, which accepts a list, so the test
  now covers what actually matters: every non-dict item is stringified so
  the body stays JSON-serializable.
- Three i18n READMEs still linked `docs/integrations/openshell.md`, which
  the stack deletes, and the dcbadge ignore pattern pinned the previous
  Discord invite code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(merge): keep the post-base Discord invite fix and Rust dependency pins

Two main-side deltas from after the merge-base were reverted by the
stack-end-state rebuild and must survive the merge:

- #3481: README.md and README.ko.md returned to the expired 7aVPCcVh
  Discord invite; restore the non-expiring TxMRqY3pFr link
- Dependabot: re-lock the base64 0.23.0, clap 4.6.4, regorus 0.11.0,
  and cedar-policy 4.12.0 pins against the branch workspace
  (cargo update --precise per crate)

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* test(agent-os): re-add the #3519 output-verdict regressions and census

The rebuild dropped the regression tests that pin the anthropic and
autogen output-verdict enforcement (the adapter-side fix itself is in
the tree) and the discarded-verdict census in
test_transform_never_dropped.py. Re-add them with the per-point stub
adapted to the ACS evaluate_intervention_point protocol; all pass
against the branch adapters.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Dane Parin (SemTiOne) pushed a commit to SemTiOne/agent-governance-toolkit that referenced this pull request Jul 30, 2026
…ntmesh integrations (microsoft#3451)

* refactor(v4-removal)!: replace the v4 policy language with ACS v5 across the Python runtime

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(agent-os): remove dead branches and duplicate assignments in the adapters

Review findings on the native adapter rewrite, all on paths the rewrite
carried across rather than introduced:

  openai_adapter.health_check branched on has_clients to return "healthy"
  either way. Collapsed to match the sibling adapters.

  openai_adapter tool mediation had an "escalate" branch after the
  not-allowed guard. escalate is not a permitting verdict, here or on main,
  so the guard always raised first and the branch never ran. The docstring
  already documents the surviving behaviour: an escalate the resolver
  refuses surfaces as a deny.

  agentshield_adapter called _get_or_create_context() twice in a row at
  three sites.

  ToolCallInterceptor.intercept used a bare "..." body; the sibling
  AdapterRuntime protocol in this package uses a docstring.

  test_policy_replay_metadata imported agt.policies both ways. It now uses
  the module form only, which is the one patch.dict(vars(policies)) needs.

The maf_adapter PolicyViolationError re-export stays. It looks unused, but
test_adapter_exception_identity asserts every adapter module resolves the
canonical error type, so a comment now records why it is there.

agent-os 2683 passed, agent-compliance 457 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* ci: fail policy validation on a manifest that omits its ACS version

The manifest scan keyed only on the agent_control_specification_version
substring, so a manifest that omits that field was not linted at all. For a
policy gate, skipping silently is the wrong direction: the file most likely to
be malformed is the one missing its version.

Files declaring intervention_points without a version are now reported.
intervention_points is the ACS-specific tell; keying on policies or agents
instead flags 22 unrelated config files in this repo that happen to use those
names, which is why the check is narrow.

Verified against the tree at every layer of this stack: no new findings, and a
probe manifest with intervention_points and no version is caught.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* ci: let BREAKING_CHANGES.md name the v4 symbols it removes

The ratchet counts any mention of a v4 name as a use of it. A breaking-changes
record has to name each removed symbol so a reader can find its replacement,
so the file is allowlisted. Naming a symbol in order to say it is gone is not
a use of it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: stop treating a transform verdict as plain permission

A transform verdict permits the call, but it permits a rewritten one: the
verdict carries the replacement the caller is expected to apply. Several
gates read only whether the verdict permits, so they ran the original value
while the policy believed it had been rewritten. A policy that redacts a
credential did not redact it, and nothing reported a failure.

Three groups of sites, three different resolutions:

The five sandbox providers now refuse a transform outright at the
pre_tool_call gate. That gate gets code it is about to execute and cannot
rewrite it, so refusing is the only honest answer. This gate is new in this
change, so the gap came in with it rather than predating it.

The four adapters that called post_execute and dropped the return value now
raise PolicyViolationError. main evaluated no output policy at all here, so
these call sites only became load-bearing once this change added it.

base.py returns (False, "transform_not_applicable") from the tuple helpers
instead of silently discarding the replacement, and NativeAdapterResult grows
permits_unchanged for callers that cannot apply one. allowed stays as it was;
a site that can apply a transform should keep using it.

Tests drive the real execute_code gate on all five providers with a real
PolicyEvaluation. The five transform cases fail without the guards and the
deny and allow cases pass either way, so they discriminate.

agent-sandbox 499 passed, agent-os 2827 passed, ratchet 1415, lint clean on
every changed file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs: record this PR's removed exports where the removal happens

The stack documented every Python removal in the last PR, six merges after
the removals land. Anyone on main between the two saw 75 public names
disappear with nothing to read.

The list is computed from the export diff against the merge base rather than
assembled by hand. That matters: the hand-written version I started from
claimed 51 names, and of those 31 were still exported at this point in the
stack because their removal happens in a later PR. The computed set is 75,
and every one of them is gone here.

The budget-counting change is recorded with them, since a deployed policy
now permits one more tool call than it used to.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: an unbound output point is not a denial

Making the adapters honour the output verdict broke every manifest that does
not bind an output intervention point. The engine reports an unconfigured
point as runtime_error:intervention_point_unknown, which is the right answer
to a request naming an unknown point. The adapter is not answering a request
though. It evaluates output after every call whether or not the host asked
for output governance, so reading that error as a denial blocked every
response under a manifest binding only input or only tool calls. Seven
adapter scenarios caught it, and the previous code hid it by discarding the
verdict.

post_execute now treats that one reason as "no output policy configured",
permits, and records completion. Completion mattered: it was already gated on
allowed, so budgets drifted on those manifests before this change too. Every
other reason still denies, including the other runtime errors.

Tests pin both directions, since the risk in relaxing this is that output
enforcement quietly stops working. Only the unconfigured case fails without
the fix; the six covering deny, transform, allow, and the other runtime
errors pass either way.

agt-policies 350, agent-os 2834, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: apply the unbound-point permit at the runtime, not the base class

The previous commit put the carve-out in BaseIntegration.post_execute. Only
six adapters reach output evaluation that way. Roughly eleven others call
evaluate_output directly (langchain, semantic_kernel, smolagents,
pydantic_ai, agentshield, google_adk, guardrails, crewai, llamaindex, openai,
openai_agents_sdk) and still hard-failed, so one manifest behaved two ways
inside one package. Reported by Haroon on #3444.

The permit now lives on NativeAdapterResult behind permit_if_unconfigured,
which NativeAdapterRuntime.evaluate_output sets. Every caller goes through
that method, so there is one place to reason about instead of 26 call sites.
point_not_configured is exposed so callers can tell an unconfigured point
from a denial. base.py drops its copy and reads allowed like everything else.

The flag is set for output only. input and pre_tool_call stay fail-closed:
omitting them omits governance of an action about to happen, and the fix
leans on pre_execute staying strict, so a test pins that.

The skip warns once per runtime rather than staying silent, since an operator
otherwise cannot see that output enforcement is off.

BREAKING_CHANGES records the split, including that the bridge used to rewrite
any unconfigured point to an allow.

Tests build the real NativeAdapterResult rather than a stand-in, because a
stand-in for the class carrying the permit would test the stand-in. Removing
the flag from the chokepoint fails exactly the direct-adapter test and no
other, so the evidence discriminates.

agent-os 2839, agt-policies 349, agent-sandbox 499, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: permit every unconfigured post-hoc point, not just output

Fixing output alone left the same bug at post_tool_call. Thirteen scenario
manifests bind pre_tool_call and none bind post_tool_call, so
openai_agents_sdk.py:521 raised on the configuration every scenario uses.
Nothing caught it because that adapter has no scenario test.

The line is not output versus the rest, it is whether the point gates an
action or runs after one. input, pre_tool_call and pre_model_call still deny:
the action has not happened, so refusing protects it. output, post_tool_call
and post_model_call permit: the tool already ran and the model already
answered, so refusing protects nothing and only breaks the caller.

One helper carries this now, so a fourth post-hoc point cannot pick up the
wrong behaviour by being written somewhere else. Each point warns once rather
than once per call.

Tests cover all three post-hoc points and pin that both pre-points still deny,
since the split is the whole design.

agent-os 2843, agt-policies 349, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: refuse an unapplied transform everywhere, and stop permitting unbound points

Two findings from re-reviewing this PR at its own head.

The unbound-point permit was wrong and is reverted. I had argued a post-hoc
point cannot prevent anything because the tool already ran, so refusing an
unconfigured one only breaks the caller. That is false. The SDK says it
plainly in AgentControlBlocked: "a block prevents the result from
propagating, it does not undo the side effect." A post_tool_call block stops
tool output reaching the model and an output block stops it reaching the
caller, so permitting those forwarded results no policy was consulted about,
on the exfiltration boundary. It also contradicted this stack's own Rust
guidance, which already says a manifest must bind every point the adapter
evaluates. The gemini and mistral scenario manifests now bind output, which
is what langchain and llamaindex already did and what made them pass.

Thirteen gates still read bare `allowed`, which is true for a transform.
They had nowhere to put the replacement, so they ran the original value while
the policy believed it was rewritten. They now gate on `permits_unchanged` and
refuse. maf keeps reading `allowed` because it applies the transform fifteen
lines further down; a first pass changed it and its tests caught the mistake.

to_policy_violation now says the integration cannot apply the transform
rather than reporting the policy's own reason, which described the policy
instead of the problem. post_execute no longer records completion for a
transform it is about to report as refused, which was charging the budget and
seeding the drift baseline from output the caller is told not to use. The
unused to_legacy_tuple is deleted; it returned bare `allowed` and carried the
same trap.

The gate test is anchored on class and method and asserts the whole set of
thirteen, so a fourteenth cannot be added by copying a naive one.

agent-os 2854, agt-policies 349, agent-sandbox 499, ratchet 1415, links 0 new
broken, no-stubs clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: refuse a transform in the MCP gateway and the trust root

A re-review at this PR's own head found two more consumers that dropped a
transform, both migrated to the native runtime by this PR and both outside
integrations/, which is why the earlier sweep missed them: it was scoped to a
directory rather than to the diff.

MCPGateway._evaluate answers with a bool and a reason, so it has nowhere to
put rewritten tool arguments; it permitted and forwarded the originals.
TrustRoot.validate_action returns a TrustDecision that carries no
replacement, so it downgraded a transform to a plain allow and charged the
call. This is the final authority, so that is the worst place for it. Both
now refuse, as the five sandbox providers already do.

The gate test is replaced. The old one listed the known sites by name, so it
could not catch a consumer written somewhere new, and it asserted on source
shape, which a no-op gate would satisfy. The new one is a census: it walks
agent_os, finds every function that evaluates a policy and branches on the
verdict, and requires each to either read transformed_value or gate on
permits_unchanged. Forwarders that hand the result back are excluded, since
they judge nothing. It reports 42 applying and 24 refusing, and it fails on
both new findings when their guards are removed, so it would have caught them.
Two behavioural tests drive the gateway and the trust root with a real
transform and assert they refuse rather than forward.

The unconfigured-point test also now asserts that a refused transform records
no completion, which it previously left open.

agent-os 2903, agt-policies 349, agent-sandbox 499, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: make the transform census see through delegation and past docstrings

The census had two holes, both found by re-review rather than by the census
itself.

It only looked at functions that both evaluated a policy and branched on the
verdict. Four consumers split those across two functions, so neither half was
classified and all four went unchecked: pre_execute with _tuple_for,
guardrails _validate with _apply_bridge_result, and agentshield's two
validate_* entries with _merge_bridge_verdict. All four are correct today, but
_tuple_for is the base-class transform contract that an earlier commit in this
PR had to fix, so leaving it uncovered by the test written to cover exactly
that class was not defensible. The census now follows one level of same-file
delegation and judges the pair together.

It also matched raw source, so a docstring counted. That was live, not
hypothetical: deleting _tuple_for's transform check left the suite green
because its docstring mentions transformed_value. Matching now runs on
executable code with docstrings stripped and comments gone, and the same
deletion fails the census.

guardrails now fails validation on a non-string transform instead of passing
the original through, since it validates strings and has nowhere to put a
payload of another shape.

I tried the same guard in agentshield and it was wrong. Its tool-call path
applies a dict replacement to params before merging, so a non-string payload
there has already been applied. A scenario test caught it; the merge keeps its
string check and now records why.

agent-os 2907, agt-policies 349, agent-sandbox 499, ratchet 1415, links 0 new
broken. Diff-wide consumer census: 42 apply, 24 refuse, 0 naive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: remove the pydantic-ai tool wrapper this PR orphaned

_wrap_single_tool was called from _wrap_tools_for on main. This PR removed
that wiring but kept the function, and its body calls
kernel.intercept_tool_call, which this PR also deleted. It would raise
AttributeError if anything reached it. Nothing does: it has no caller and is
not exported.

Found by re-reviewing this PR's own deletions for survivors, not by a test,
since dead code passes every suite.

agent-os 2907, agt-policies 349, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: a replacement of the wrong shape no longer falls through

Forty-three sites applied a transform only when the replacement matched the
shape the surface takes, and did nothing when it did not, so the original
value the policy meant to rewrite was forwarded. ACS lets a transform value be
any JSON value, so a manifest can return a dict for a string target and reach
this; it is the same silent drop as ignoring a verdict, one level down.

I had deferred this as pre-existing, since main carries 30 of these and this
PR only renamed the accessor. That was not a defensible reason. It is a
fail-open in a governance library, every one of the sites is in a file this PR
already rewrites, and no new file is added, so the 300-file display limit does
not come into it either.

Twenty-six sites already raised on a denial and now raise the same way when
the replacement will not fit. Sixteen with other refusal styles fold
applies_to into the deny check they already had, so an unusable replacement
takes that site's own path rather than a duplicated one. agentshield's
validate_tool_call folds it into the returned verdict.

Two sites needed individual treatment. autogen's on_send takes a dict or a
string and swallows a failed write, so applies_to could not express it; it
tracks whether the rewrite happened and drops the message if not.
agentshield's _merge_bridge_verdict is exempt with a comment: it runs after
validate_tool_call has already written a dict replacement, so refusing there
would reject a correct rewrite. A first attempt did exactly that and a
scenario test caught it.

A second census covers the class: every guarded application must have a path
for the shape it cannot take. Removing one fold fails it.

Still open and reported rather than changed: 25 sites swallow a failed rewrite
with `except Exception: pass`, so a type-correct replacement that cannot be
written also forwards the original. Unlike the shape mismatch that is not
demonstrably reachable, and whether a failed best-effort write should abort
the call is a behaviour decision across every adapter.

agent-os 2923, agt-policies 349, agent-sandbox 499, ratchet 1415, links 0 new
broken. Consumer census 42 apply, 24 refuse, 0 naive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: fold the unconfigured-point tests into the transform census file

Both modules cover the same thing: what an adapter must do with a verdict it
cannot act on. Keeping them apart pushed this PR to 301 files, one past the
point where GitHub stops rendering the diff, and the split bought nothing.

No coverage is lost. The unconfigured-point classes move across whole and
reuse the census file's stubs rather than carrying their own near-duplicates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: move the framework SDK stubs into conftest

They were a module three test files imported by hand. conftest is where
pytest already looks, so the separate file bought an import line and a file.

This does not change the file count, which I claimed in the first draft of
this message before checking: deleting the module drops one file but conftest
becomes modified, so the diff stays at 300. That is at GitHub's rendering
limit rather than past it. Keeping the change because it is the right home
for the stubs, not because it buys headroom.

agent-os 2923 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: catch the transform drops hiding behind a third condition

Two sites survived the previous pass and the census written to guard it,
both for the same reason: their guard ANDs a third condition, and the fixing
scripts and the census all keyed on a two-operand shape with the transform
check first.

maf's input middleware applied a redaction only when the replacement was a
string AND a message existed, so a dict replacement forwarded the original
text with call_next proceeding. langchain's wrap_tool_call applied only when
the replacement and the tool call were both dicts, so anything else ran the
original arguments. Both were reachable and neither denied.

The census now matches an AND of any arity where any operand is the transform
check and any is an isinstance call. Broadening it first made it fail on
exactly these two and nothing else, which is what says it would have caught
them.

Both use the applied-flag form rather than applies_to, because both wrap the
write in except Exception: pass. That closes the write-failure drop at these
two sites as well as the shape drop.

Verified after: 26 inserted guards still leave their apply body reachable, 16
applies_to folds still match their site's function, variable and type, census
42 apply / 24 refuse / 0 naive.

agent-os 2923, agt-policies 349, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: audit and surface a refused transform the way a denial is

Both refusal paths added in the previous commit were incomplete.

maf raised MiddlewareTermination and nothing else, while the deny path beside
it logs, sets a user-visible context.result and writes an audit record. A
block that leaves no record is not acceptable in a governance runtime, so the
refusal now does all three.

langchain logged a refused transform as "Policy DENY" and printed the
transform's own reason, so an operator would read "DENY: pii_redaction" for a
call that was refused because the replacement could not be written. The line
now distinguishes the two. The raised exception was already correct, since
to_policy_violation branches on transform.

A behavioural test drives the maf middleware with a dict replacement and
asserts it refuses, does not call the agent, leaves the message intact and
records the block. It fails on the pre-fix shape. It lives with the other maf
scenarios rather than in agent-os, where driving the middleware would have
needed a stub of everything around it.

agent-os 2923, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken. Census 42 apply, 24 refuse, 0 naive; 26 inserted guards reachable; 16
folds correctly placed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: a rewrite that fails to land is refused, not swallowed

The last open finding. Twenty-four sites applied a transform inside a try
whose handler only passed, so a write the framework object refused left the
original value in place and execution carried on while the policy believed it
had rewritten. The write targets objects the adapter does not own, and frozen
pydantic models and immutable message contents do raise on assignment, so
this was reachable rather than theoretical.

I had reported it rather than fixed it, on the grounds that it needed a
behaviour decision. The same reasoning was rejected for the shape mismatch
and it is no better here.

Eighteen sites whose function already raises to_policy_violation now raise it
from the write failure. maf raises MiddlewareTermination, matching its deny
path. crewai's before_tool and before_llm and autogen's on_send track whether
the rewrite landed and take the refusal each already had, because their
targets are searched for in a loop or written through two different shapes,
so a raise would be in the wrong place.

crewai's before_llm also refuses when no message takes the rewrite at all,
which had the same effect as a failed write.

A third census fails on any transform rewrite whose handler only passes,
unless the function tracks whether it landed. It reads both spellings, since
some sites hold the replacement in a local first. Reintroducing a swallow in
gemini fails it.

agent-os 2921, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken. Census 42 apply, 24 refuse, 0 naive; 26 inserted guards reachable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: a write target that takes nothing is refused, not passed over

An eighth finding, and a fourth way the same value gets dropped. The three
guards so far cover the verdict, the replacement's shape, and a write that
raises. This one is a write that is never attempted: a guard on what is being
written TO fails, no exception is raised, and the original value goes out.

Two were demonstrated end to end. autogen's _apply_content buried the swallow
in a helper that returned None, so on_send's rewritten flag could never go
false and on_publish had no check at all; a message whose content rejects
assignment forwarded the original with allowed=True. langchain's
wrap_tool_call wrote only when the result had .content, and a tool returning a
plain string has none, which the same function's own str(getattr(result,
"content", result)) shows is expected.

_apply_content now returns whether the write landed and both callers read it.
The rest refuse on the branch that cannot take the replacement: langchain's
model input loop, which could complete without writing anything, its response
path, anthropic's and mistral's input loops on a non-dict message, mistral's
tool call with no function to write to, google_adk's non-dict tool args, and
smolagents' tool call carrying neither argument attribute.

A fourth census covers the shape: an application guarded on the target with no
refusal, no flag and no else. It found a ninth site in mistral that was not in
the report, and reintroducing the anthropic guard fails it.

agent-os 2921, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken. Census 42 apply, 24 refuse, 0 naive; 26 inserted guards reachable; 20
raise and 5 flag-guarded rewrites, 0 swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: redact and continue where the value is the return, not an attribute

The previous commit over-corrected langchain. A tool returning a plain string
has no .content to write to, and I made that a hard denial, which turns a
redaction policy into a block for the most ordinary kind of tool. llamaindex
and semantic_kernel already handle the same case by returning the replacement
itself, which is right: the value IS the return, so handing back the redacted
one applies the policy rather than refusing it. Both langchain sites now do
the same. The comment I left there was also wrong; returning transformed_value
hands back the redaction, not the original.

Census 4 flagged the corrected form, since it only recognised a raise, an
else or a landed-flag. Returning the replacement is a third correct handling
and it now counts. Reintroducing the anthropic drop still fails the census, so
it did not go slack.

agent-os 2921, agt-policies 350, agent-sandbox 499, ratchet 1415, links 0 new
broken, ratchet self-test and tests/ci 108, no-stubs clean. Census 42 apply,
24 refuse, 0 naive; 26 guards reachable; 20 raise and 5 flag-guarded, 0
swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: make the verdict stubs read the same at every layer of the stack

The stubs modelled the pre-ACS evaluation, which reads verdict, transform and
reason off the evaluation itself. The ACS runtime reads them off a Verdict
object instead, so the same tests failed at the last PR in the stack with
'str' object has no attribute 'transform', and the session there routes every
intervention point through one evaluate() rather than a method per point.

The stubs now carry both shapes: a verdict that is still a string but also
answers .decision, .reason and .transform, and a session that accepts either
spelling. One version of the file passes at both ends of the stack, so the
tests do not have to be ported layer by layer as the runtime changes
underneath them.

agent-os 2921 at this layer and at the tip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: spell-check on the words this PR introduces

cspell runs on changed lines and flagged five. Three were mine to reword or
rename: a comment said "unrewritten", and two locals in the census helper were
funcname and fbody, now func_name and enclosing_src.

conftest and orelse go in the dictionary instead. The first is pytest's own
filename, which this PR imports from now that the framework stubs live there;
the second is the attribute name on ast.If, which the census reads to tell an
unguarded branch from one that refuses.

agent-os 2921, ratchet 1415.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: refuse a transform langchain cannot write, instead of returning a bare string

The previous commit had these two sites return post_result.transformed_value
when the target had no .content, on the grounds that llamaindex and
semantic_kernel do the same. That analogy was false and the change was wrong.

LangChain types wrap_tool_call as returning ToolMessage | Command and
wrap_model_call as ModelResponse | AIMessage | ExtendedModelResponse. A str is
none of those. It also breaks the premise I wrote in the comment: a tool that
returns a plain string is wrapped as a ToolMessage before middleware sees it,
and ToolMessage has .content, so what actually arrives without one is a
Command. Returning a str there makes ToolNode fold it into messages as a
HumanMessage, losing the command's updates and its tool_call_id; on the model
path the caller then reads .result off a str and raises AttributeError. The
siblings differ because their return type is Any by contract.

Both sites refuse again, which is protocol-safe, fail-closed, and the same
answer the sandbox providers, the MCP gateway and the trust root already give
when they cannot apply a replacement.

Refusing also removes a budget bug the early return had introduced: it skipped
self._ctx.call_count += 1, so a redacted tool call was never charged and
max_tool_calls undercounted.

A scenario test drives wrap_tool_call with a Command and a transform verdict
and asserts it refuses. It fails on the previous revision, which returned str.

Census 4 now reads the If node's own else branch rather than searching the
unparsed source for "else:", which also matched an else on a nested for, try
or inner if and could mark a dropped transform as handled.

agent-os 2921, agt-policies 351, ratchet 1415, censuses 42 apply / 24 refuse /
0 naive, 20 raise and 5 flag-guarded rewrites, 0 swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* test: close two holes in the target-guard census

Both found by re-reviewing the census itself rather than the code it guards.

A raise inside the guarded branch's own except handler was counted as
refusing. That raise fires when the write THROWS, which is the class census 3
covers; it says nothing about the branch never being entered. Counting it
exempted the repo's most common shape, a guarded write inside a try, which is
the likeliest way a skipped write gets back in. Only a raise reachable when
the guard is false counts now. Restoring main's shape at langchain's tool-post
site is caught, where before it passed.

The fallback clause stays scoped to the whole block, deliberately, and now
says why. llamaindex and semantic_kernel chain several target guards and end
the block with `return <result>.transformed_value`, so a guard falling through
reaches that fallback rather than carrying on with the original. Scoping it to
the branch flagged all four as drops. The two fixes interact and only one of
them is right.

agent-os 2921, agt-policies 351, agent-sandbox 499, ratchet 1415, links 0 new
broken, censuses 42 apply / 24 refuse / 0 naive, 26 guards reachable, 20 raise
and 5 flag-guarded rewrites, 0 swallowing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(integrations): stop transform verdicts from proceeding unapplied

A transform verdict permits, so code that gates on is_allowed alone lets the
run continue with the original payload while the policy believed it had been
rewritten. A redaction policy silently does not redact. Neither surface here
can apply a replacement: the lifecycle hooks observe a run, and a guardrail can
only trip or not.

Both now refuse a transform rather than proceeding. The guardrail fix covers
its input and output sites, not just the one flagged, since the two shared the
defect.

Also in this package, the input guardrail passed a list of input items straight
to evaluate_input, which declares body as str or dict. The list branch now
encodes to a dict.

Two packaging faults that would ship:

  adk_agentmesh/__init__.py is a deprecation shim with no re-exports, so every
  import in its own README raised ImportError. The four documented names are
  re-exported, and each one was imported to confirm it.

  agent-governance-toolkit-integrations declared the console script
  openshell-agentmesh = openshell_agentmesh.cli:main while this stack deletes
  cli.py, so the wheel would install a command that crashes on first run. The
  entry is removed; only a deprecation shim remains in that package.

Tests cover every verdict on both surfaces. They skip where the openai-agents
SDK is missing, as the package's existing tests already do, so the behaviour
was additionally verified against stubbed SDK modules: allow and warn proceed,
deny, escalate and transform refuse, and the guardrail body encodes to a dict.

Ratchet 1180. Links: 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor(v4-removal)!: move the examples onto ACS v5 manifests (#3452)

* refactor(v4-removal)!: move the examples onto ACS v5 manifests

Example policies and bundles move to ACS manifests, and the demos that existed
only to show v4 syntax are deleted with their fixtures. The package-local
examples, docs, tutorials, benchmarks and notebooks under agent-os follow the
same move.

docs/compliance/nist-rfi-2026-00206.md moves here too, because it links to the
native A2A runtime example this PR adds and the link would dangle if the two
landed separately.

Also carries two review fixes:

  sync_atr_rules interpolated third-party ATR patterns into a Rego backtick
  raw-string literal. Raw strings have no escape mechanism, so a pattern
  carrying a backtick closed the literal and the rest parsed as policy source.
  The weekly sync compiles that corpus from npm unattended, so a poisoned
  upstream rule could flip a verdict on benign input. Patterns are now emitted
  as JSON-quoted strings and the validator rejects backticks. atr-import shares
  the emission path through compile_patterns.

  The demos README documented AgtRuntimeMiddleware, which is
  RuntimeGovernanceMiddleware, and advertised real LLM calls with Ollama setup
  instructions. The example it points at makes no model call, so that section
  described a mode that does not exist.

Ratchet: 656. Links: 0 new broken. atr tests: 5 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs(rust): correct the dependency audit and document the unbound-point deny (#3445)

The audit claimed nothing was pulled from crates.io that was not already in the
graph. Cargo.lock gains 66 entries: 2 first-party and 64 third-party crates
that were not in the workspace before. It now lists them and names the two
clusters.

The second cluster is the one that matters: ureq, rustls, ring and
webpki-roots link an HTTP and TLS stack into agentmesh. They arrive because the
ACS Rust SDK pins its core features to include the remote annotators and
because Manifest::from_url exists, and a consumer cannot opt out since the
feature set is fixed inside the SDK crate. Nothing in agentmesh calls a remote
annotator or from_url, so no egress is added in practice, but for a governance
library the linked capability belongs in the security record. Narrowing the
SDK's features is a follow-up on that crate.

MIGRATION_V5.md also now documents that the runtime denies an intervention
point the manifest does not declare. Failing closed is right, but the adapter
evaluates input on every execute() and pre_tool_call on every tool call, so a
manifest binding only one denies the other path with no obvious cause.

Links: 0 new broken.

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

* docs(v4-removal): rewrite the documentation for ACS v5 (#3446)

* docs: use the real class names in the integration guides

A mechanical rename left several component names that resolve to nothing:

  AgtRuntimeMiddleware and AgtRuntimeMW are RuntimeGovernanceMiddleware, and
  CapabilityGuardMW, AuditTrailMW and RogueDetectionMW are the corresponding
  *Middleware classes in maf_adapter.

  AgtRuntimeChecker never existed in any SDK. The Haystack integration ships
  TrustGate and AuditLogger, which is what the tables now list.

  PolicyEvaluation.* in the .NET tutorial was the rename applied where it does
  not belong: the .NET engine still defines PolicyAction and no PR in this
  stack touches dotnet source. Reverted, and the file is allowlisted in the
  ratchet so documenting that engine's own enum does not read as v4 usage.

Ratchet: 94, unchanged. Links: 0 new broken. Ratchet tests: 35 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor(v4-removal)!: confine the v4 policy language to the migration tool (#3447)

* docs: scope the breaking-changes record to what each PR removes

The Python entry listed the whole stack's removals in one place, so it
overlapped the record the first PR now carries and named 33 symbols twice.
It also claimed agent_os.kernel keeps a PolicyRule; the surviving one is
re-exported from agent_control_plane.

What is left here is what could only go once the rule model went: manifest
resolution, the evaluators and decision types that read the rule model, the
framework integrations' local policy surfaces, and the unreleased cedarling
backend. Names that survive under the same spelling in an unrelated module
are called out, since agent_os still exports a PolicyDecision and a
PolicyRule that have nothing to do with the v4 language.

The langflow README documented GovernanceComponent, which this stack deletes.
Its section, the YAML policy that only configured it, its table row, and its
node in the flow diagram are gone, matching what the haystack and openshell
READMEs already do.

ratchet 0, links 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* refactor!: delete agt.policies and evaluate through ACS directly (#3449)

* fix(acs): drop the dead manifest-timeout branch and correct the docs it claimed

_resolve_approval_timeout read approval.timeout_seconds off
getattr(control, "manifest", None). AgentControl never stores that attribute,
so the branch was dead in production and only ran in tests because the stub
defined one. BREAKING_CHANGES then documented behaviour the code could not
deliver.

The core treats the approval section as opaque host configuration
(SPECIFICATION §17.1) and does not surface it across the native boundary, so
honouring it needs an SDK accessor that does not exist. The branch is removed,
the docstring and BREAKING_CHANGES say a caller must pass the value, and the
300 second default that closes the original fail-open stays.

The tests that covered the branch went with it. A replacement drives a resolver
that never returns through a real HostSession and asserts it denies in about a
second, which the removed tests could not have caught.

Also repairs what earlier mechanical renames left behind:

  AgentControlMiddleware and AgentControlMW in the Python and MAF docs are
  RuntimeGovernanceMiddleware. The .NET class of that name is real but belongs
  to the ACS dotnet SDK, so the name resolved while meaning the wrong thing.

  AgentControlChecker and HaystackAdapter exist nowhere; the Haystack package
  ships TrustGate and AuditLogger. NoOpAgentControl and the compat.py path
  beside it were both deleted by this stack.

  Ten `the ACS manifest` substitutions where a symbol name used to be, across
  AGENTS.md, three compliance docs and openclaw-sidecar.md.

  Fourteen compliance citations pointing at agt/policies and agent_os/policies
  files this stack deletes, repointed at the ACS SDK.

  The agt-policies README still described the deleted wrapper, including two
  submodules that never existed and a snippet calling AgentControl.evaluate.
  Rewritten for what the package now is, a migration tool, and every command
  and symbol in it was run.

ACS host: 25 passed. agent-os: 2683 passed. Ratchet 0. Links: 0 new broken.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* docs: cite the live Haystack integration, not the deprecated package

The framework tables in docs/FAQ.md and agent-mesh/README.md pointed at the
deprecated haystack_agentmesh package and its TrustGate/AuditLogger
components. The maintained module is agentmesh.integrations.haystack, whose
exports are TrustGateComponent, TrustAgentComponent, TrustedPipeline, and
PipelineAuditEntry.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* test: delete the dead __init_result__ stub from _RecordingControl

The method is not a dunder Python calls, nothing in the suite invokes it,
and its body only raises NotImplementedError. Verified by AST that
_RecordingControl keeps __init__ and evaluate_intervention_point; the PyO3
_native extension is not built in this environment, so the module was
checked with py_compile rather than by running pytest.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs: describe the post-removal world with AgentControl, not AgtRuntime

Two sentences in BREAKING_CHANGES.md used AgtRuntime for what code takes
after the removal, contradicting the file's own removed-symbols table,
which maps AgtRuntime(path) to AgentControl.from_path. The mentions that
remain describe the removed v4 surface and stay as they are.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(openai-agents-trust): note the hooks path carries no tool arguments

The openai-agents lifecycle hook signature (on_tool_start/on_tool_end) does
not include the tool-call arguments, so GovernanceHooks evaluates
pre_tool_call and post_tool_call with args={}. Policy rules conditioned on
tool arguments therefore never match on this path. Say so at both call
sites and in the README, pointing at the guardrail integration for
arg-level enforcement.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* fix(openshell): stop pointing the deprecation at a package that ships this shim

The DeprecationWarning and README told users to install
agent-governance-toolkit-integrations[openshell], but that extra
force-includes this same package, whose skill (GovernanceSkill,
ShellPolicyViolation, governed_shell) this stack removes - the pointer was
circular. Say what is true: the skill is removed in the v5 ACS migration
with no OpenShell-specific replacement; build an AgentControl from an ACS
manifest and evaluate intervention points in the host. Packaging and
force-includes are unchanged.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(examples): remove the openshell-governed demo

The v4-removal stack deletes openshell_agentmesh.skill (GovernanceSkill),
which was this demo's only entry point. The example could no longer run;
remove it with the rest of the retired openshell surface.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs: retire the openshell integration page

The v4-removal stack deletes the openshell governance skill this page
documents (its quickstart imports the removed GovernanceSkill). Remove
the page and the openclaw-sidecar cross-link along with the example
removed in the previous commit.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f

* fix(ratchet): restore enhanced scanner, dedupe BREAKING_CHANGES entry, add _migrate_resolution prefix, update baseline post-merge

Signed-off-by: GitHub <noreply@github.com>

* fix: restore what the conflict resolution dropped, and address review

The automatic resolution of the merge with main deleted 158 files that both
main and this branch still had. They were not deliberate removals: examples,
benchmarks, docs, and the ``agt.policies`` modules ``manifest``, ``session``
and ``_re2``, whose absence broke every agent-os test that loads a manifest.
All are restored from the pre-merge branch tip. The resolver had also taken
the downstream package's ``agt/__init__.py``, which assumes ``agt.policies``
is gone; the version here exports both surfaces and resolves ``cli`` lazily,
because importing the migrator eagerly pulls in the native SDK and made
``from agt.policies import AdapterRuntimeSession`` fail on a host without it.

Three moves the resolver got half-right are completed rather than reverted:
the resolution layer now lives only under ``agt.cli._migrate_resolution``, so
its last two importers point there, and the scenario harness that reached into
it from outside the CLI is gone along with its only consumer, which is what
the migrator-boundary test asks for. The ratchet baseline was recomputed on
the corrected tree; it had been written against the tree missing those files.

Review feedback: the ADK README's quickstart named a class the package does
not export, so it raised ImportError on the first line a reader runs; it names
``ADKPolicyEvaluator`` now and the snippet was executed. The lifecycle hooks
in openai-agents-trust called ``audit_record()`` on a result type that has no
such method, and handed the audit log a verdict object where it declares a
string and then hashes it, so every governed tool event raised before it could
decide anything; both writers now share one JSON-safe record builder.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix: rebuild the merge on the stack's end state and green the CI gates

The automatic resolution merged #3519 — a fix branched from main — into the
stack and took main's side wherever they overlapped. That resurrected
``agt.policies``, which this stack deletes, leaving a tree that was neither
state: the wheel gate failed because the migration wheel shipped a second
policy layer, and the resolver package was half-present.

The tree is rebuilt as the stack's end state plus main's independent changes,
which is what the merge was supposed to produce. #3519's behavioural fix is
re-applied on top rather than dropped: the four adapter sites that evaluated
the output intervention point and discarded the verdict now raise on a deny,
so a blocked response is not disclosed, and the anthropic scenario manifest
binds the output point those tests now exercise.

The remaining gate failures were consequences of the same move. The crypto
allowlist still named ``manifest_resolution/build.py`` after the stack moved
it under the private migrator, so its long-standing SHA-256 exemption stopped
matching. Two integration packages lost their license headers. The spell
checker met crate names from the Rust dependency audit and two British
spellings. Two gitleaks findings are documentation placeholders, fingerprinted
the way this repository already handles that file.

Review feedback: the ADK README named a class the package does not export, so
its quickstart raised ImportError on the first line; it names
``ADKPolicyEvaluator`` now and the snippet was executed. The openai-agents
lifecycle hooks and guardrails called ``audit_record()`` on a result type that
has no such method and handed the audit log a verdict object where it declares
a string and then hashes it, so every governed event raised before deciding;
both writers share one JSON-safe record builder.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(ci): repair dependency pins, ACS SDK installs, and stale doc links

Fixes the CI failures left by the automatic merge-conflict resolution on
this branch. Six distinct root causes, not one:

- agent-governance-toolkit-core kept the stack's stale
  `agentrust-trace>=0.2.0,<0.3.0` instead of main's dependabot bump to
  `>=0.5.1,<0.6.0`, making agent-mesh and docker-compose-test unresolvable
  (ResolutionImpossible) and breaking the TRACE v0.2 sinks.
- agent-compliance, agent-marketplace and agent-sandbox now import
  `agent_control_specification` at module scope, but the CI gate that
  builds the native ACS SDK only covered agent-os and agt-policies.
- The Policy Validation workflow calls the ACS-backed manifest linter
  without ever building the ACS SDK.
- test-integrations resolved the published ACS 0.3.1b0 wheel, which
  predates `HostSession`; build the vendored SDK first so the resolver
  sees the requirement already satisfied by this commit's source.
- test_hooks_verdicts asserted a `HostSession.evaluate_input(body=...)`
  API that does not exist. The real intervention point is
  `HostSession.input(body: JsonValue)`, which accepts a list, so the test
  now covers what actually matters: every non-dict item is stringified so
  the body stays JSON-serializable.
- Three i18n READMEs still linked `docs/integrations/openshell.md`, which
  the stack deletes, and the dcbadge ignore pattern pinned the previous
  Discord invite code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: Liam Crumm <liamcrumm@gmail.com>

* fix(merge): keep the post-base Discord invite fix and Rust dependency pins

Two main-side deltas from after the merge-base were reverted by the
stack-end-state rebuild and must survive the merge:

- #3481: README.md and README.ko.md returned to the expired 7aVPCcVh
  Discord invite; restore the non-expiring TxMRqY3pFr link
- Dependabot: re-lock the base64 0.23.0, clap 4.6.4, regorus 0.11.0,
  and cedar-policy 4.12.0 pins against the branch workspace
  (cargo update --precise per crate)

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* test(agent-os): re-add the #3519 output-verdict regressions and census

The rebuild dropped the regression tests that pin the anthropic and
autogen output-verdict enforcement (the adapter-side fix itself is in
the tree) and the discarded-verdict census in
test_transform_never_dropped.py. Re-add them with the per-point stub
adapted to the ACS evaluate_intervention_point protocol; all pass
against the branch adapters.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

---------

Signed-off-by: Liam Crumm <liamcrumm@gmail.com>
Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Copilot-Session: 384bda36-161f-4689-9b2c-0a6d5c90776f
Signed-off-by: SemTiOne <emphyst80@gmail.com>
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)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants