Skip to content

ogar-obo: migrate the row key from the V1 u24 tail to the V3 family:identity rail - #248

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/x265-x266-plans-review-h9osnl
Aug 6, 2026
Merged

ogar-obo: migrate the row key from the V1 u24 tail to the V3 family:identity rail#248
AdaWorldAPI merged 1 commit into
mainfrom
claude/x265-x266-plans-review-h9osnl

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What this does

ogar-obo's pack_key wrote the V1 u24 tail (identity's low 3 bytes at
13..16, family zeroed). lance-graph-contract has defaulted to
guid-v3-tail since the 2026-07-04 ruling (ISS-V1-TAIL-RESIDUE), so the
baker was minting a shape the manifest's own declared loader
(canonical_node::node_rows_from_le_bytes) no longer reads as canonical.

There are no pre-flip rows of this bake to preserve, so this is a straight
migration, not a gated dual-read.

V3 tail per NodeGuid::new_v2: leaf u16 @10..12, family u16 @12..14,
identity u16 @14..16.

The CURIE numeric does not fit identity

OBO ids run past u16 — MONDO:0700092 = 700,092 — and mint_for's V2/V3 arm
asserts identity <= 0xFFFF rather than truncating. So the 24-bit numeric
is carried on the family:identity rail: family = num >> 16,
identity = num & 0xFFFF. Lossless for the full 24 bits, 8 bits of headroom
left in family.

Sort order is unchangedfamily holds the high bits, so ordering by
(classid, family, identity) is ordering by (classid, num). The
binary-search-by-key invariant over the sorted artifact survives.

The test is the point

v3_tail_carries_oversize_curie_numerics_on_the_family_identity_rail is the
falsifier the existing round-trip could not be: that one uses id 9, which
fits either layout and therefore cannot tell them apart. The new one uses
700,092, pins the byte positions against new_v2, asserts a V1 u24 read of
the same bytes is observably wrong
(proving the tail moved rather than
coincided), and asserts tail-byte ordering still tracks the numeric.

ogar-obo 13/13 · ogar-ro 6/6 · clippy clean on both.

Open, and deliberately not decided here

1. A canon question the reviewer should rule on. OGAR's canon says the
12-byte register is 6×(u8:u8) and that "u8:u8 stays two separate bytes —
never widened to u16 or u24"
, because a flat wide integer has no axis. Using
family:identity as a 32-bit split is arguably that same widening, spread over
two fields. The canon names a sanctioned home for exactly this shape — the
grace carvings (D-V1-GRACE-CARVINGS, legacy_outliers), which state outright
that "the V1 family:identity u24 fragment is the degenerate G1/G2 case"
while preferring migration to a real axis. Three options: keep this; register
it as a declared grace carving; or give family a genuine bucket meaning
(cheap now, expensive after a re-bake ships).

2. The published artifact is now stale. obo-core.soa (release
ogar-obo-v0.1.0, 68,797 rows) was baked V1-tailed; a reader on this code
would decode the numeric from the wrong bytes. It needs a re-bake + re-publish.

3. The manifest has no tail-variant field — which is what allowed the
ambiguity. I-LEGACY-API-FEATURE-GATED requires a layout reclaim to be paired
with a version gate: "a v1 binary blob under a v2 reader MUST refuse to
decode."
Adding that field, and making the loader fail closed on a mismatch,
is the durable fix.

Pre-existing rustfmt drift in examples/bake_obo.rs is untouched (verified
pre-existing by stashing this diff).

…dentity rail

`pack_key` wrote the V1 tail (identity's low 3 bytes at 13..16, family zeroed);
`lance-graph-contract` has defaulted to `guid-v3-tail` since the 2026-07-04
ruling (ISS-V1-TAIL-RESIDUE), so the baker was minting a shape the declared
loader no longer reads as canonical. There are no pre-flip rows of this bake to
preserve, so this is a straight migration rather than a gated dual-read.

V3 tail, per `NodeGuid::new_v2`: leaf u16 @10..12, family u16 @12..14,
identity u16 @14..16.

The CURIE numeric does NOT fit `identity` alone. OBO ids run past 65_535
(MONDO:0700092 = 700_092) and `mint_for`'s V2/V3 arm asserts
`identity <= 0xFFFF` rather than truncating, so the 24-bit numeric is carried
by the family:identity RAIL -- family = num >> 16, identity = num & 0xFFFF.
That is the point of V3 over a flat u24: a u24 has no axis and cannot carry an
X:Y rail; the pair can, losslessly, with 8 bits of headroom left in `family`.

Sort order is unchanged. `family` holds the high bits, so ordering by
(classid, family, identity) is ordering by (classid, num) -- the
binary-search-by-key invariant over the sorted artifact survives the migration.

New test `v3_tail_carries_oversize_curie_numerics_on_the_family_identity_rail`
is the falsifier the existing round-trip could not be: that one uses id 9, which
fits either layout and so cannot tell them apart. The new one uses 700_092,
pins the byte positions against new_v2, asserts a V1 u24 read of the same bytes
is observably WRONG (proving the tail moved rather than coincided), and asserts
tail-byte ordering still tracks the numeric.

ogar-obo 13/13, ogar-ro 6/6, clippy clean on both.

CONSEQUENCE, not handled here: the published `obo-core.soa`
(release ogar-obo-v0.1.0, 68_797 rows) was baked V1-tailed and is now STALE
against this code -- a reader would decode the numeric from the wrong bytes. It
needs a re-bake and re-publish. The manifest also carries no tail-variant field;
adding one, and refusing a bake whose declared tail does not match the reader,
is what I-LEGACY-API-FEATURE-GATED requires of a layout reclaim ("a v1 binary
blob under a v2 reader MUST refuse to decode"). Both are operator actions --
flagged, not silently left.

Pre-existing rustfmt drift in `examples/bake_obo.rs` is untouched (verified
pre-existing by stashing this diff); not widening this change to cover it.
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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_4cf97f37-a1ec-4946-ad75-bec4f72ef0bd)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review August 6, 2026 07:31
@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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_0ffe801d-f84f-4d27-9a70-6888e933f64d)

@AdaWorldAPI
AdaWorldAPI merged commit 0897619 into main Aug 6, 2026
2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82be74d293

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +311 to +312
let f = ((identity >> 16) as u16).to_le_bytes();
let i = ((identity & 0xFFFF) as u16).to_le_bytes();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Encode the numeric using the actual V3 facet layout

When these rows are loaded under the advertised guid-v3-tail default, this writes the deprecated NodeGuid::new_v2 carving (leaf:u16 / family:u16 / identity:u16) into bytes that V3 defines as six independent (u8, u8) axes (CLAUDE.md:9-22 and docs/NODEGUID-CANON-AUDIT.md:80-94). For OBO IDs above 65,535, canonical V3 readers therefore see unrelated rail components and cannot reconstruct values such as 700_092; only this crate's custom decode_key understands the invented concatenation, breaking the stated cross-repository byte contract. Encode through the sanctioned V3 facet/ClassView layout, or explicitly select a registered legacy/wide carving instead of splitting the numeric across V2 fields.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants