Skip to content

docs: schema-validated documentation examples#359

Open
igrigorik wants to merge 1 commit intomainfrom
docs/example-validation
Open

docs: schema-validated documentation examples#359
igrigorik wants to merge 1 commit intomainfrom
docs/example-validation

Conversation

@igrigorik
Copy link
Copy Markdown
Contributor

Validation harness that ensures every JSON example in the spec docs is validated against UCP schemas. Catches documentation drift — when schemas change, stale examples break CI and docs builds.

Contract

Every json block requires an HTML comment annotation on the preceding line:

<!-- ucp:example schema=shopping/checkout op=read -->
<!-- ucp:example schema=shopping/checkout path=$.totals op=read -->
<!-- ucp:example schema=shopping/cart op=create direction=request -->
<!-- ucp:example skip reason="JSON-RPC transport binding" -->

Unannotated blocks are hard failures.

Core rule: "if you open it, you own it." When an example opens an object's braces, every required field (per the resolved schema for the declared op + direction) must be shown or acknowledged with an ellipsis. This applies recursively.

Three-tier value contract for annotated (non-skip) examples:

Tier Syntax Meaning
Full value Real JSON Validated against schema (types, constraints, enums)
Required field { ... }, [ ... ], "..." Field must exist; value not shown; scaffold fills it for validation
Absent Not in example Only permitted for optional schema fields

Pipeline

  1. Extract — find annotated ```json blocks (handles MkDocs tab indentation)
  2. Unwrap — strip HTTP headers from request/response examples
  3. Expand — substitute {{ ucp_version }} with valid date
  4. Preprocess — convert bare ... to valid JSON ({ ... }{"...":"..."})
  5. Coverage — walk resolved schema, verify required fields acknowledged
  6. Strip — remove ellipsis markers
  7. Merge — deep-merge into scaffold (example wins, scaffold fills gaps)
  8. Validateucp-schema validate on merged payload
  9. Report — map errors to source file:line

Bugs caught and fixed

  • checkout.md: item missing required price field in disclosure example
  • checkout.md: totals array missing required subtotal entry
  • checkout-rest.md: business outcome used non-schema available_quantity field on line_item; replaced with message-based pattern
  • discount.md (4 instances): quantity wrongly nested inside item object instead of on line_item — schema requires quantity at line_item level
  • discount.md: price and title shown in create requests where schema omits them (request-only fields: only item.id is valid)

Current state

python3 scripts/validate_examples.py --schema-base source/schemas/ — 52 passed, 0 failed, 0 errors

268 blocks across 39 files:

  • 52 validated — REST request/response pairs, core spec examples, error responses, extension examples
  • 216 skipped — transport bindings, schema definitions, configs (future: payload_path= for JSON-RPC unwrap)

🟢 Pausing fixing the rest (skipped) to get feedback: 🟢

  • Does this strategy make sense?
  • Anything we want to change or improve?

Type of change

  • Documentation update
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

  Introduce a validation harness that ensures every JSON example in the spec
  docs is either validated against UCP schemas or explicitly skipped. This
  catches documentation drift — when schemas change, stale examples break CI
  instead of silently misleading readers.

  ## Contract

  Every ```json block requires an annotation on the preceding line:

      <!-- ucp:example schema=shopping/checkout op=read -->
      <!-- ucp:example schema=shopping/checkout path=$.totals op=read -->
      <!-- ucp:example schema=shopping/cart op=create direction=request -->
      <!-- ucp:example skip reason="JSON-RPC transport binding" -->

  Unannotated blocks are hard failures.

  Principle: "if you open it, you own it." When an example opens an object's
  braces, every required field (per the resolved schema for the declared
  op + direction) must be shown or acknowledged. This applies recursively.

  Three-tier value contract for annotated (non-skip) examples:
  - Full value: validated against schema (types, constraints, enums)
  - Required fields ({ ... }, [ ... ], "..."): field must exists but
    values are optional; scaffold fills it for validation.
  - Absent fields only permitted for optional schema fields

  ## Pipeline

  1. Extract — find annotated ```json blocks (handles tab indentation)
  2. Unwrap — strip HTTP headers from request/response examples
  3. Expand — substitute {{ ucp_version }} with valid date
  4. Preprocess — convert bare ... to valid JSON ({ ... } → {"...":"..."})
  5. Coverage — walk resolved schema, verify required fields acknowledged
  6. Strip — remove ellipsis markers
  7. Merge — deep-merge into scaffold (example wins, scaffold fills gaps)
  8. Validate — ucp-schema validate on merged payload
  9. Report — map errors to source file:line

  ## Bugs caught and fixed

  - checkout.md: item missing required `price` field in disclosure example
  - checkout.md: totals array missing required `subtotal` entry
  - checkout-rest.md: business outcome used non-schema `available_quantity`
    field on line_item; replaced with message-based pattern
  - discount.md (4 instances): `quantity` wrongly nested inside `item`
    object instead of on `line_item` — schema requires quantity at line_item
    level
  - discount.md: `price` and `title` shown in create requests where schema
    omits them (request-only fields: only `item.id` is valid)

  ## Current state

  268 blocks across 39 files:
  - 52 validated (REST request/response pairs, core spec examples, error
    responses, extension examples)
  - 216 skipped (transport bindings, schema definitions, configs — future
    work: payload_path= for JSON-RPC unwrap)

  Run: python3 scripts/validate_examples.py --schema-base source/schemas/
@igrigorik igrigorik self-assigned this Apr 16, 2026
@igrigorik igrigorik added the TC review Ready for TC review label Apr 16, 2026
@igrigorik igrigorik requested review from a team as code owners April 16, 2026 05:28
Copy link
Copy Markdown
Collaborator

@drewolson-google drewolson-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, one small comment on empty request bodies.


=== "Request"

<!-- ucp:example skip reason="empty request body" -->
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.

Can we make the validator / annotation handle empty request bodies? This is a valid request, so having to skip it feels off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

TC review Ready for TC review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants