Skip to content

Commit dd1bb9e

Browse files
committed
feat(contract): mirror OGAR APP-prefix (hi-u16) layer into ogar_codebook — closes ISS-CONTRACT-APP-PREFIX-MIRROR
Membrane consumers (woa-rs / medcare-rs / smb-office-rs, BBB-barrier: contract/ontology/callcenter only) can now pull BOTH halves of a render classid from lance_graph_contract::ogar_codebook — no hand-stamped 0x000N. The #591 consumer spellbook surfaced the gap: contract::ogar_codebook mirrored the lo-u16 concept pull (canonical_concept_id) but not OGAR#97's PortSpec::APP_PREFIX / render_classid_for (the hi-u16 render composition). This closes it, following the OGAR#98 canonical_concept_name precedent. NEW (wire-compat mirror of OGAR ogar_vocab::app, no ogar-vocab dependency): - AppPrefix enum = the OGAR#95 §2 allocation table as typed data (Core 0x0000 / OpenProject 0x0001 / Odoo 0x0002 / WoA 0x0003 / SMB 0x0004 / Healthcare 0x0005 / Redmine 0x0007), with prefix() / from_prefix() / render(concept). - render_classid(prefix, concept) (mirror of app::render_classid) - render_classid_for_concept(AppPrefix, &str) (one-call membrane helper) - classid_app_prefix(classid) (mirror of app::app_of) - classid_concept(classid) (mirror of app::concept_of) Parity tests pin the wire: - app_prefixes_match_ogar_allocation_table — the 6 prefixes vs OGAR PortSpec::APP_PREFIX (drift guard). - render_classid_composes_decomposes_and_preserves_the_concept_half — the 0x0005_0901 MedCare-patient worked example, and that the render lens never perturbs the lo-u16 concept RBAC keys on. Board-hygiene (same commit): ISSUES.md ISS-CONTRACT-APP-PREFIX-MIRROR -> RESOLVED; LATEST_STATE.md Contract Inventory entry; the consumer spellbook Core-gap section -> CLOSED + remediation step 3 cites the new helper. Incidental: the crate-wide cargo fmt pass also corrected pre-existing struct-literal/line-width drift in content_store.rs (same crate, no behavior change). Verify: cargo test -p lance-graph-contract green (8 ogar_codebook unit tests + render_classid_for_concept doctest); clippy -p lance-graph-contract --all-targets -D warnings clean (default + guid-v2-tail); fmt --check clean.
1 parent c66dac4 commit dd1bb9e

6 files changed

Lines changed: 272 additions & 20 deletions

File tree

.claude/board/ISSUES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
## 2026-06-22 — ISS-CONTRACT-APP-PREFIX-MIRROR — `contract::ogar_codebook` lacks the OGAR#97 `APP_PREFIX` / `render_classid_for` mirror, so membrane consumers must hand-stamp the hi-u16 render prefix
44

5-
**Status:** Open · Owner: lance-graph-contract · Surfaced by: `.claude/knowledge/ogar-consumer-preflight.md` (the consumer spellbook).
5+
**Status:** RESOLVED 2026-06-22 (`claude/contract-app-prefix-mirror`) · Owner: lance-graph-contract · Surfaced by: `.claude/knowledge/ogar-consumer-preflight.md` (the consumer spellbook).
6+
7+
**Resolution:** `contract::ogar_codebook` now mirrors the hi-u16 APP-prefix layer — `AppPrefix` (the OGAR#95 §2 allocation table as typed data: `0x0001` OpenProject / `0x0002` Odoo / `0x0003` WoA / `0x0004` SMB / `0x0005` Healthcare / `0x0007` Redmine), `render_classid` + `render_classid_for_concept` (compose), `classid_app_prefix` + `classid_concept` (decompose). A membrane consumer (BBB-safe) now pulls BOTH halves from one source — no hand-stamped `0x000N`. Wire-compat parity test `app_prefixes_match_ogar_allocation_table` pins the prefixes against OGAR `PortSpec::APP_PREFIX`; `render_classid_composes_decomposes_and_preserves_the_concept_half` pins the `0x0005_0901` MedCare-patient worked example. Mirrors OGAR#97 (`ogar_vocab::app`), following the OGAR#98 `canonical_concept_name` precedent.
68

79
`contract::ogar_codebook` mirrors `canonical_concept_id` / `canonical_concept_name` (the lo-u16 concept pull, BBB-safe for membrane consumers woa-rs / medcare-rs / smb-office-rs) but does NOT mirror OGAR#97's `PortSpec::APP_PREFIX` + `render_classid_for` (the hi-u16 render composition: `render_classid = APP << 16 | concept`, OGAR#95 §2). A membrane consumer (BBB-barrier: contract/ontology/callcenter only — `lance_graph_ogar` forbidden) can therefore pull the shared concept but must re-derive the app prefix from the OGAR#95 allocation table by hand. Per Core-First the consumer MUST NOT hard-code `0x000N`. **Fix:** mirror the app-prefix table + a `render_classid` helper into `contract::ogar_codebook` (the `canonical_concept_name` reverse-map mirror, OGAR#98, is the precedent) so the membrane stamps from one source. Interim: the spellbook's Q5 says "stamp from the allocation table." Cross-ref: `.claude/knowledge/ogar-consumer-preflight.md` § "A Core gap this spellbook surfaces"; OGAR#95/#97/#98.
810

.claude/board/LATEST_STATE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
1111
---
1212

13+
## 2026-06-22 — IN PR (`claude/contract-app-prefix-mirror`) — `contract::ogar_codebook` APP-prefix (hi-u16) mirror — closes `ISS-CONTRACT-APP-PREFIX-MIRROR`
14+
15+
Membrane consumers can now pull BOTH halves of a render `classid` BBB-safely from `lance_graph_contract::ogar_codebook` — no hand-stamped `0x000N`. **NEW:** `AppPrefix` enum (the OGAR#95 §2 allocation table as typed data — `Core 0x0000` / OpenProject `0x0001` / Odoo `0x0002` / WoA `0x0003` / SMB `0x0004` / Healthcare `0x0005` / Redmine `0x0007`) with `prefix()` / `from_prefix()` / `render(concept)`; free fns `render_classid(prefix, concept)`, `render_classid_for_concept(AppPrefix, &str)`, `classid_app_prefix(classid)`, `classid_concept(classid)` — the wire-compat mirror of OGAR#97 `ogar_vocab::app` (`render_classid_for::<P>` / `app_of` / `concept_of`), **no `ogar-vocab` dependency**. Two parity tests: `app_prefixes_match_ogar_allocation_table` (pins the 6 prefixes vs OGAR `PortSpec::APP_PREFIX`) + `render_classid_composes_decomposes_and_preserves_the_concept_half` (pins the `0x0005_0901` MedCare-patient worked example, and that the render lens never perturbs the lo-u16 concept RBAC keys on). Follows the OGAR#98 `canonical_concept_name` mirror precedent. Closes the gap the #591 consumer spellbook surfaced. Contract lib **+2 tests** / +1 doctest; `cargo fmt -p lance-graph-contract --check` clean; `clippy -p lance-graph-contract --all-targets -D warnings` clean (also `--features guid-v2-tail`). (Incidental: the crate-wide `cargo fmt` pass also corrected pre-existing struct-literal/line-width drift in `content_store.rs` — same crate, no behavior change.) Refs: PR (this branch), ISSUES `ISS-CONTRACT-APP-PREFIX-MIRROR` (RESOLVED), `.claude/knowledge/ogar-consumer-preflight.md` § Core-gap (CLOSED), OGAR#97/#98.
16+
17+
---
18+
1319
## 2026-06-20 — golden-image (symbiont) harness shipped to `main`; lance-7 lockstep unified end-to-end
1420

1521
`crates/symbiont/` (workspace-`exclude`d) compiles+links the FULL stack into ONE binary — lance-graph + lance7/lancedb0.30 + ndarray + ractor + surrealdb(kv-lance) + OGAR. **Verified green** (real git-deps build, `CARGO_EXIT=0`, 4.3 MB binary runs): unified `lance 7.0.0 / lance-index 7.0.0 / lancedb 0.30.0 / datafusion 53.1.0 / arrow 58` — no lance-6/7 split. It is a **living integration harness** (`Dockerfile` + portable git-deps `Cargo.toml`) that tracks each fork's canonical branch (`master`/`main`), **NOT** a frozen snapshot; every per-session `jirak` branch is stale (HEAD ⊂ main/master, 0 unique commits). **`TD-SURREALDB-KVLANCE-LANCE7` PAID** — surrealdb `main` carries the lance-7 bump. PR #555 adds the 5+3 council `INTEGRATION_PLAN.md` (loose-end ledger → the Spain-grid acceptance gate). **Honest state:** linked into one binary; the *runtime edges* between the five crates are still pending integration (Grid→NodeRow bridge, kanban loop). Battle-test plan (probes A1–E3) queued behind the singleton-BindSpace → SoA switch. Refs: PR_ARC #555, EPIPHANIES `E-GOLDEN-IMAGE-IS-A-LIVING-HARNESS`, AGENT_LOG 2026-06-20.

.claude/knowledge/ogar-consumer-preflight.md

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,10 @@ One signature is suspicious; a local codebook copy alone is the trap.
112112
2. **Pull the classid** — pure function, no registry:
113113
- spine: `lance_graph_ogar::WoaPort::class_id(name) -> Option<u16>`
114114
- membrane (BBB): `lance_graph_contract::ogar_codebook::canonical_concept_id(name)`
115-
3. **Stamp the app prefix + delete the old surface.** Render id =
116-
`APP << 16 | cid`; authorize on the shared `cid` (lo u16). Then delete: the
115+
3. **Stamp the app prefix + delete the old surface.** Compose the render id —
116+
membrane (BBB): `ogar_codebook::render_classid_for_concept(AppPrefix::X, name)`
117+
or `AppPrefix::X.render(cid)`; spine: `render_classid_for::<XPort>(cid)`. Both
118+
are `APP << 16 | cid`; authorize on the shared `cid` (lo u16). Then delete: the
117119
`*Bridge` import, any `OntologyRegistry` field, and every local codebook
118120
copy. Your diff touches only your crate; the spine is byte-for-byte unchanged.
119121

@@ -133,17 +135,27 @@ repo; the classid pull is a pure function call.
133135
These three are the migration backlog. The terminal `bridges/` deletion in the
134136
spine is gated on all three reaching 0.
135137

136-
## A Core gap this spellbook surfaces (honest — flag, don't paper)
137-
138-
`contract::ogar_codebook` mirrors `canonical_concept_id` / `canonical_concept_name`
139-
(the lo-u16 pull, BBB-safe) but does **not** yet mirror OGAR#97's
140-
`PortSpec::APP_PREFIX` / `render_classid_for` (the hi-u16 render composition). So
141-
a **membrane** consumer can pull the shared concept but must hand-stamp the app
142-
prefix from the OGAR#95 allocation table — a small re-derivation a
143-
`contract::ogar_codebook::APP_PREFIX` mirror would remove. Per Core-First the
144-
consumer must NOT hard-code `0x000N`; file the contract mirror (the
145-
`canonical_concept_name` precedent is OGAR#98) rather than minting a local
146-
prefix const. Tracked: `ISS-CONTRACT-APP-PREFIX-MIRROR`.
138+
## A Core gap this spellbook surfaced — now CLOSED
139+
140+
> **Resolved 2026-06-22** (`claude/contract-app-prefix-mirror`, closes
141+
> `ISS-CONTRACT-APP-PREFIX-MIRROR`). `contract::ogar_codebook` now mirrors the
142+
> hi-u16 APP-prefix layer alongside the lo-u16 concept layer: `AppPrefix` (the
143+
> OGAR#95 §2 allocation table as typed data — `0x0001` OpenProject … `0x0005`
144+
> Healthcare … `0x0007` Redmine), `render_classid` / `render_classid_for_concept`
145+
> (compose), `classid_app_prefix` / `classid_concept` (decompose). A **membrane**
146+
> consumer now pulls BOTH halves BBB-safely —
147+
> `render_classid_for_concept(AppPrefix::Healthcare, "patient")``Some(0x0005_0901)`,
148+
> no hand-stamped `0x000N`. Wire-compat parity tests pin the prefixes against OGAR
149+
> `PortSpec::APP_PREFIX`. Mirrors OGAR#97 `ogar_vocab::app`, following the OGAR#98
150+
> `canonical_concept_name` precedent.
151+
152+
Original gap (kept for the record — honest, flag don't paper):
153+
`contract::ogar_codebook` mirrored the lo-u16 concept pull (`canonical_concept_id`)
154+
but not OGAR#97's `PortSpec::APP_PREFIX` / `render_classid_for` (the hi-u16 render
155+
composition), so a **membrane** consumer could pull the shared concept yet had to
156+
hand-stamp the app prefix from the OGAR#95 allocation table. Per Core-First the
157+
fix was to mirror it into the contract (the `canonical_concept_name` precedent is
158+
OGAR#98), never to mint a local prefix const.
147159

148160
## When this doc fires + trigger phrases
149161

crates/lance-graph-contract/src/content_store.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ impl SourceSpan {
8888
/// New span; `end` is clamped to be `>= start`.
8989
#[must_use]
9090
pub fn new(content: ContentId, start: u32, end: u32) -> Self {
91-
Self { content, start, end: end.max(start) }
91+
Self {
92+
content,
93+
start,
94+
end: end.max(start),
95+
}
9296
}
9397

9498
/// Span length in bytes. Saturating: a malformed span (`end < start`, only
@@ -226,7 +230,10 @@ mod tests {
226230
fn out_of_bounds_and_missing_fail() {
227231
let mut s = MemStore::default();
228232
let id = s.put_str("short");
229-
assert_eq!(s.resolve_span(SourceSpan::new(id, 0, 999)), Err(ContentError::SpanOutOfBounds));
233+
assert_eq!(
234+
s.resolve_span(SourceSpan::new(id, 0, 999)),
235+
Err(ContentError::SpanOutOfBounds)
236+
);
230237
assert_eq!(
231238
s.resolve_span(SourceSpan::new(ContentId(123), 0, 1)),
232239
Err(ContentError::NotFound)
@@ -245,7 +252,11 @@ mod tests {
245252
fn malformed_span_len_saturates_not_panics() {
246253
// Public fields let a consumer build end < start, bypassing new()'s clamp.
247254
// len() must saturate to 0 (consistent with is_empty), never panic/wrap.
248-
let bad = SourceSpan { content: ContentId(7), start: 13, end: 0 };
255+
let bad = SourceSpan {
256+
content: ContentId(7),
257+
start: 13,
258+
end: 0,
259+
};
249260
assert_eq!(bad.len(), 0);
250261
assert!(bad.is_empty());
251262
assert!(!bad.is_cited());

crates/lance-graph-contract/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,9 @@ pub use episodic_edges::{EdgeRef, EpisodicEdges64};
142142
pub use head2head::{CompetitionOutcome, Head2Head, WinnerCriterion};
143143
pub use kanban::{ExecTarget, KanbanColumn, KanbanMove, RubiconTransitionError};
144144
pub use ogar_codebook::{
145-
canonical_concept_domain, canonical_concept_id, classid_concept_domain, source_domain_concept,
146-
ConceptDomain, LabelDTO, CODEBOOK,
145+
canonical_concept_domain, canonical_concept_id, classid_app_prefix, classid_concept,
146+
classid_concept_domain, render_classid, render_classid_for_concept, source_domain_concept,
147+
AppPrefix, ConceptDomain, LabelDTO, CODEBOOK,
147148
};
148149
pub use scheduler::{DatasetVersion, NextPhaseScheduler, VersionScheduler};
149150
pub use soa_graph::{

0 commit comments

Comments
 (0)