docs(errors): clarify two-layer error model + add error-code lint#2595
Merged
Conversation
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.
Contributor
Schema Link Check ResultsCommit:
|
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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[]vsadcp_error). Investigating the current spec, these aren't competing shapes — they live at different layers and serve different purposes:errors[]adcp_errorA fatal task failure SHOULD populate both layers. Non-fatal warnings populate only the payload.
The real gap was:
error-handling.mdxlearnederrors[]; implementers who readtransport-errors.mdxlearnedadcp_error. Neither doc cross-referenced the other's shape.check: field_present, path: "errors"). Agents that surface errors only via the transport envelope (conformant pertransport-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_codereference is now validated against the canonical enum atstatic/schemas/source/enums/error-code.json.scripts/lint-error-codes.cjswalks every storyboard, extractsvalue:/allowed_values:undercheck: error_code, and fails the build if any code isn't in the enum.Wired into the main
npm testpipeline. 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_codevs ❌check: field_present, path: "errors". Explains the lint and alias convention.static/compliance/source/universal/storyboard-schema.yaml— spec forcheck: error_codesemantics (shape-agnostic resolution fromadcp_error.codeorerrors[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 codeINVALID_STATE.scripts/lint-error-codes.cjs— new lint script.package.json— newtest:error-codesscript, wired into maintest.Backwards compatibility
Test plan
npm run test:error-codes— clean (0 errors, 1 warning for a pre-existing YAML parse issue inrunner-output-contract.yaml)npm run test:schemas— 483 schemas validatenpm run test:storyboard-scoping— 3 tests passnpm run test:unit— 627 tests passnpm run typecheckcleannpm run build:compliancecleanrunner-output-contract.yaml(line 286 duplicatesummary:key) so it's not perpetually skippedCloses #2587, #2588.
🤖 Generated with Claude Code