Repository navigation
[Impl] A schema can't be derived from the tables yet: seven authoring rules and a CI lint would fix that #121
Description
What did you build?
A validator or schema
What you tried to express, and what happened
Postindustria (IAB Tech Lab member) generated its own schemas from the specification. A deterministic parser reads it, with no model calls. Outputs: JSON Schema 2020-12, rules with test vectors, and TypeScript, Python, and Go types. Inputs: main at 44dfaaf, and with the 14 open PRs, ours included. Generating them shows what the schema README asks implementers to report:
- Decisions: 45. In 39, the text leaves a point open; 37 of these ask the specification to state something, and say what. The remaining 6 are changes we propose, such as UUIDs for minted IDs.
- Example failures: The specification's examples fail the generated schemas, or break a generated MUST rule, in 55 places on
mainand 88 with the open PRs. Most follow the text but not a change we propose. Onmain, 50 do only that, 4 only disagree with the text, and 1 does both. With the open PRs, these are 78, 9, and 1. - Lint findings, by the authoring rule below that removes them. No rule removes a MUST statement naming no field or key: it needs conformance test vectors or binds a party.
| Finding | main |
With the open PRs |
|---|---|---|
| 1. Required column | 4 | 5 |
| 2. Object keys | 7 | 7 |
| 3. Option types | 6 | 5 |
| 4. Value lists | 5 | 9 |
| 5. Markers | 1 | 1 |
| 6. Paths | 4 | 6 |
| 7. Backticked names | 6 | 13 |
| MUST statements naming no field or key | 13 | 26 |
| Other | 0 | 1 |
Every item: What generating the schemas had to decide.
What you did instead
We recorded each decision; two implementers could decide them differently. To remove the lint findings, we propose seven authoring rules that CI enforces:
- Every field and key table has a Required column:
REQUIRED,OPTIONAL, or a condition, such as "whenbasisisspend". - Every Object field has a key table, or links one. No structure lives only in prose.
- A selectable or settable row names its option type, such as "
available[]of format IDs". - A value list is written once, as
`a` | `b`. Other mentions link to it, and an open list says that it is open. - Tables use only the markers § 3 defines.
- Paths in prose match the table rows:
pricing[].cost_method, notpricing.cost_method. - Every backticked name in normative text resolves to a row or a key, or is listed as an external reference, such as an AdCOM field.
One lint checks all seven, extending scripts/check_appendix.py in validate.yml. Existing findings go on an allowlist that only shrinks, so CI fails only a PR that adds one. We can contribute it as a PR.
Open questions for the working group, as suggestions:
- Should money be
{ amount, currency }, so that an amount never appears without its currency? The generated schemas keep what the text writes: a bare amount beside itscurrency. - Should each zoned value carry its IANA zone, as
{ instant, time_zone }or an RFC 9557 string, beyond the line itemtime_zonethat #123 adds? The generated schemas keep RFC 3339 instants. JSON Schema'sdate-timeformat doesn't accept RFC 9557's[zone]suffix.
Fields involved
Every field and key table, and prose, mostly in § 2 and § 3.
How blocking was it?
Workaround required, but it works
Before you submit
- I have read CONTRIBUTING.md and agree my contribution may be published under this repository's licence.
- This contains no confidential or commercially sensitive information — no real rate cards, deal terms, client names or inventory data.
Revised 2026-10-27: The issue reports what generating the schemas took, and asks about structured money and time. The open-PR input adds our three PRs to the 11 others.
Analyzed and generated with the help of Claude, fully reviewed by me.
Activity