conformance: every object position declares its own closure - #9
Merged
Merged
Conversation
… censused
Absence is not a disposition. `provenance[].source` and
`attribution.sketch_params` are byte-identical `{"type": "object"}` in the
shipped metalog schema and mean opposite things: the first is a standard object
whose members §12.4 names, the second a map whose keys are data. No property of
the schema text separates them — only the prose does. So a census of what is
open cannot be maintained by reading `additionalProperties`, and a hand-kept
list of exemptions beside the schemas would rot on the next release. Enforce the
rule instead and derive the position set from the artifacts.
--selftest now walks every object position in both schemas, after the $defs
mirror check and before any fixture runs, and requires each to declare one of
three dispositions: `false`, a CONSTRAINING value schema (a map — closed over
its values, never over its key set), or `{"description": "<why it is open>"}`.
Bare `true` is refused because it is the one spelling with nowhere to put the
reason; accepting a node-level `description` in its place would have passed both
document roots vacuously, on sentences describing the document TYPE ("Pair-wise
difference between two MetaLog documents") that say nothing about openness — a
check going green on the two positions it exists to interrogate. Exit 2, like a
$defs drift: the standard's own artifacts fail to say what they mean, and no
verdict about anyone's documents is honest underneath that.
In-place applicators are not positions and the exclusion is load-bearing:
`additionalProperties: false` inside an `if` changes the condition and inside a
`then` closes the object, so demanding a declaration there would order an author
to break his own schema. Nine such fragments carry `required`/`properties` and no
`type` here; a walk blind to the distinction censuses 58 instead of 49 and reds
on all nine.
Run against the shipped v0.9.0 pair it reds at 7 of 49 — the two absences above,
and five positions open with no reason attached (both roots, `provenance[].window`,
the diff's `current.window` and `previous.window`). All seven now declare, and
NOTHING closes: `{"description": ...}` and `true` are the same schema in Draft
2020-12 and an absent `additionalProperties` already meant open, so no conformant
document changes validity. `sketch_params` declares that it is a map — §6 makes it
REQUIRED and its parameter set depend on `sketch_type`, so `false` there would
admit only the empty object and invalidate every document carrying `attribution`.
Coupled, because landing the walk alone would have shipped two defects with it.
The undescribed walker decided "unconstrained by design" from the ABSENCE of
every object keyword, so writing an openness down instead of leaving it absent
armed the walker against the author: measured, spelling those two positions open
produced six invented findings on a fully conformant document while
`additionalProperties: {}` — the same schema — produced none. The mirror image was
live too: any non-empty value schema was read as DESCRIBING the extras, so moving
the roots from `true` to `{"description": ...}` would have silenced the
legal-but-undescribed species at both of them. The walker now reads what a
declaration means rather than whether a keyword is spelled out. Both halves are
mutation-measured: reverting the first reds valid/rich.metalog.jsonl with the six
findings; reverting the second reds undescribed/open_containers.metalog.jsonl and
invalid/unprefixed_extension_key.diff.json by going QUIET, which is the worse
direction.
And the control that should have caught this was blind. valid/rich.metalog.jsonl
claimed to exercise "sketch-shaped free objects" while carrying no `attribution`
block at all, and its `provenance[]` entry carried no `source` — so the two
positions this change had to rule on were the two the undescribed-false-positive
control could not see. It now carries §6's three sketch parameters and §12.4's
service/host beside a fleet.
Six live sites claimed the roots are spelled `additionalProperties: true` and are
repaired to say they are OPEN, which is what was ever meant and stays true;
0.8.0's CHANGELOG entry is left alone because it is a past-tense record of a state
that really was that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Change class: Editorial + conformance tooling (
GOVERNANCE.md§2). No conformant document changes validity.{"description": ...}andtrueare the same schema in Draft 2020-12, and an absentadditionalPropertiesalready meant open — so nothing here closes anything, and a 0.9.0 producer stays legal.What it adds
--selftestwalks every object position in both shipped schemas — after the$defsmirror check, before any fixture runs — and requires each to declare one of three dispositions:additionalProperties: false{"description": "<why it is open>"}An absent
additionalPropertiesis a defect, because absence is not a disposition — it is the lack of one.provenance[].sourceandattribution.sketch_paramsare byte-identical{"type": "object"}in the shipped metalog schema and mean opposite things: the first is a standard object whose members §12.4 names, the second a map whose keys are data. Nothing in the schema text separates them; only the prose does. So the census of what is open cannot be maintained by reading the schema, and a hand-kept list of exemptions beside it would rot on the next release. The position set is derived from the artifacts — a position added tomorrow is checked on arrival.Bare
trueis refused because it is the one spelling with nowhere to put the why. Accepting a node-leveldescriptioninstead would have passed both document roots vacuously, on sentences that describe the document type ("Pair-wise difference between two MetaLog documents") and say nothing about openness — a check going green on the two positions it exists to interrogate.Exit 2, like a
$defsdrift: the standard's own artifacts fail to say what they mean, and no verdict about anyone's documents is honest underneath that.First run on the shipped tree: RED at 7 of 49 positions
metalog #true, no reasonmetalog #/properties/provenance/items/properties/windowtrue, no reasonmetalog #/properties/attribution/properties/sketch_paramsmetalog #/properties/provenance/items/properties/sourcediff #true, no reasondiff #/properties/current/properties/windowtrue, no reasondiff #/properties/previous/properties/windowtrue, no reasonAll seven now declare, and nothing closes.
sketch_paramsdeclares that it is a map: §6 makes itrequiredand makes its parameter set depend onsketch_type, soadditionalProperties: falsethere would admit only the empty object and invalidate every document carryingattribution.provenance[].sourcedeclares that it is a standard object whose members are not declared at that position, so closing it bare would forbid theserviceandhostthat §12.4's own example carries.Two coupled repairs, because landing the walk alone would have shipped defects with it
The undescribed walker read the spelling, not the meaning. It decided "unconstrained by design" from the absence of every object keyword, so writing an openness down instead of leaving it absent armed the walker against the author. Measured: spelling those two positions open produced six invented findings on a fully conformant document, while
additionalProperties: {}— the same schema — produced none.The mirror image was live in the other direction: any non-empty value schema was treated as describing the extras, so moving the two roots from
trueto{"description": ...}would have silenced the legal-but-undescribed species at both of them.Both halves are mutation-measured:
valid/rich.metalog.jsonlreds with the six invented findingsundescribed/open_containers.metalog.jsonl+invalid/unprefixed_extension_key.diff.jsonred by going quietadditionalPropertiesfrom one position#/$defs/cube_celltruediff #In-place applicators are not positions, and the exclusion is load-bearing.
additionalProperties: falseinside anifchanges the condition; inside athenit closes the object. Demanding a declaration there would order an author to break his own schema.The control that should have caught this was blind
valid/rich.metalog.jsonlclaimed to exercise "sketch-shaped free objects" while carrying noattributionblock at all, and itsprovenance[]entry carried nosource. The two positions this change had to rule on were the two theundescribed-false-positivecontrol could not see. It now carries §6's three sketch parameters and §12.4'sservice/hostbeside afleet.Also repaired
Six live sites claimed the roots are spelled
additionalProperties: true; they now say the roots are open, which is what was ever meant and stays true.0.8.0's CHANGELOG entry is deliberately left alone — it is a past-tense record of a state that really was that.Verification
python3 conformance/metalog_validate.py --selftest→ 16/16, 5 controls armed, exit 0python3 conformance/metalog_validate.py --expect-documents 1 schema/metalog.v0.example.json→ CONFORMANT, exit 0Draft202012Validator.check_schema