Resolves: #6, #8, #12, #23
Composes with: #19
The bug
spatial: lets you store the same fact in two slots. Array level today:
{
"node_type": "array",
"attributes": {
"spatial:shape": [1024, 1024],
"spatial:transform": [10.0, 0.0, 500000.0, 0.0, -10.0, 5000000.0],
"spatial:bbox": [500000.0, 4990000.0, 510000.0, 5000000.0]
}
}
For an affine transform, the bbox is fully determined by transform + shape (four-corner walk). Storing it again invites floating-point drift between the two copies. Each consumer picks its own tolerance and its own tiebreaker. Same store, different rectangles in different viewers.
The fix
One slot per fact:
- Affine array: carry
spatial:transform + spatial:shape. MUST NOT carry spatial:bbox (derivable).
- Non-affine array (rpc, polynomial, lookup, custom): MUST carry
spatial:bbox. No closed-form derivation exists.
- Group: MAY carry
spatial:bbox only. No per-array spatial: properties belong at the group level.
JSON Schema enforces all three rules directly. No tolerance arithmetic, no normative prose for validators to ignore.
The cost
Two example fixtures need a one-line edit. Both included in the draft PRs below. No deployed production producers identified so far; if you know one that emits the forbidden pattern, flag it.
The proof
Draft PRs, both ready to run:
Receipts
For prior art (STAC, OGC, GeoTIFF, CF), rejected alternatives, per-issue cross-references, and out-of-scope reasoning, see notes/single-source-rule-rationale.md in the spec PR.
Resolves: #6, #8, #12, #23
Composes with: #19
The bug
spatial:lets you store the same fact in two slots. Array level today:{ "node_type": "array", "attributes": { "spatial:shape": [1024, 1024], "spatial:transform": [10.0, 0.0, 500000.0, 0.0, -10.0, 5000000.0], "spatial:bbox": [500000.0, 4990000.0, 510000.0, 5000000.0] } }For an affine transform, the bbox is fully determined by
transform+shape(four-corner walk). Storing it again invites floating-point drift between the two copies. Each consumer picks its own tolerance and its own tiebreaker. Same store, different rectangles in different viewers.The fix
One slot per fact:
spatial:transform+spatial:shape. MUST NOT carryspatial:bbox(derivable).spatial:bbox. No closed-form derivation exists.spatial:bboxonly. No per-arrayspatial:properties belong at the group level.JSON Schema enforces all three rules directly. No tolerance arithmetic, no normative prose for validators to ignore.
The cost
Two example fixtures need a one-line edit. Both included in the draft PRs below. No deployed production producers identified so far; if you know one that emits the forbidden pattern, flag it.
The proof
Draft PRs, both ready to run:
npm install && npm test: 2/2 example fixtures PASS, 12/12 rule cases PASS.Receipts
For prior art (STAC, OGC, GeoTIFF, CF), rejected alternatives, per-issue cross-references, and out-of-scope reasoning, see
notes/single-source-rule-rationale.mdin the spec PR.