Integrate canonical v0.1.0#2
Conversation
Replace the single legacy specification/ACS/acs_schema.json with the modular
v0.1.0 schema package from canonical ACS. The legacy file becomes an
aggregator manifest that cross-references the new modular files.
Adds:
specification/v0.1.0/{request,response}-envelope.json
specification/v0.1.0/{handshake,provenance,provenance-summary}.json
specification/v0.1.0/{context-entry,ask-details,defer-details,modifications}.json
specification/v0.1.0/hooks/*.json (19 per-hook schemas: session-start/end,
agent-trigger, turn-start/end, user-message, agent-response,
knowledge-retrieval, memory-context-retrieval, memory-store,
tool-call-request/result, pre/post-compact, subagent-start/stop,
system-ping, agbom-snapshot/changed)
specification/v0.1.0/agbom/{component,document}.json
specification/v0.1.0/inspect/format-mapping.json
specification/v0.1.0/trace/{otel,ocsf}-mapping.json
specification.md and hooks.md now mirror the canonical ACS v0.1.0 spec proposal: design principles, two-layer Guardian architecture, JSON-RPC 2.0 envelope with stdio + HTTP transports, capability-negotiation handshake (handshake/hello), 16 native steps/* hooks, full disposition vocabulary including DEFER, decision-result fields supporting paradigm composition (FIDES / CaMeL / AARM / IBAC), provenance with optional trust enum and default channel-to-trust mapping, SessionContext with normative chain hashing (RFC 8785 JCS + SHA-256), Intent immutability and intent-extension via ASK, crypto-agile signature registry (HMAC, ECDSA, RSA-PSS, ML-DSA, SLH-DSA, hybrids), replay protection, OPA policy-engine reference binding, the new lifecycle hooks (sessionStart/End, turnStart/End, pre/postCompact, subagentStart/Stop), system/ping liveness, multi-tenancy reservation, and the v0.2 / future deferred list.
Refresh docs/spec/trace/{events.md,extend_opentelemetry.md,extend_ocsf.md}
to align with the canonical v0.1.0 mapping:
- OpenTelemetry: span-name + required-attribute table for every step,
decision-as-span-event rule, provenance attributes, span-link rule for
cited_provenance_ids.
- OCSF: class assignments per step (Authentication 3002, Application
Activity 6002, Process Activity 1007, Datastore Activity 6005, Detection
Finding 2004 for deny/modify/ask/defer, Inventory Info 5001 for
agbom/*) and disposition -> severity_id mapping.
- ACS-Trace conformance bar.
- Failure isolation: trace sink failure MUST NOT change the disposition.
Refresh docs/spec/inspect/{README.md,extend_cyclonedx.md,extend_spdx.md,
extend_swid.md} to align with the canonical v0.1.0 Inspect pillar:
- agbom/snapshot and agbom/changed wire methods, with audit-chain
integration.
- Canonical component graph with seven types (model, mcp_server, a2a_peer,
tool, knowledge_source, memory_store, agent_capability).
- registration_provenance on every component (REQUIRED under
ACS-Provenance).
- CycloneDX 1.6, SPDX 3.0, SWID derivations from the canonical document.
- ACS-Inspect and ACS-Inspect-Dynamic conformance bars.
New docs/spec/conformance.md defining the v0.1.0 profile system: ACS-Core (mandatory baseline) plus five optional profiles deployments declare in the handshake's profiles_supported / profiles_accepted fields. - ACS-Core: handshake, envelope, hooks, dispositions (incl. DEFER), SessionContext, Intent, replay protection, ping, wrapped MCP. - ACS-Trace: OTel + OCSF event emission, decision-as-span-event. - ACS-Inspect / ACS-Inspect-Dynamic: agbom/snapshot, agbom/changed. - ACS-Provenance: field-level Provenance with optional trust enum. - ACS-Crypto: ML-DSA-65 primary, SLH-DSA-128s backup, hybrids. - ACS-Audit: request_hash on every ContextEntry. Profiles compose; the page includes a quick-reference matrix.
core_concepts.md now introduces the v0.1.0 vocabulary: - Three pillars (Instrument / Trace / Inspect) as co-equal. - Two parties (Observed Agent / Guardian Agent with deterministic + optional agent layers). - Session, Turn, Step, Hook Response, Provenance, SessionContext, Intent with the immutability rule. - Default channel-to-trust mapping and the optional wire-level trust enum. ACS_in_action_example.md now mirrors the canonical Appendix C worked example: handshake, agbom/snapshot, sessionStart, userMessage, toolCallRequest with argument-level provenance, IBAC + FIDES decision envelope with reason_codes / policy_references / policy_data / cited_provenance_ids, plus a parallel deny-shaped example.
- docs/references/hook_surfaces.md: survey of lifecycle-hook systems across coding agents (Claude Code, Cursor, Codex, Copilot, Replit, Tabnine, Windsurf) with a semantic-event matrix and migration notes. Source for the v0.1.0 hook taxonomy and the DEFER disposition design. - docs/references/standards_compatibility.md: comparison of CaMeL, FIDES, AARM, IBAC, Conseca, MELON, ControlValve, AgentSentry, LlamaFirewall, PROV-AGENT/Flowcept; Google secure-agent guidance, Chrome User Alignment Critic, MCP security best practices, OWASP Agentic Top 10. Source for the deterministic-mediation core + pluggable detectors architecture. - docs/assets/ibac-paper.pdf: primary source for the Intent model. - docs/references/README.md: index linking the three sources.
Replace the work-in-progress intro with a v0.1.0 summary: what ACS is, the three pillars (Instrument / Trace / Inspect), and the six profiles (ACS-Core mandatory plus ACS-Trace, ACS-Inspect/-Dynamic, ACS-Provenance, ACS-Crypto, ACS-Audit). Leaves the existing aspirational SDK/MCP/A2A code samples below intact.
- Add Specification > Conformance Profiles entry pointing at the new spec/conformance.md page. - Add a top-level References section exposing hook_surfaces.md and standards_compatibility.md.
- Schema-file references now point at GitHub blob URLs under https://github.com/Agent-Control-Standard/ACS/blob/dev/specification/v0.1.0/... Schemas live outside docs_dir, so relative links from the docs tree cannot reach them; absolute URLs work both in the rendered site and when reading docs raw. - Update §13 anchor slug (Agent-Control-Standard#13-liveness-system-methods) to match Python-Markdown's TOC slugifier. - A2A hook docs: replace stale spec-anchor links (OWASP#48-a2a-protocol-methods, OWASP#51-acssuccessresponse-object) with code spans for the method names and a pointer to the new dispositions section. The protocols/A2A/* namespace remains reserved for v0.2. - extend_mcp.md: point at the new protocols/MCP/* anchor in hooks.md (the old #mcp-protocol-hooks anchor is gone). - gitignore: add site/ (mkdocs build artifact). `uv run mkdocs build --strict` now succeeds.
The hook-surfaces and standards-compatibility surveys plus the IBAC paper are local research artifacts, not site content. Move them to references/ at the repo root, gitignore that path, and drop the References section from the mkdocs nav. Files remain on disk locally; they no longer ship in the repo or build into the site.
ACS_in_action_example.md now shows one denial-shaped envelope per paradigm so readers can see how the same wire format expresses each: - IBAC: Intent.parsed mismatch with intent_parsed and closest match exposed in policy_data. - FIDES: P-T (planner-taint) failure with violating_argument_path, lineage_origins, and earliest_untrusted_provenance_id. - CaMeL: per-argument dependency-graph violation showing the actual vs. expected lineage roots. - AARM: cumulative-context denial with earliest_untrusted_step_id and the entry_count_by_origin aggregate from provenance_summary. - IBAC + FIDES composition: a single decision citing both paradigms in policy_references, reason_codes, and a policy_data object keyed by paradigm name. Also: repoint all schema GitHub URLs from Agent-Control-Standard/ACS (404) to afogel/ACS_official (the actual configured remote).
ClientHello now carries an optional approver_types_supported array (human / agent / service) mirroring the existing ServerHello field. New §9.2 normatively requires Guardians to substitute DEFER or DENY for ASK whenever the client/Guardian approver-type sets do not intersect. This lets headless automation, IDE plugins, and runtimes with allow/deny policy models (e.g., Microsoft Agent Governance Toolkit) participate in ACS sessions as fully conformant ACS-Core deployments — without silently allowing actions that would have been ASK'd elsewhere. Updates: - specification/v0.1.0/handshake.json: add approver_types_supported to ClientHello. - docs/spec/instrument/specification.md: §4 mentions the new field; §6 disposition table cross-references the fallback rule; new §9.2 spells out the normative substitution and the policy-driven DEFER vs. DENY choice.
…-band The handshake-field approach was over-engineered. §9.2 already preserves the security guarantee through Guardian-side substitution; the Guardian can determine client ASK-handling capability by deployment-defined means (agent identity, policy bundle, organizational configuration) without a wire-format declaration. Reverts the schema addition from a1b7958. The normative rule in §9.2 stands — Guardians MUST substitute DEFER or DENY when ASK isn't reachable — but the trigger is now 'Guardian determines the client cannot resolve ASK' rather than 'set intersection on the wire is empty.' Smaller spec surface, same outcome.
|
Heads up @afogel — doing some branch surgery that affects this PR. Want to flag it before it happens so nothing surprises you. Context (and acknowledging the screwup): when I cloned/forked this repo from the original OWASP/AOS source, I left both The change:
What this means for PR #2:
What you may want to do:
Sorry for the disruption. Should be the last branch surgery for a while. |
Catches the PR branch up with 5 commits on dev (sync_version trigger switch, CONTRIBUTING/README/CLAUDE.md updates, deploy_mkdocs removal, upstream docs typo fixes). Conflicts resolved in favor of the PR branch: - docs/acs.md: dev removed a TODO comment from text that the PR branch rewrote wholesale during the v0.1.0 documentation overhaul. - docs/spec/instrument/specification.md: dev fixed a typo and broken anchor in a section that the PR branch replaced with the new §11 error handling content. The PR branch's concise error code table supersedes the old verbose pre-rewrite tables.
|
@rocklambros I fixed the merge conflicts, should be good to go? |
Expand the one-line A2A reservation in hooks.md to note that the namespace is recognized in the handshake's wrapped_protocols field for forward compatibility, while making explicit that v0.1 defines no normative wrapping semantics. Addresses review feedback that the prior wording was too terse to tell readers what 'reserved' means operationally for v0.1 deployments.
Readers of the previous wording could not tell whether handshake or sessionStart comes first when both are in play. Position sessionStart explicitly after the handshake/hello exchange completes and before any other steps/* hook, removing the ordering ambiguity flagged in review.
Replace the terse 'User input received, before reaching the agent' with wording that mirrors the 'reasoning context' terminology used elsewhere in the spec, while preserving the existing provenance sentence. Improves consistency with toolCallResult and clarifies what 'reaching the agent' means in practice.
The previous wording implied the recipient is always a user, but agentResponse also fires on A2A egress, subagent-return to a parent agent, and outbound delivery to external systems. Generalize the recipient list so the description holds across all deployment shapes while keeping the existing provenance sentence intact.
Add a normative MUST clause to the toolCallRequest section requiring frameworks to fire the hook for every action that leaves the agent's reasoning context, regardless of whether the framework's tool registry models the action as a tool. Enumerates the easy-to-miss cases (filesystem reads/writes, network fetches, process execution, shell commands) and explains why: primitives that bypass toolCallRequest are invisible to policy, defeating the Guardian's ability to enforce on outward actions.
'Fires before tool execution' left the boundary fuzzy: readers could not tell whether the hook fires when the LLM emits a tool call token, when the framework parses it, or somewhere in between. Replace with the precise framing: after the framework has parsed a tool call from the LLM's output, but before the tool is dispatched to its handler. Matches the implementation contract Guardians need to reason about intercept points.
v0.1 carries factual provenance (origin, source_id, derived_from) on the wire and leaves trust classification to the Guardian against local policy. Calling arguments 'tainted' in the hooks doc implies a classification judgment the wire format does not make. Reword to lineage-based framing that stays consistent with the rest of the v0.1 provenance contract.
Mirror the userMessage update: describe the tool result as ingested into the agent's reasoning context, not merely 'into the agent'. The reasoning-context phrasing is the term of art used throughout the spec for the boundary that hooks gate, so the two halves of the tool-call lifecycle now describe their boundaries the same way.
Add a note at the top of the system/ping section stating that the method lives in the system/* namespace, not steps/*, and that it is not a hook in the enforcement sense. The audit and signature exemptions were already documented further down, but readers landing on this section first need the namespace distinction up front so they do not assume the steps/* contract applies.
The conditional 'When the deployment populates trust on the wire' clause in hooks.md restated specification.md §7.1's optional-trust extension contract inline with the v0.1 baseline payload contract. Readers landing on hooks.md without §7.1 context misread the MUST as a wire-format requirement, which it is not (trust is an OPTIONAL reserved enum, not a v0.1 baseline field). The monotonicity rule still applies via §7.1's general clause for agent_generated provenance, which already covers the summarization case. hooks.md now describes only the v0.1 baseline (origin + derived_from); extension semantics live in §7.1.
Every schema description that discussed the optional trust enum pointed readers to specification.md §3.7, but the trust enum is actually at §7.1 (and the channel-to-trust mapping at §7.2). Section numbering must have shifted at some point and the schema refs were left behind. Anyone clicking through would land nowhere. Update all 10 affected files (provenance.json, provenance-summary.json, response-envelope.json, inspect/format-mapping.json, trace/otel-mapping.json, trace/ocsf-mapping.json, and the four hook payloads that touch trust: post-compact, pre-compact, turn-end, subagent-stop) to point at §7.1.
There was a problem hiding this comment.
Pull request overview
Integrates the ACS canonical v0.1.0 specification into the docs site, including modular JSON Schemas, tiered conformance profiles, and major rewrites/additions across Instrument/Trace/Inspect documentation.
Changes:
- Added modular v0.1.0 JSON Schemas for envelopes, handshake, provenance, AgBOM, and hook payloads.
- Rewrote/expanded docs for Trace (OTel + OCSF), Inspect (AgBOM + format mappings), and conformance profiles.
- Added/updated concepts and supporting rationale/proposal documents; updated MkDocs navigation.
Reviewed changes
Copilot reviewed 69 out of 71 changed files in this pull request and generated 53 comments.
Show a summary per file
| File | Description |
|---|---|
| specification/v0.1.0/trace/otel-mapping.json | Adds normative ACS→OTel mapping schema/data |
| specification/v0.1.0/trace/ocsf-mapping.json | Adds normative ACS→OCSF mapping schema/data |
| specification/v0.1.0/response-envelope.json | Defines JSON-RPC response envelope schema |
| specification/v0.1.0/request-envelope.json | Defines JSON-RPC request envelope schema |
| specification/v0.1.0/provenance.json | Defines Provenance object schema |
| specification/v0.1.0/provenance-summary.json | Defines provenance summary schema |
| specification/v0.1.0/modifications.json | Defines modify/redaction payload schema |
| specification/v0.1.0/inspect/format-mapping.json | Documents AgBOM→CycloneDX/SPDX/SWID mapping rules |
| specification/v0.1.0/hooks/user-message.json | Adds steps/userMessage payload schema |
| specification/v0.1.0/hooks/turn-start.json | Adds steps/turnStart payload schema |
| specification/v0.1.0/hooks/turn-end.json | Adds steps/turnEnd payload schema |
| specification/v0.1.0/hooks/tool-call-result.json | Adds steps/toolCallResult payload schema |
| specification/v0.1.0/hooks/tool-call-request.json | Adds steps/toolCallRequest payload schema |
| specification/v0.1.0/hooks/system-ping.json | Adds system/ping payload schema |
| specification/v0.1.0/hooks/subagent-stop.json | Adds steps/subagentStop payload schema |
| specification/v0.1.0/hooks/subagent-start.json | Adds steps/subagentStart payload schema |
| specification/v0.1.0/hooks/skill-unload.json | Adds steps/skillUnload payload schema |
| specification/v0.1.0/hooks/skill-register.json | Adds steps/skillRegister payload schema |
| specification/v0.1.0/hooks/skill-load.json | Adds steps/skillLoad payload schema |
| specification/v0.1.0/hooks/session-start.json | Adds steps/sessionStart payload schema |
| specification/v0.1.0/hooks/session-end.json | Adds steps/sessionEnd payload schema |
| specification/v0.1.0/hooks/pre-compact.json | Adds steps/preCompact payload schema |
| specification/v0.1.0/hooks/post-compact.json | Adds steps/postCompact payload schema |
| specification/v0.1.0/hooks/memory-store.json | Adds steps/memoryStore payload schema |
| specification/v0.1.0/hooks/memory-context-retrieval.json | Adds steps/memoryContextRetrieval payload schema |
| specification/v0.1.0/hooks/knowledge-retrieval.json | Adds steps/knowledgeRetrieval payload schema |
| specification/v0.1.0/hooks/agent-trigger.json | Adds steps/agentTrigger payload schema |
| specification/v0.1.0/hooks/agent-response.json | Adds steps/agentResponse payload schema |
| specification/v0.1.0/hooks/agbom-snapshot.json | Adds agbom/snapshot payload schema |
| specification/v0.1.0/hooks/agbom-changed.json | Adds agbom/changed payload schema |
| specification/v0.1.0/handshake.json | Defines handshake ClientHello/ServerHello schemas |
| specification/v0.1.0/defer-details.json | Defines defer_details schema |
| specification/v0.1.0/context-entry.json | Defines SessionContext ContextEntry hashing schema |
| specification/v0.1.0/ask-details.json | Defines ask_details schema |
| specification/v0.1.0/agbom/document.json | Defines canonical AgBOM document schema |
| specification/v0.1.0/agbom/component.json | Defines canonical AgBOM component schema |
| specification/proposals/skill-lifecycle/README.md | Adds skill lifecycle threat-model/rationale doc |
| SPEC_REVIEW_PRINCIPLES.md | Adds spec review principles guidance |
| mkdocs.yml | Updates MkDocs nav to include Concepts + Conformance |
| docs/topics/core_concepts.md | Rewrites Core Concepts for v0.1.0 framing |
| docs/topics/ACS_in_action_example.md | Replaces ACS-in-action with canonical worked example |
| docs/spec/trace/extend_opentelemetry.md | Updates OTel extension guidance to v0.1.0 |
| docs/spec/trace/extend_ocsf.md | Updates OCSF extension guidance + examples to v0.1.0 |
| docs/spec/trace/events.md | Rewrites Trace Events page + tables |
| docs/spec/instrument/extend_mcp.md | Updates MCP wrapping/extension doc for v0.1.0 |
| docs/spec/instrument/a2a/hooks/stream_message_request.md | Updates A2A hook doc links/wording |
| docs/spec/instrument/a2a/hooks/set_task_push_notification_config_request.md | Updates A2A hook doc links/wording |
| docs/spec/instrument/a2a/hooks/send_message_request.md | Updates A2A hook doc links/wording |
| docs/spec/instrument/a2a/hooks/resubscribe_to_task_request.md | Updates A2A hook doc links/wording |
| docs/spec/instrument/a2a/hooks/get_task_request.md | Updates A2A hook doc links/wording |
| docs/spec/instrument/a2a/hooks/get_task_push_notification_config_request.md | Updates A2A hook doc links/wording |
| docs/spec/instrument/a2a/hooks/cancel_task_request.md | Updates A2A hook doc links/wording |
| docs/spec/inspect/README.md | Rewrites Inspect/AgBOM overview for v0.1.0 |
| docs/spec/inspect/extend_swid.md | Adds SWID derivation guidance |
| docs/spec/inspect/extend_spdx.md | Adds SPDX derivation guidance |
| docs/spec/inspect/extend_cyclonedx.md | Adds CycloneDX derivation guidance + example |
| docs/spec/conformance.md | Adds tiered conformance profiles page |
| docs/concepts/trust.md | Adds Trust basis concept page |
| docs/concepts/skill.md | Adds Skill concept page |
| docs/concepts/session-lifecycle.md | Adds Session lifecycle concept page |
| docs/concepts/README.md | Adds Concepts index page |
| docs/concepts/provenance.md | Adds Provenance concept page |
| docs/concepts/intent.md | Adds Intent concept page |
| docs/concepts/identity.md | Adds Identity concept page |
| docs/concepts/capability.md | Adds Capability concept page |
| docs/concepts/agents.md | Adds Agents concept page |
| docs/acs.md | Updates landing page for v0.1.0 features/profiles |
| .gitignore | Ignores MkDocs site/ output and references/ |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…review
Clear-cut consistency fixes; no semantic changes to the spec.
- concepts/provenance.md: correct the origin enum to match
provenance.json (user_input, system, tool_output, retrieved,
agent_generated, a2a_inbound, external). Drops the invalid
"memory" value and restores a2a_inbound/external.
- trace/events.md: resync three rows to the normative
otel-mapping.json. Mark acs.tool.provider and acs.duration_ms
optional; split preCompact/postCompact (correct optional key is
acs.compact.lineage_depth_after); split agbom snapshot
(component_count) from changed (change_reason).
- inspect/extend_cyclonedx.md: registration_provenance.origin
"configuration" is not a valid origin; use "system" per
component.json guidance.
- topics/ACS_in_action_example.md: make the worked request valid
against the schemas: tool is an object {name}, session_id is a
UUID, add capability; remove the stale per-field trust values
(trust is not a v0.1 wire field).
- a2a/hooks/send_message_request.md: fix "Reponse" heading and
"an response" wording.
…Provenance PR Agent-Control-Standard#2 review (Copilot) flagged that the data-bearing hook schemas mark provenance as required, which contradicts the documented design: ACS-Core does not require field-level Provenance, and a pure-IBAC deployment must emit toolCallRequest (the IBAC enforcement gate) without it. As written, the schemas made ACS-Core non-implementable and provenance_producer: none non-conformant. WHY this shape (not "just make it optional") Provenance is the substrate FIDES, CaMeL, and AARM read; freely omittable provenance reintroduces the silent-degradation / laundering risk that Specification section 7.1 warns against. So optionality is whole-session, governed by the handshake's provenance_producer, not a per-field choice at emit time: - deterministic producer -> provenance on every data-bearing field; all- or-nothing per session. This is the ACS-Provenance conformance bar. - none producer -> no Provenance; a Guardian whose policy needs it refuses the session at handshake. The presence rule is cross-document (payload shape depends on the handshake), which a single JSON Schema cannot express. Rather than rely on prose alone, the obligation is made machine-checkable with a strict schema variant per hook. WHAT - Base hook schemas (userMessage, agentResponse, toolCallRequest, toolCallResult, knowledgeRetrieval, memoryStore, memoryContextRetrieval): provenance dropped from the required set so ACS-Core payloads validate. Each provenance property documents that it is required under provenance_producer: deterministic. - New strict overlays *.acs-provenance.json (one per data-bearing hook): allOf-ref the base and restore provenance to required. ACS-Provenance deployments validate against these; ACS-Core validates against the base. - specification.md section 7: replace the per-field-optional wording with the session-level rule (deterministic = all fields; none = none; refuse-at-handshake), and point ACS-Provenance at the strict variants. - conformance.md: ACS-Core line clarified (base schemas, provenance optional); ACS-Provenance now states the all-fields obligation and the strict-variant validation target. Verified: base schemas accept payloads with and without provenance; strict variants reject provenance-free payloads and accept populated ones.
The OCSF class table omitted steps/agentTrigger, which the normative ocsf-mapping.json maps to 6002 Application Activity (and which the OTel table already lists). Surfaced by a full events.md vs mapping audit following the PR Agent-Control-Standard#2 review.
… review) JSON-RPC 2.0 requires the response id to be null when the request id cannot be determined (parse error / invalid request). The response envelope forbade null, making correct error envelopes non-compliant. Allow null on id, but constrain it to the error branch: a successful (result) response still MUST echo the original non-null id.
|
Thanks @rocklambros for requesting a GH copilot, it gave a useful review. I addressed the doc/schema consistency gaps; the provenance cluster needed a design decision rather than the suggested edit, so I want to explain that one in full. I'll address the 53 comments grouped into 9 distinct findings: Provenance required on hook payloads (7 comments:
|
|
Thanks @afogel i'll take a closer...human look this afternoon when I get back in front of my laptop and approve to merge if it looks good. |
rocklambros
left a comment
There was a problem hiding this comment.
Hey @afogel — sat with this for a while and did an adversarial pass. Six things below I'd want to land before we tag v0.1.0. None of them are dealbreakers for the direction, more places where a conformant implementation could end up doing the thing the standard is meant to prevent, or two implementations could end up not interoperating. Happy to take any of these as follow-up PRs if you'd rather merge this and iterate. I've also opened separate issues for the things I think can wait.
Adds section 17.1, a registry mapping each mandated refusal to a fixed code in the -32000..-32099 ACS range, and cites those codes at the refusal sites that previously returned none. WHY: section 17 reserved the ACS error range and named three errors only as examples, with no table binding names to codes. Meanwhile section 4 (version mismatch, provenance-required), section 10.2 (signature failure), and section 10.3 (replay) mandate refusals without saying which code to return. An Observed Agent that receives -32000 cannot tell whether to retry, fall back to an older spec version, regenerate request_id, or give up, so no SDK can write portable error recovery. Each refusal now maps to one code, so an SDK branches on the code without parsing prose: -32000 SESSION_REFUSED residual policy refusal -32001 UNSUPPORTED_VERSION no common acs_version (section 4) -32002 PROVENANCE_REQUIRED provenance required, producer=none (4, 7) -32003 CAPABILITY_NOT_NEGOTIATED method or profile not negotiated (4) -32004 SIGNATURE_INVALID signature missing, malformed, or unverifiable (10) -32005 REPLAY_DETECTED duplicate request_id or nonce (10.3) -32006 TIMESTAMP_OUT_OF_WINDOW timestamp outside the skew window (10.3) -32007 CHAIN_MISMATCH chain_hash disagrees with the Guardian head (8) One code per reason, rather than a single SESSION_REFUSED catchall with the reason in a data field, so an SDK can branch on the code alone. SESSION_REFUSED stays as the residual for policy refusals that have no more specific code. An error response MAY carry a data object with a machine-readable reason, a human-readable message, and per-code fields (supported_versions, method, skew_window_ms). Sections 4, 10.2, and 10.3 now cite their codes. MODIFY_FIELDS_CONFLICT is held until the modify-precedence decision lands, since a precedence rule may remove the need for a conflict error. Addresses the PR review comment on docs/spec/instrument/specification.md:371.
Adds skew_window_ms to the handshake ServerHello and points section 10.3 at it. WHY: section 10.3 requires Guardians to reject requests whose timestamp is outside "the handshake-negotiated skew window," but no such field existed in handshake.json. timeout_config covers response timeouts, not request-timestamp skew. So conformant Guardians could not meet their own MUST: each vendor would pick a different default, and an Observed Agent with clock drift would be accepted by some Guardians and rejected by others with no way to discover the threshold. Replay protection is part of ACS-Core, so this was a baseline-level inconsistency. skew_window_ms is optional, in milliseconds to match default_ms, with a RECOMMENDED default of 300000 (5 minutes); deployments with well-synchronized clocks SHOULD tighten it. A skew violation is rejected with TIMESTAMP_OUT_OF_WINDOW (section 17.1), which echoes the window in data.skew_window_ms. The field is chosen over "deployment-configured" wording so the Observed Agent can read the window for clock-drift diagnostics and feed it to Trace events. Addresses the PR review comment on docs/spec/instrument/specification.md:278.
…eview)
Replaces the unconstrained modifications object with two mutually
exclusive shapes and adds Specification 6.3.
WHY: modifications allowed modified_content, redactions, and
parameter_overrides in any combination (minProperties 1, no oneOf, no
order rule), and MODIFY is a mandatory disposition. With no rule, two
conformant frameworks could apply the same Guardian decision in
different orders and produce different effective tool calls, a
security-relevant divergence: a redaction and an override targeting the
same field resolve differently depending on which runs last. The
Guardian that issued the decision could not predict its own effect,
because the framework that applies the decision is usually a different
vendor than the one that made it.
The fix removes the conflict rather than resolving it by a fixed order.
A fixed apply-order would force every overlap to pick a winner, and
either choice fails: applying overrides last can re-expose a field a
redaction removed, and applying redactions last lets arbitrary
replacement text overwrite a value an override deliberately sanitized.
Instead:
- modified_content is exclusive. It replaces the whole payload and
cannot combine with the path-based edits, which have nothing to
address inside an opaque replacement string.
- redactions and parameter_overrides may combine only on disjoint
targets. Disjoint edits commute, so the effective payload is
order-independent and no apply-order rule is needed.
- A Guardian MUST NOT emit a violating object. A framework that
receives one fails closed to DENY and records an audit event,
because it cannot determine the Guardian's intent.
This is the conservative shape. It is forward-compatible: overlapping
composition, or a Guardian-declared order, can be added in a later
version without a wire break, and it mints no new trust label on the
wire. No MODIFY_FIELDS_CONFLICT error code is added: an overlapping
decision is a Guardian-side validity error handled by fail-closed, not
a wire error the agent returns to the Guardian. The provenance of a
value introduced by parameter_overrides (its origin, and whether a
Guardian may raise the trust of data it rewrites) is deferred to a
later version alongside the audit-chain integrity work, since
tamper-evidence is what makes a Guardian trust-raise auditable.
The schema oneOf enforces exclusivity and the non-empty rule; the
disjoint-target rule is normative in Specification 6.3 because path
disjointness is not expressible in JSON Schema.
Addresses the PR review comment on
specification/v0.1.0/modifications.json:7.
Adds a normative "Signed input" rule to Specification 10 and repoints the two references that pointed at an undefined "same canonical input." WHY: Section 10 defined the signature envelope and the hybrid value encoding, and the hybrid rule said verifiers must verify "over the same canonical input," but the canonical input was never defined anywhere. The Signature object carried no signed_fields or canonicalization pointer either. With the input undefined, a producer that signed only an inner payload (for example toolCallResult.outputs) produced a signature an attacker could lift into a forged envelope with a different session_id or request_id; verification still passed because the signature never committed to the envelope. This is a cross-context forgery, worst under the HMAC-SHA256 baseline. The signed input is now fixed: the RFC 8785 (JCS) canonicalization of the envelope with the signature field removed, UTF-8 encoded. It binds the signature to method, metadata.session_id, request_id, and timestamp, so a captured signature cannot be re-wrapped. Verifiers MUST recompute this form and MUST reject a signature that does not cover it (SIGNATURE_INVALID). This matches the JCS rule already used for the audit chain in Section 8. The input is fixed by the rule rather than declared per message. A discoverable signed_fields field was considered and rejected: once the input is normatively fixed, signed_fields would only let a producer advertise partial coverage that omits session_id, reopening the gap the rule closes, so it is the wrong direction. Addresses the PR review comment on docs/spec/instrument/specification.md:274.
…review)
Adds chain_hash and a signature to the response envelope and a normative
Specification 8.6, so the audit chain is tamper-evident to an outside
party rather than purely Guardian-internal.
WHY: SessionContext and its rolling chain_hash lived only on the
Guardian, and the response envelope had no chain_hash and no signature
field at all. A Guardian could drop a ContextEntry (for example a DENY),
recompute the chain forward, and present a clean history later, with no
on-wire signal of the rewrite. Calling that an audit chain implied a
tamper-evidence the wire never delivered.
Changes:
- response-envelope.json: AcsResult gains chain_hash (lowercase-hex
SHA-256 head) and signature (referencing the request envelope's
Signature definition, computed on the canonical input from
Specification 10).
- Specification 8.6 (normative): for every step where the Guardian
writes a ContextEntry (the content-bearing steps), the Guardian MUST
include the resulting chain_hash in its response, covered by the
response signature. Publishing the head as each decision is made
lets an observer that records traffic detect a later rewrite.
- CHAIN_MISMATCH is exposed two ways for two detectors: a deny
reason_code 'chain_mismatch' when the Guardian can still return a
decision, and the existing error code -32007 when it cannot.
'chain_mismatch' is added to the reason_codes vocabulary.
Scope, stated honestly: tamper-evidence is bounded by the signature in
use. Under the HMAC baseline a published, signed head gives integrity
and lets a key-holder verify the chain, but does not bind the Guardian
itself, which holds the symmetric key and can re-sign a rewritten head.
Non-repudiation, proving to a third party that a specific Guardian
issued a specific head, requires the asymmetric ACS-Crypto profile. The
baseline catches accidental divergence and cross-Guardian disagreement;
defeating a compromised Guardian is a profile-level guarantee, not Core.
Reviewed the AARM reference architecture (arXiv 2602.09433) for
conflicts: AARM only recommends hash-chaining and lists non-repudiation
as an open problem, so a signed, published, hash-chained head exceeds
its accountability requirements and conflicts with none of them.
Addresses the PR review comment on
docs/spec/instrument/specification.md:177.
…(PR review)
Promotes two guarantees into the ACS-Core baseline and rewrites the
conformance and landing-page wording so the badge matches what Core
actually delivers.
WHY: the cover page promised ACS makes agents inspectable, traceable,
and instrumentable, but ACS-Core required none of Trace, AgBOM,
provenance, or signatures, and no rule forced the Observed Agent to wait
for or honor a decision. A vendor could ship "ACS-Core conformant" and
deliver none of the three, since every hook could fire and be ignored.
Core now requires two wire-observable guarantees, neither of which
touches the Guardian's policy logic:
- Baseline integrity: every request and response is signed over the
Section 10 canonical input. HMAC-SHA256 is the baseline; its
per-session key is HKDF-derived from deployment-provided input
keying material (a pre-shared secret or a transport channel binding)
and the session_id. No in-band key-exchange is defined in v0.1; a
deployment with no shared secret or secure transport uses an
ACS-Crypto algorithm instead. Asymmetric and PQC stay ACS-Crypto.
- Decision honoring (Section 6.4): the Observed Agent MUST wait for
the decision up to the negotiated timeout and apply it. On timeout
it applies the on_timeout posture, default proceed (fail-open) so
ACS cannot take production down, and a fail-open proceed MUST be
recorded as an audit event so the bypass is visible. A decision that
arrives in time MUST be honored regardless of posture. on_timeout is
added to the handshake ServerHello.
We did NOT add the proposed "Guardians MUST evaluate rather than
blanket-allow" rule. It dictates vendor implementation and is not
checkable from the wire; a permissive policy is a legitimate deployment
choice. Badge integrity comes from the two mechanical guarantees plus
honest wording, not an unprovable diligence mandate.
The conformance text and acs.md cover are rewritten to be precise.
"ACS-Core conformant" means the channel is authenticated and the agent
honors decisions. It does not claim the deployment is secure, that
policies are strict, or that the audit chain is tamper-evident against a
compromised Guardian: the HMAC baseline is symmetric, so non-repudiation
and tamper-evidence against the Guardian are the ACS-Crypto and
ACS-Audit profiles. Trace and Inspect stay profiles, so the cover now
gates the traceable and inspectable claims on them.
Addresses the PR review comment on docs/spec/conformance.md:26.
…roduction Review question: if ACS is adopted as vendor middleware enforcing control points via hooks, where does the spec fail closed when the vendor's service goes down? Four gaps, each with its design decision: 1. Unify timeout, transport failure, and error-without-decision into one "decision failure" condition (Section 6.4). The old text was normative only "on timeout", but a vendor outage usually presents as a fast failure (connection refused, 503, JSON-RPC -32603), not a timeout. A conformant implementation could read those opposite ways: fail closed (no decision means no proceed) or fail open without the mandated audit event. One condition, one posture, one audit rule removes the divergence. Registry errors (17.1) keep their recovery actions within the timeout budget; unambiguous failures may resolve immediately so fail-open adds no latency. 2. Rename on_timeout -> on_decision_failure. The field now governs more than timeouts, and a field named for one failure mode invites implementers to scope it back down. Renaming is cheap pre-release; it will not be after v0.1.0 ships. 3. Keep fail-open (proceed) as the default, with mandatory audit. Inline enforcement ecosystems learned this the hard way: K8s admission webhooks default failurePolicy Fail and a webhook outage has locked entire clusters. A control standard that halts production on vendor outage will be ripped out, not tightened. The trade is stated in the spec text - an adversary who can disrupt the channel converts control into audit - so fail-closed is an informed opt-in, not a buried one. 4. Add a startup posture for handshake failure (Section 4.1). on_decision_failure is negotiated in the handshake, so it cannot govern a handshake that never completes - a bootstrap circularity the spec previously left silent. Default proceed matches the mid-session posture so one outage story covers both. A Guardian refusal is a decision, not a failure, and is excluded. Unguarded sessions must be recorded locally since no Guardian can receive the event. Mid-flight Guardian attachment is explicitly deferred to v0.2 rather than left undefined by omission. Two clarifications ride along: the timeout_decision deny default is declared deliberately opposite to on_decision_failure proceed (an expired DEFER is a Guardian that flagged a concern and vanished, not one that never answered, so a degraded Guardian fails closed where a silent one fails open); and ping's signature exemption means liveness cannot prove the signed hook path is healthy (a key-resolution outage fails every hook while ping stays green), so deployments should monitor decision failures directly. Per-method timeout budgets get a SHOULD because every millisecond of timeout is worst-case latency on the hot path and the protocol sets no ceiling.
Summary
Integrates the canonical v0.1.0 spec into this docs site. The canonical materials introduce a tiered conformance model, eight new lifecycle hooks, a normative provenance + SessionContext + Intent system, a crypto-agile signature registry, and OTel/OCSF/CycloneDX/SPDX/SWID mapping tables — all of which were either missing or sketched-only in the prior content.
What's new
specification/v0.1.0/: 9 top-level objects (envelopes, handshake, provenance, context-entry, ask/defer/modifications), 19 per-hook payloads, AgBOM canonical schema, Inspect format-mapping, OTel + OCSF mappings. The legacyspecification/ACS/acs_schema.jsonis now a Draft 2020-12 aggregator manifest cross-referencing all modular schemas.docs/spec/conformance.md): ACS-Core mandatory baseline + ACS-Trace, ACS-Inspect, ACS-Inspect-Dynamic, ACS-Provenance, ACS-Crypto, ACS-Audit, with handshake-based declaration.specification.md,hooks.md): JSON-RPC 2.0 envelope with HTTP+stdio transports,handshake/hellocapability negotiation, full disposition vocabulary including DEFER, decision-result fields supporting paradigm composition (FIDES / CaMeL / AARM / IBAC), provenance with optional trust enum and default channel-to-trust mapping, SessionContext with normative chain hashing (RFC 8785 JCS + SHA-256), Intent immutability and intent-extension via ASK, crypto-agile signature registry (HMAC-SHA256 baseline; ECDSA, RSA-PSS, ML-DSA-44/65/87, SLH-DSA-128s/f, hybrids), replay protection, OPA reference policy-engine binding, and the new lifecycle hooks (sessionStart/End, turnStart/End, pre/postCompact, subagentStart/Stop, system/ping).docs/spec/trace/): OpenTelemetry semconv mapping withdecision-as-span-eventrule, OCSF event-class mapping (Authentication 3002, Application Activity 6002, Process Activity 1007, Datastore Activity 6005, Detection Finding 2004 for non-allowdecisions, Inventory Info 5001 for AgBOM), severity_id mapping, ACS-Trace conformance bar.docs/spec/inspect/): canonical AgBOM with seven component types,agbom/snapshotandagbom/changedwire methods, deterministic CycloneDX 1.6 / SPDX 3.0 / SWID derivations.core_concepts.mdupdated to introduce the v0.1.0 vocabulary;ACS_in_action_example.mdreplaced with the canonical Appendix C worked example (toolCallRequest with argument-level provenance, IBAC + FIDES decision envelope).docs/references/withhook_surfaces.md(lifecycle-hook landscape across coding agents) andstandards_compatibility.md(CaMeL/FIDES/AARM/IBAC + Conseca, ControlValve, MELON, AgentSentry, LlamaFirewall, PROV-AGENT/Flowcept comparison). IBAC paper added underdocs/assets/.Commits (in order)
Test plan
uv run mkdocs build --strictsucceeds with no warnings (only an informational notice that the existing A2A hook example pages aren't in the nav, matching pre-PR behavior — they're linked fromextend_a2a.md).uv run mkdocs serveand click through the new Specification > Conformance Profiles section, the rewritten Instrument > Specification + Hooks pages, the refreshed Trace + Inspect pages, and the new References section.