Skip to content

docs(errors): clarify two-layer error model + add error-code lint#2595

Merged
bokelley merged 1 commit into
mainfrom
bokelley/error-envelope-taxonomy
Apr 20, 2026
Merged

docs(errors): clarify two-layer error model + add error-code lint#2595
bokelley merged 1 commit into
mainfrom
bokelley/error-envelope-taxonomy

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Closes #2587 and #2588 — the training-agent migration's error-shape findings.

Two-layer error model (#2587)

The issue framed this as "pick one envelope shape" (errors[] vs adcp_error). Investigating the current spec, these aren't competing shapes — they live at different layers and serve different purposes:

Layer Key When to populate
Task payload errors[] Task ran; payload reports issues (fatal or non-fatal warnings)
Transport envelope adcp_error Task failed; transport needs typed, extractable error signal

A fatal task failure SHOULD populate both layers. Non-fatal warnings populate only the payload.

The real gap was:

  1. The spec didn't explicitly say this. Implementers who read error-handling.mdx learned errors[]; implementers who read transport-errors.mdx learned adcp_error. Neither doc cross-referenced the other's shape.
  2. Four storyboards pinned assertions to the payload shape (check: field_present, path: "errors"). Agents that surface errors only via the transport envelope (conformant per transport-errors.mdx) failed those validators with no remediation path short of duplicating the error into a payload array.

Error-code taxonomy lint (#2588)

Every storyboard check: error_code reference is now validated against the canonical enum at static/schemas/source/enums/error-code.json. scripts/lint-error-codes.cjs walks every storyboard, extracts value: / allowed_values: under check: error_code, and fails the build if any code isn't in the enum.

Wired into the main npm test pipeline. Current storyboards pass clean.

Alias-file convention (static/schemas/source/enums/error-code-aliases.json) established for future renames — the file is created lazily on the first rename. Aliased codes pass the lint as warnings during the deprecation window.

What changed

  • docs/building/implementation/error-handling.mdx — new "Envelope vs. payload errors" subsection in Error Response Format. Table + JSON examples showing both MCP and A2A shapes.
  • docs/contributing/storyboard-authoring.md — new "Asserting on errors" section. Shows ✅ check: error_code vs ❌ check: field_present, path: "errors". Explains the lint and alias convention.
  • static/compliance/source/universal/storyboard-schema.yaml — spec for check: error_code semantics (shape-agnostic resolution from adcp_error.code or errors[0].code) + vocabulary rules.
  • static/compliance/source/universal/error-compliance.yaml — fixed 2 broken validators (validate_error_shape, validate_transport_binding) that pinned to payload shape.
  • static/compliance/source/protocols/media-buy/state-machine.yaml — fixed 2 broken validators (pause_canceled_buy, resume_canceled_buy) and tightened them with specific expected code INVALID_STATE.
  • scripts/lint-error-codes.cjs — new lint script.
  • package.json — new test:error-codes script, wired into main test.

Backwards compatibility

  • Docs clarification is additive. Existing conformant agents (those populating both layers on fatal failures) are unaffected.
  • Storyboard fixes only relax validators — agents that were passing continue to pass, and agents that were failing due to the shape pin will now pass.
  • Error-code lint is new but all current storyboards pass clean.
  • Alias-file is lazy (not created in this PR) — no runtime dependency.

Test plan

  • npm run test:error-codes — clean (0 errors, 1 warning for a pre-existing YAML parse issue in runner-output-contract.yaml)
  • npm run test:schemas — 483 schemas validate
  • npm run test:storyboard-scoping — 3 tests pass
  • npm run test:unit — 627 tests pass
  • npm run typecheck clean
  • npm run build:compliance clean
  • Follow-up: fix the pre-existing YAML issue in runner-output-contract.yaml (line 286 duplicate summary: key) so it's not perpetually skipped

Closes #2587, #2588.

🤖 Generated with Claude Code

Closes #2587, #2588.

Two-layer error model (#2587):
- adcp_error lives in the transport envelope (MCP structuredContent,
  A2A DataPart, JSON-RPC error.data). Signals a failed task.
- errors[] lives in the task payload. Carries per-field error arrays,
  can also carry non-fatal warnings on submitted/input-required tasks.
- Fatal task failures SHOULD populate both layers.

Storyboard validator fixes:
- Replaced 4 `check: field_present, path: "errors"` assertions with
  `check: error_code`. Shape-agnostic — resolves from either layer.
- error-compliance.yaml:validate_error_shape, validate_transport_binding.
- state-machine.yaml:pause_canceled_buy, resume_canceled_buy.

Error-code taxonomy lint (#2588):
- scripts/lint-error-codes.cjs walks all storyboards, validates every
  error_code reference against the canonical enum. Wired into npm test.
- Alias-file convention for future renames:
  static/schemas/source/enums/error-code-aliases.json (lazy).

Docs updates:
- error-handling.mdx: new "Envelope vs. payload errors" subsection.
- storyboard-authoring.md: new "Asserting on errors" section.
- storyboard-schema.yaml: spec for `check: error_code` semantics +
  error-code vocabulary rules.
@github-actions

Copy link
Copy Markdown
Contributor

Schema Link Check Results

Commit: 48709a2 - docs(errors): clarify two-layer error model + add error-code lint

⚠️ Warnings (schema not yet released)

These schemas exist in source but haven't been released yet. The links will be broken until the next version is published:

  • https://adcontextprotocol.org/schemas/v3/enums/specialism.json
    • Schema exists in latest (source) but not yet released in v3
    • Action: This link will work after next 3.x release is published

To fix: Either:

  1. Wait for the next release and merge this PR after the release is published
  2. Use latest instead of a version alias if you need the link to work immediately (note: latest is the development version and may change)
  3. Coordinate with maintainers to cut a new release before merging

@bokelley bokelley merged commit 11b599e into main Apr 20, 2026
16 checks passed
bokelley added a commit that referenced this pull request Apr 20, 2026
…template, narrative drift (#2596)

* docs(errors): follow-up to #2595 — transport-errors cross-ref, alias template, narrative drift

Addresses protocol-review feedback on the merged error-model PR #2595:

1. transport-errors.mdx — add "Envelope vs. payload errors" section
   cross-linking to the normative two-layer model. Previously the
   normative text only lived in error-handling.mdx; readers who landed
   on transport-errors had no pointer.

2. transport-errors.mdx — add "Storyboard check: error_code contract"
   section. Promotes the shape-agnostic extraction contract from a
   storyboard-schema.yaml comment to spec-grade text so @adcp/client
   implementers can find it without grepping compliance source.

3. transport-errors.mdx — new client-detection step 6 for payload.errors[0]
   as a fallback path, making the two-layer detection explicit in code.

4. state-machine.yaml — replace INVALID_STATE_TRANSITION → INVALID_STATE
   in narrative/expected prose (validator assertions were already
   INVALID_STATE; the prose was drifting).

5. comply-test-controller.mdx — add a Note clarifying the controller
   response enum (INVALID_TRANSITION/INVALID_STATE/NOT_FOUND/etc. per
   comply-test-controller-response.json) is controller-specific and
   distinct from the canonical seller error-code.json. The transition-
   vs-state distinction is meaningful at the state-machine primitive
   layer; storyboard assertions on controller responses use path:
   "error", not check: error_code.

6. static/schemas/source/enums/error-code-aliases.json — new template
   file with an empty aliases map and a self-describing JSON Schema.
   Documents the alias shape so future renames have a home without ad-
   hoc invention. tests/schema-validation.test.cjs updated to skip
   files whose $id ends in -aliases.json from the enum-values check.

All tests clean: schemas, examples, error-codes lint, unit, typecheck.

* refactor(lint): move alias registry out of schemas/, simplify to pure data

Reviewer concern on PR #2596: error-code-aliases.json was a hybrid
schema+instance file (JSON Schema declaring its own shape AND an
instance of itself, permitted by additionalProperties: true). Unusual,
and likely to confuse tooling that expects /schemas/ paths to be pure
schemas.

Fix: move to scripts/error-code-aliases.json — pure data, sits next
to the lint script that reads it. No JSON Schema wrapper; just
{"aliases": {}} with a $_comment$ documenting the shape.

Touches:
- scripts/error-code-aliases.json (new, pure data)
- scripts/lint-error-codes.cjs (ALIAS_PATH points at new location)
- static/schemas/source/enums/error-code-aliases.json (removed)
- tests/schema-validation.test.cjs (revert the `-aliases.json` skip —
  the file no longer sits under /schemas/, so the enum-values test
  doesn't need to exclude it)
- docs/contributing/storyboard-authoring.md (reference path updated)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spec: response envelope unification — errors[] vs adcp_error on failure responses

1 participant