Repository navigation
ogar-vocab: mint osm_street_node (0x0F0B), the first SYNTHESIZED Geo concept - #268
Merged
Merged
Conversation
…concept
A junction in the routable-way graph earns its own concept id. Not because
the Geo domain was short of ids, but because the substrate has no other way
to say which reading a row's value slots carry.
Junction rows exist today (Berlin 233,053; Brandenburg 590,437) and bake as
plain `osm_node`. That is the problem: nothing on disk distinguishes "a
junction using its edge slots" from "a tagged node using its first tag slot".
osm-soa-bake's street.rs documents what that costs — an earlier draft placed
the edge-name lane at value-local slot 2 and measured 1,084,213 FALSE HITS on
real Berlin data, one per tagged node, each reinterpreting a node's first tag
bytes as eight raw name ordinals. It survives today only by hiding at slot 1,
the last gap the tag path never writes. Adjacency, bearing, turn matrix and
speed need several more slots and there is no second gap. A distinct classid
makes the layout class-resolved, which is the canon's own answer ("Tetris it
across the slots"), and retires the workaround instead of stacking on it.
── The invariant this breaks, deliberately ──
`OSM_CONCEPTS` asserted every Geo concept has a Rails source class. That held
while the domain was a mirror of OSM's schema. It stopped holding when the
bake began SYNTHESIZING junctions: OSM has no junction element, so there is
nothing to ground against, and inventing a Rails name to keep the assertion
green would put a falsehood in the Core.
So the exemption is an explicit `SYNTHESIZED` list, not a relaxed assertion —
membership is a reviewable claim that OSM genuinely has no source element. A
paired check asserts every exempted name IS a concept, so the exemption
cannot silently cover a typo and re-open the hole it was cut for.
── Cross-repo, because the fuses said so ──
Adding a concept moved class_ids::ALL 90 -> 91 and tripped four drift guards,
one of which names its own remedy: "update this pin AND the lance-graph
mirror COUNT_FUSE in the same PR". Both pins, all_promoted_classes(), and the
lance_graph_contract::ogar_codebook mirror move together. Verified: ogar-osm
18/18, ogar-vocab 155/155, and lance-graph-ogar compiles — its COUNT_FUSE is
a compile-time assert, so a half-done mint fails the BUILD, not just tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_32356ff8-fc73-4bb9-bcf4-2e9f331d4de6) |
…mised
CI caught the mint landing half-done: `osm_street_node` (0x0F0B) went into
`class_ids::ALL` without a matching `OgarClassView` registry entry, so both
paired invariants failed —
every_codebook_id_appears_in_class_ids_all
osm_street_node (0x0f0b) in class_ids::ALL but missing from registry
known_class_ids_iterates_in_stable_codebook_order
known_class_ids drifted from codebook order (missing 3851)
which is exactly what that test's own comment says it exists to catch: "a
CODEBOOK promotion landing in OGAR without being added here."
The failure is pointed, given what the mint is FOR. The whole argument for
0x0F0B is that a junction's value slots need a class-resolved reading rather
than a lucky gap in the tag path — and the reading IS the ClassView. An id in
the codebook with no registry entry is an address that resolves to nothing:
the precise defect the mint was meant to remove, reintroduced one layer up.
`osm_street_node()` already existed in ogar-vocab; only the lift was missing.
Added to the import list and to the registry after `osm_user`, matching the
codebook order the second test asserts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
AdaWorldAPI
marked this pull request as ready for review
August 14, 2026 12:53
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_9d4c99d5-9cd6-434d-9b5e-309e5bc3aaf8) |
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.
Draft — this is a codebook mint, so it wants a deliberate read before it lands.
What
Adds
osm_street_node = 0x0F0Btoogar-vocab(and itsogar-osmre-export), plus aSYNTHESIZEDlist that names it explicitly.Why a new concept id, not a new slot
Junction rows already exist and already bake — Berlin 233,053, Brandenburg 590,437 — as plain
osm_node. The problem is not capacity, it is that nothing on disk distinguishes "a junction using its edge slots" from "a tagged node using its first tag slot." The reading is ambiguous, and the ambiguity is load-bearing.osm-soa-bake'sstreet.rsrecords what that cost. An earlier draft put the edge-name lane at value-local slot 2 and measured 1,084,213 false hits on real Berlin data — one per tagged node, each reinterpreting a node's first tag bytes as eight raw name ordinals. The lane survives today only by hiding at slot 1 (GEO_SLOPE_SLOT), which happens to be the last gap the ordinary tag path never writes.That gap is now spent. Adjacency, bearing, the turn matrix and speed limits each need slots, and there is no second gap to hide in. A distinct classid makes the layout class-resolved — which is the canon's own answer to this shape of problem ("Tetris it across the slots"), not a workaround for it.
Why
SYNTHESIZEDEvery other Geo concept mirrors an OSM element. A junction does not: OSM has no junction element, only ways that happen to share a node. The id names something the bake derives, so the list says so in the vocabulary rather than leaving a future reader to infer it from absence.
0x0F0Bis otherwise an ordinary Geo-domain concept id.What this does not do
No consumer wired yet.
heading.rs(exit direction + bending) andaccess.rs(access/oneway/contraflow + the 8×8 Abbiegeverbot mask) are built and unit-tested inopenstreetmap-website-rs, but nothing reads them —access.rssays so in its own module docs: "wiring real slot indices from actual junction rows is pending." The slot layout is the next decision and deliberately not made here; given the 1,084,213-false-hit precedent, guessing a slot is exactly the move to avoid.Blast radius
Additive. A new constant and a new list; no existing id changes value, no reader changes behaviour. Nothing bakes
0x0F0Byet, so no artifact on disk is affected either way.Generated by Claude Code