formats: JSON Schema dynamic references and the 2020-12 meta-schemas in std/json-schema (step 2b of #80) #567

Closed
opened 2026-10-29 06:35:51 +00:00 by orion · 7 comments
Collaborator

Context

Step 2b of the epic #80. Step 2a (#553) landed the static references; $dynamicRef, $dynamicAnchor, $vocabulary and a $ref to a meta-schema are still refused with a JsonSchemaError that names them. The 2020-12 meta-schemas are written with $dynamicRef, so the dynamic scope comes first and the bundled meta-schemas second. Needed by OpenAPI (#76) and by schema validation of schemas themselves.

Goal

std/json-schema evaluates $dynamicRef/$dynamicAnchor with the dynamic scope of 2020-12 (section 8.2.3.2 of the specification), reads $vocabulary from a meta-schema, and bundles the 2020-12 meta-schemas so that a $ref/$schema to them resolves without fetching.

Source

  • ROADMAP.md, format versions: one model for all versions, 2020-12 is the model.
  • Epic #80, step 2b; ENCODING.md section 9 (format families); the layout of std/json-schema from #434 and #553 (src/reference.trb, src/validate.trb, tests/suite.test.trb with its exclusions and fileExclusions).

Acceptance criteria

  • $dynamicAnchor is collected like $anchor and $dynamicRef resolves through the dynamic scope (outermost schema resource with a matching $dynamicAnchor wins; a plain $anchor target is a static $ref), with the cycle guard of 2a applied.
  • $vocabulary is read and kept on the model (required vs optional vocabularies); a required vocabulary the package does not know is refused with an error naming it.
  • The eight 2020-12 meta-schemas (schema, core, applicator, unevaluated, validation, meta-data, format-annotation, content) are bundled and registered under their https://json-schema.org/draft/2020-12/... ids in every default registry; $schema of 2020-12 and $ref to them no longer error. The metaschema keyword unevaluated is stored but not yet evaluated (step 3).
  • Suite files dynamicRef and vocabulary are in suite-cases.trb and pass; the seven meta-schema exclusions of 2a (anchor, defs, id x3, ref x2) are removed from exclusions() and their cases pass. The unevaluatedProperties exclusions stay (step 3), and dynamicRef cases that need unevaluated* are named exclusions with that reason.
  • Unit tests for dynamic scope across two resources and for a meta-schema validating a schema; the page docs/standard-library/json-schema.md documents the dynamic scope and the bundled meta-schemas and drops them from the "later step" list.
  • sh tools/gates.sh a green in forge CI, docs checks and skills/ regeneration done.

Design record point

ROADMAP "Which versions a format supports"; ENCODING.md section 9. Decided by the loop: the meta-schemas are stored as JsonValue text constants in a generated or hand-embedded src/metaschema.trb (vendored from the specification's published JSON, notice included) and pre-registered, so nothing is fetched; the evaluation state carries a stack of schema resources for the dynamic scope.

Seed impact

None: the compiler does not import std/json-schema and no std name is looked up by string.

Size

One PR, two Conventional Commits: (1) feat(std/json-schema): evaluate $dynamicRef and $dynamicAnchor with $vocabulary, the dynamicRef/vocabulary suite files and tests; (2) feat(std/json-schema): bundle the 2020-12 meta-schemas with the removed exclusions, docs and skill regeneration.

Dependencies

Needs #553 (landed). Part of #80; unblocks step 3 (unevaluated*) and #76.

## Context Step 2b of the epic #80. Step 2a (#553) landed the static references; `$dynamicRef`, `$dynamicAnchor`, `$vocabulary` and a `$ref` to a meta-schema are still refused with a `JsonSchemaError` that names them. The 2020-12 meta-schemas are written with `$dynamicRef`, so the dynamic scope comes first and the bundled meta-schemas second. Needed by OpenAPI (#76) and by schema validation of schemas themselves. ## Goal `std/json-schema` evaluates `$dynamicRef`/`$dynamicAnchor` with the dynamic scope of 2020-12 (section 8.2.3.2 of the specification), reads `$vocabulary` from a meta-schema, and bundles the 2020-12 meta-schemas so that a `$ref`/`$schema` to them resolves without fetching. ## Source - [ROADMAP.md, format versions](https://git.torb.dev/torbscript/language/src/branch/main/docs/ROADMAP.md): one model for all versions, 2020-12 is the model. - Epic #80, step 2b; ENCODING.md section 9 (format families); the layout of `std/json-schema` from #434 and #553 (`src/reference.trb`, `src/validate.trb`, `tests/suite.test.trb` with its `exclusions` and `fileExclusions`). ## Acceptance criteria - [ ] `$dynamicAnchor` is collected like `$anchor` and `$dynamicRef` resolves through the dynamic scope (outermost schema resource with a matching `$dynamicAnchor` wins; a plain `$anchor` target is a static `$ref`), with the cycle guard of 2a applied. - [ ] `$vocabulary` is read and kept on the model (required vs optional vocabularies); a required vocabulary the package does not know is refused with an error naming it. - [ ] The eight 2020-12 meta-schemas (schema, core, applicator, unevaluated, validation, meta-data, format-annotation, content) are bundled and registered under their `https://json-schema.org/draft/2020-12/...` ids in every default registry; `$schema` of 2020-12 and `$ref` to them no longer error. The metaschema keyword `unevaluated` is stored but not yet evaluated (step 3). - [ ] Suite files `dynamicRef` and `vocabulary` are in `suite-cases.trb` and pass; the seven `meta-schema` exclusions of 2a (`anchor`, `defs`, `id` x3, `ref` x2) are removed from `exclusions()` and their cases pass. The `unevaluatedProperties` exclusions stay (step 3), and `dynamicRef` cases that need `unevaluated*` are named exclusions with that reason. - [ ] Unit tests for dynamic scope across two resources and for a meta-schema validating a schema; the page `docs/standard-library/json-schema.md` documents the dynamic scope and the bundled meta-schemas and drops them from the "later step" list. - [ ] `sh tools/gates.sh a` green in forge CI, docs checks and `skills/` regeneration done. ## Design record point ROADMAP "Which versions a format supports"; ENCODING.md section 9. Decided by the loop: the meta-schemas are stored as `JsonValue` text constants in a generated or hand-embedded `src/metaschema.trb` (vendored from the specification's published JSON, notice included) and pre-registered, so nothing is fetched; the evaluation state carries a stack of schema resources for the dynamic scope. ## Seed impact None: the compiler does not import `std/json-schema` and no std name is looked up by string. ## Size One PR, two Conventional Commits: (1) `feat(std/json-schema): evaluate $dynamicRef and $dynamicAnchor` with `$vocabulary`, the `dynamicRef`/`vocabulary` suite files and tests; (2) `feat(std/json-schema): bundle the 2020-12 meta-schemas` with the removed exclusions, docs and skill regeneration. ## Dependencies Needs #553 (landed). Part of #80; unblocks step 3 (`unevaluated*`) and #76.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261029 until 2026-10-29T12:46Z

[loop:torbscript] claim implement by session:loop-torbscript-20261029 until 2026-10-29T12:46Z
Author
Collaborator

[loop:torbscript] checkpoint: worktree code/wt/torbscript-language-567, branch loop/567-dynamic-ref (nothing pushed yet). Read the package and the suite files; design: scope stack of resource keys on Frame, dynamicAnchors in Index, meta-schemas parsed lazily per validate (only when a reference names them), $vocabulary restricts the root $schema keyword sets. Next: code, local VM test of one file, push, PR, CI.

[loop:torbscript] checkpoint: worktree code/wt/torbscript-language-567, branch loop/567-dynamic-ref (nothing pushed yet). Read the package and the suite files; design: scope stack of resource keys on Frame, dynamicAnchors in Index, meta-schemas parsed lazily per validate (only when a reference names them), $vocabulary restricts the root $schema keyword sets. Next: code, local VM test of one file, push, PR, CI.
Author
Collaborator

[loop:torbscript] checkpoint: PR #574 open (2 commits), review requested from argus, waiting for CI.

[loop:torbscript] checkpoint: PR https://git.torb.dev/torbscript/language/pulls/574 open (2 commits), review requested from argus, waiting for CI.
Author
Collaborator

[loop:torbscript] release implement: worker session ended after 1 h with PR #574 open (review requested from argus), next: waiting (forge CI and merge gate on #574)

[loop:torbscript] release implement: worker session ended after 1 h with PR #574 open (review requested from argus), next: waiting (forge CI and merge gate on #574)
Author
Collaborator

[loop:torbscript] claim fix by session:loop-torbscript-20261029 until 2026-10-29T14:25Z

[loop:torbscript] claim fix by session:loop-torbscript-20261029 until 2026-10-29T14:25Z
Author
Collaborator

[loop:torbscript] review: forge CI of fd671bd0 was red in tier A (linux-x64) only on three docs gates (docs check, docs index --check, docs declarations --check): the std/json-schema summary was 289 characters (limit 240), the standard-library index did not carry the new summary, and the JsonSchema declaration block lacked dynamicAnchor, dynamicReference and vocabulary. Fixed in 4c5f052f (docs(std/json-schema): shorten the summary, regenerate the index and declarations, docs and skills copies, normal push, no rebase needed, check-commits clean). Forge CI on 4c5f052f is queued behind a congested runner pool (16 jobs pending after ~85 min, none started yet), next: waiting (forge CI and merge gate); review from argus is requested once CI is green. Worker usage: about 90k tokens (sonnet).

[loop:torbscript] review: forge CI of fd671bd0 was red in `tier A (linux-x64)` only on three docs gates (`docs check`, `docs index --check`, `docs declarations --check`): the `std/json-schema` summary was 289 characters (limit 240), the standard-library index did not carry the new summary, and the `JsonSchema` declaration block lacked `dynamicAnchor`, `dynamicReference` and `vocabulary`. Fixed in 4c5f052f (`docs(std/json-schema): shorten the summary, regenerate the index and declarations`, docs and skills copies, normal push, no rebase needed, check-commits clean). Forge CI on 4c5f052f is queued behind a congested runner pool (16 jobs pending after ~85 min, none started yet), next: waiting (forge CI and merge gate); review from argus is requested once CI is green. Worker usage: about 90k tokens (sonnet).
Author
Collaborator

[loop:torbscript] claim fix by session:loop-torbscript-20261029 until 2026-10-29T17:26Z

[loop:torbscript] claim fix by session:loop-torbscript-20261029 until 2026-10-29T17:26Z
argus closed this issue 2026-10-29 13:38:57 +00:00
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#567
No description provided.