Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/reference-implementation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@
# concurrency group are an integer pull request number and a ref name GitHub validates,
# and a concurrency group is not a shell context.
#
# Scoped by path. A documentation or schema change should not pay for a Bun install.
# Scoped by path. A documentation change should not pay for a Bun install. A schema change
# does, because this tree reads the schemas at runtime.
name: Reference implementation

on:
pull_request:
paths:
- "reference-implementations/**"
# The conformance checker and the trace exporter read the schemas and
# the Trace mappings at runtime, so a spec-only change can break them.
- "specification/**"
- ".github/workflows/reference-implementation.yml"
push:
branches: ["main", "integration"]
paths:
- "reference-implementations/**"
# The conformance checker and the trace exporter read the schemas and
# the Trace mappings at runtime, so a spec-only change can break them.
- "specification/**"
- ".github/workflows/reference-implementation.yml"
workflow_dispatch:

Expand Down
42 changes: 40 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
# Changelog

All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) once it reaches 1.0.0. Pre-1.0 releases MAY change the ACS-Core baseline in patch versions; the wire format version (`acs_version` in the handshake) evolves separately in the `specification/` directory.
All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) once it reaches 1.0.0. Pre-1.0 releases MAY change the ACS-Core baseline and the Trace mappings in patch versions. The wire format version (`acs_version` in the handshake, and the `v0.1.0` in every schema `$id`) evolves separately in the `specification/` directory.

## [0.1.3] — 2026-09-09
## [0.1.4] - 2026-10-16

This release changes ACS-Trace for producers and consumers. The specification version stays v0.1.0, so the files at the `/schema/v0.1.0/` URIs change in place, and the handshake cannot tell the old rules from the new. Name this release, or its commit, when you claim ACS-Trace conformance.

### Changed: Trace (breaking)

- Every step span now requires `acs.request_id` and `acs.session.id`. A reader joins the two parties' spans on that pair. An exporter that omits either no longer conforms ([PR #275](https://github.com/GenAI-Security-Project/agent-control-standard/pull/275), issue #228).
- The OCSF decision class now requires `finding_info`, `finding_info.title`, `action_id`, `action`, `disposition_id`, `disposition`, `evidences` and `metadata.correlation_uid`, and decision events list the `security_control` profile. `defer` maps to `disposition_id` 99. `is_alert` is true exactly when `severity_id` is 3 or higher, so deny, ask and defer alert, and allow and modify don't. A decision that arrives late is emitted and flagged in `finding_info.types`, where it used to be suppressed (PR #275).
- A decision event records the Guardian's verdict, not the enforced outcome. A matching `acs.decision.failure` in either party's stream means the decision MUST NOT be counted as enforced (PR #275).
- The four `policy_references` fields and the provenance `source_id` now require `minLength: 1` (PR #275, issue #263).
- The OCSF mapping is pinned to OCSF 1.9.0 and uses its class names. Three rows on class 5001 set `activity_id` 2, and `required_fields` includes `finding_info` ([PR #258](https://github.com/GenAI-Security-Project/agent-control-standard/pull/258)).

### Added

- The `acs.decision.failure` span event, and the extra `acs.decision` attributes a reader needs to replay a decision from the trace alone (PR #275).
- Per-hook `conditional_attributes` in `otel-mapping.json` ([PR #261](https://github.com/GenAI-Security-Project/agent-control-standard/pull/261)).
- Trace mappings for the skill lifecycle hooks, `steps/skillRegister`, `steps/skillLoad` and `steps/skillUnload`, in both OpenTelemetry and OCSF ([PR #63](https://github.com/GenAI-Security-Project/agent-control-standard/pull/63), issue #57, issue #58).
- The reference Guardian exports OTLP traces to a file and over HTTP, and a converter turns the host adapter's audit log into the same format. It covers the two hook methods the Guardian evaluates and is not an ACS-Trace conformance claim ([PR #244](https://github.com/GenAI-Security-Project/agent-control-standard/pull/244), issue #91).
- A committed sample trace, a test that holds the exporter to the mapping, and "Produce a trace", a walkthrough from a fresh clone to a trace file, tested natively and with Docker ([PR #277](https://github.com/GenAI-Security-Project/agent-control-standard/pull/277), issue #266).
- "How the reference Guardian produces a trace" on the Trace with OpenTelemetry page, with diagrams of both streams and of one tool call, and a Troubleshooting list in the Guardian README ([PR #280](https://github.com/GenAI-Security-Project/agent-control-standard/pull/280)).

### Changed: reference implementation

- The audit converter's spans carry `service.name` `acs-reference-audit-converter`, no longer the Guardian's `acs-reference-guardian`. A query that filtered on the old value for converter spans needs updating (PR #277, issue #274).
- The converter refuses to write to an output file that already exists and exits 1. It used to append, which could write OTLP lines into the audit log or double every span on a rerun (PR #277).
- `otel-collector.yaml` stays on loopback, keeps its file across restarts, writes owner-only, and pins its container image by digest (PR #277, issue #276).
- `OTEL_TRACES_*`, `OTEL_SPAN_*`, `OTEL_ATTRIBUTE_*`, `OTEL_BSP_*` and OTLP compression variables no longer change what the Guardian or the converter exports. `OTEL_EXPORTER_OTLP_*HEADERS` still apply (PR #277).
- A loopback trace endpoint is added to whichever of `NO_PROXY` and `no_proxy` is set, keeping the user's own entries, and the Guardian warns at startup when a proxy is set and loopback isn't exempt. The walkthrough says to stop the Guardian and the collector before deleting `.acs` ([PR #279](https://github.com/GenAI-Security-Project/agent-control-standard/pull/279), issue #274).
- The Guardian's `main.ts`, not the trace exporter, now reads the proxy environment and applies the loopback exemption, and `close()` applies the same bounded drain whether trace export is on or off (Ariel's review on PR #286).

### Removed

- `acs.provenance.lineage_depth` and `acs.provenance.origins_compacted` from the OTel mapping (PR #261).

### Changed: dependencies

- urllib3 2.7.0 to 2.8.0 ([PR #202](https://github.com/GenAI-Security-Project/agent-control-standard/pull/202)).

## [0.1.3] — 2026-10-05

### Changed — ACS-Core baseline

Expand Down
6 changes: 3 additions & 3 deletions GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ACS is an OWASP project. This file records who leads the work, which workstream

| Role | Name |
| --- | --- |
| Project Lead | Rock Lambros ([@rocklambros](https://github.com/rocklambros)) |
| Project Lead | Kyriakos Lambros (Rock) ([@rocklambros](https://github.com/rocklambros)) |
| Project Lead | Ariel Fogel ([@afogel](https://github.com/afogel)) |
| Project Lead | Bar Kaduri ([@bar-capsule](https://github.com/bar-capsule)) |

Expand All @@ -24,7 +24,7 @@ against the conformance requirements it touches.
| Workstream | Leads |
| --- | --- |
| Coding Agents | Almog Langleben ([@almogbhl](https://github.com/almogbhl)), Stefano Amorelli ([@stefanoamorelli](https://github.com/stefanoamorelli)) |
| Development (SDK) | Rock Lambros ([@rocklambros](https://github.com/rocklambros)), Fred Wilmot ([@fewdisc](https://github.com/fewdisc)) |
| Development (SDK) | Kyriakos Lambros (Rock) ([@rocklambros](https://github.com/rocklambros)), Fred Wilmot ([@fewdisc](https://github.com/fewdisc)) |
| Documentation | Lance Dye ([@Lrd0036](https://github.com/Lrd0036)) |
| Identity | Eva Benn ([@evabenn](https://github.com/evabenn)), Richard Bird ([@RbBuiltWrong](https://github.com/RbBuiltWrong)) |
| Outreach | Eva Benn ([@evabenn](https://github.com/evabenn)), Aruneesh Salhotra ([@aruneeshsalhotra](https://github.com/aruneeshsalhotra)) |
Expand Down Expand Up @@ -75,7 +75,7 @@ they step back.

| Volunteer | Assigned by |
| --- | --- |
| Victor Hernandez ([@victorm-hernandez](https://github.com/victorm-hernandez)) | Rock Lambros |
| Victor Hernandez ([@victorm-hernandez](https://github.com/victorm-hernandez)) | Kyriakos Lambros (Rock) |

## Origins

Expand Down
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ the one sending hook requests like the ones above. The **Guardian Agent** sits o
other end of the wire and decides what happens next.

A **hook** is a point in the Observed Agent's execution where it stops and asks before
acting. `toolCallRequest` is one of ACS's sixteen native lifecycle hooks. Others fire at
acting. `toolCallRequest` is one of ACS's nineteen native lifecycle hooks. Others fire at
session start and end, before and after a turn, on knowledge retrieval, and around
memory reads and writes.

Expand Down Expand Up @@ -289,12 +289,24 @@ harness measures the wire, not the language behind it.

### Harden it for production

Two gaps are named and open, not vague. The Guardian writes its envelope and audit logs
as synchronous file appends on the decision path, with no batching, so trace spans need
a batched writer before a Trace-pillar claim would hold up under load. No OpenTelemetry
collector or exporter exists yet. The conformance harness only measures which
attributes a consumer *could* emit from the envelopes, not that anything ships them.
Start in [`reference-implementations/agt/packages/`](reference-implementations/agt/packages/).
Two gaps are named and open, not vague. The Guardian writes its envelope and
session-context logs, and the host adapter writes its audit log, as synchronous file
appends on the decision path, with no batching. Trace export is
partial: set `ACS_OTEL_FILE` or `ACS_OTEL_ENDPOINT` and the Guardian exports OTLP spans
for the two live methods, `steps/toolCallRequest` and `steps/toolCallResult`, as the
[Guardian README](reference-implementations/agt/packages/guardian/README.md#export-traces)
describes. Failures only the host sees, such as an unreachable Guardian, stay in the host's
audit log until a converter turns them into spans of the same format.
[How the reference Guardian produces a trace](docs/spec/trace/extend_opentelemetry.md#how-the-reference-guardian-produces-a-trace)
explains how the two streams join. A [sample trace](reference-implementations/agt/samples/trace/acs-trace-sample.jsonl)
shows that output without running anything, and [Produce a trace](reference-implementations/agt/README.md#produce-a-trace)
walks you from a fresh clone to a trace file of your own.

That exporter does not make the reference ACS-Trace conformant, and this tree claims no
Trace profile. It covers two methods, emits no OCSF, and leaves out a required attribute
when the request omits it. The conformance harness still measures which attributes a
consumer *could* emit from the wire, not what the exporter ships. Start in
[`reference-implementations/agt/packages/`](reference-implementations/agt/packages/).

### Go deeper

Expand Down
1 change: 1 addition & 0 deletions docs/concepts/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ Load is the runtime gate. It governs each activation in live context and carries

- **Instrument**: the [`skillRegister`, `skillLoad`, and `skillUnload` hooks](../spec/instrument/hooks.md#skillregister).
- **Inspect**: the `skill` [AgBOM component type](../spec/inspect/README.md).
- **Trace**: the [span names and OCSF classes](../spec/trace/events.md) the three hooks emit.
- See also [Capability](./capability.md), [Provenance](./provenance.md), and [Trust basis](./trust.md).
2 changes: 1 addition & 1 deletion docs/spec/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Who verifies a conformance claim: nobody, in v0.1.0. `profiles_supported` and `p

Adds deterministic Trace event emission per [Trace Events](./trace/events.md). A deployment claiming ACS-Trace MUST:

1. Emit at least one of {OTel, OCSF} for every supported ACS step, with the required attributes populated.
1. Emit at least one of {OTel, OCSF} for every supported ACS step, with the required attributes populated and each conditional attribute populated whenever its source field is present.
2. Record decisions as Trace events.
3. Carry provenance facts forward onto Trace events.

Expand Down
16 changes: 16 additions & 0 deletions docs/spec/trace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ AI agents make autonomous decisions that impact business outcomes. Without obser
!!! info "ACS Extends Industry Standards"
We already have great observability standards, so ACS doesn't introduce a new one. Instead, it extends existing industry-proven standards: OpenTelemetry and OCSF to support AI agent-specific components.

## Where Trace Sits

Instrument decides in the moment. Trace records what happened, out of the request path, so someone can rebuild a session afterwards: what the agent tried, what the Guardian decided, and which steps got no usable decision at all.

Both parties write to the trace, because each one sees something the other can't. In the diagram, notice that the Observed Agent and the Guardian each emit their own stream, and the two streams meet only at the reader.

```mermaid
flowchart LR
agent["Observed Agent"] -->|"hook request"| guardian["Guardian"]
agent -->|"spans and events"| formats["OpenTelemetry or OCSF"]
guardian -->|"spans and events"| formats
formats --> reader["Reader joins on<br/>session id + request id"]
```

The reader matches records from the two parties by each step's session id and request id. [Trace Events](./events.md) defines that join and the rule for counting a decision as enforced. To see it as real files, read [How the reference Guardian produces a trace](./extend_opentelemetry.md#how-the-reference-guardian-produces-a-trace), or open the committed [sample trace](https://github.com/GenAI-Security-Project/agent-control-standard/blob/main/reference-implementations/agt/samples/trace/acs-trace-sample.jsonl).

## Why Agent Observability Matters

Modern agents orchestrate complex workflows: reasoning chains, tool execution, knowledge retrieval, multi-agent collaboration. When things go wrong, or right, you need to understand exactly what happened.
Expand Down
Loading
Loading