feat(contract): D-OVC — realign classids to OGAR 0xDDCC + ogar_codebook wire-compat mirror#563
Conversation
…ok wire-compat mirror
Resolves ISS-CLASSID-OGAR-DRIFT (operator-signed): the merged contract
classids disagreed with OGAR ogar-vocab's domain-encoded 0xDDCC codebook
(CLASSID_OSINT=0x0007 hit OGAR's Reserved domain; CLASSID_FMA=0x0008 hit
OGAR's OCR block). Realigned onto the canonical 0xDDCC scheme.
D-OVC-2/3 (canonical_node.rs, layout-preserving const values — NOT a bit
reclaim, so no ENVELOPE_LAYOUT_VERSION bump):
- CLASSID_OSINT 0x0007 -> 0x0700 (OSINT domain root, >>8 == 0x07)
- CLASSID_FMA 0x0008 -> 0x0901 (anatomy concept in Health; 0x0900 root)
- mint CLASSID_PROJECT = 0x0100, CLASSID_ERP = 0x0200
- ReadMode::{PROJECT, ERP} (Cognitive/CoarseOnly) + BUILTIN_READ_MODES
- soa_graph::{PROJECT, ERP} DomainSpecs, re-exported from lib.rs
- value-asserting tests updated to the new values + >>8 domain-byte asserts;
new project_and_erp_classids_resolve_to_their_read_modes
D-OVC-1/4 (NEW contract::ogar_codebook — zero-dep, wire-compat, NO
OGAR<->contract dependency; the u16 LE wire is the only contract):
- ConceptDomain (7 domains, id>>8 route) + canonical_concept_domain
- classid_concept_domain (D-OVC-4: classid low-u16 -> domain)
- source_domain_concept("project"|"erp"|"german-erp")
- CODEBOOK (26 project 0x01XX + 6 commerce 0x02XX, mirrored from OGAR
ogar-vocab lib.rs:1073) + canonical_concept_id
- LabelDTO { label, id, canonical } + from_canonical + id_le
(named from_canonical, not OGAR's from_alias: the contract carries the
codebook-id layer, NOT OGAR's curator-alias normalizer)
- drift-guard test pins the shared 0xDDCC ids (both sides update together)
Verified: lance-graph-contract --lib 710 (default) / 716 (guid-v2-tail);
lance-graph-callcenter --features query 211; clippy -D warnings + fmt clean
on both configs. All OSINT/FMA references are symbolic; downstream unbroken.
(Also folds in pre-existing fmt drift in the same crate: aiwar/codebook/
hhtl/nan_projection comment-alignment + line-wrapping.)
Board: AGENT_LOG (cont.12), LATEST_STATE IN-PR entry, ISSUES RESOLVING,
plan ogar-vocab-contract-codebook-migration-v1 D-OVC-1/2/4 SHIPPED.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
📝 WalkthroughWalkthroughThe PR realigns ChangesOGAR classid realign and codebook mirror
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c2f2d87b8
ℹ️ 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".
| m.insert(NodeGuid::CLASSID_PROJECT, ReadMode::PROJECT); | ||
| m.insert(NodeGuid::CLASSID_ERP, ReadMode::ERP); |
There was a problem hiding this comment.
Route read modes for all domain concept slots
When producers use the newly exported CODEBOOK ids as NodeGuid.classid values, any non-root project/ERP concept such as project_work_item (0x0102) or commercial_line_item (0x0201) falls through classid_read_mode to ReadMode::DEFAULT, because only the 0x0100/0x0200 roots are inserted here. This defeats the new 0xDDCC domain routing contract and gives those real codebook classes the wrong value schema instead of ReadMode::PROJECT/ERP; the lookup needs to handle the whole 0x01XX/0x02XX domains, not just the roots.
Useful? React with 👍 / 👎.
| /// `in_family` = relates-to, `out_family` = blocks (cross-project dependency). | ||
| /// Anchor families are caller-supplied (the milestone / release hubs). | ||
| pub const PROJECT: DomainSpec = DomainSpec { | ||
| classid: NodeGuid::CLASSID_PROJECT, |
There was a problem hiding this comment.
Include concept classids in project/ERP snapshots
This DomainSpec only matches 0x0100, but project_snapshot filters rows with exact r.key.classid() == domain.classid while the new codebook exposes actual project classes as 0x0101..0x011A (for example project_work_item = 0x0102). A graph built from LabelDTO/canonical_concept_id classids will therefore render an empty Project snapshot; the domain filter needs to match the 0x01XX domain or the spec must be per concept. The same issue applies to the ERP root below for 0x0201..0x0206.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/lance-graph-contract/src/canonical_node.rs`:
- Around line 876-879: The current implementation in BUILTIN_READ_MODES only
registers exact classids NodeGuid::CLASSID_PROJECT and NodeGuid::CLASSID_ERP,
but this causes promoted concept IDs within the broader 0x01XX and 0x02XX domain
ranges to fall through to ReadMode::DEFAULT, resulting in incorrect routing.
Instead of exact ID matching, implement domain-based routing that checks the
domain prefix of classids and maps the entire 0x01XX range to ReadMode::PROJECT
and the 0x02XX range to ReadMode::ERP. This ensures all concept IDs within these
domains are routed correctly regardless of whether they are the base classids or
promoted variants.
In `@crates/lance-graph-contract/src/soa_graph.rs`:
- Around line 111-136: The newly exported PROJECT and ERP DomainSpec constants
contain behavior-critical configuration including classids and edge labels, but
lack direct unit tests to verify their field values. Add a #[cfg(test)] module
in the soa_graph.rs file with focused unit tests that validate each field of
both the PROJECT and ERP constants: verify that classid matches the
corresponding NodeGuid::CLASSID_PROJECT and NodeGuid::CLASSID_ERP, confirm the
name fields are "Project" and "ERP" respectively, ensure anchor_families are
empty slices, and validate that in_family_edge, out_family_edge, and member_edge
contain the correct edge label strings as documented in their comments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3fb19666-36e0-4196-b09b-a325ab619e79
📒 Files selected for processing (12)
.claude/board/AGENT_LOG.md.claude/board/ISSUES.md.claude/board/LATEST_STATE.md.claude/plans/ogar-vocab-contract-codebook-migration-v1.mdcrates/lance-graph-contract/src/aiwar.rscrates/lance-graph-contract/src/canonical_node.rscrates/lance-graph-contract/src/codebook.rscrates/lance-graph-contract/src/hhtl.rscrates/lance-graph-contract/src/lib.rscrates/lance-graph-contract/src/nan_projection.rscrates/lance-graph-contract/src/ogar_codebook.rscrates/lance-graph-contract/src/soa_graph.rs
| // Project-management (OpenProject ↔ Redmine) + commerce/ERP (Odoo ↔ OSB) — | ||
| // the OGAR `0x01XX` / `0x02XX` domains; both hot business graphs (Cognitive). | ||
| m.insert(NodeGuid::CLASSID_PROJECT, ReadMode::PROJECT); | ||
| m.insert(NodeGuid::CLASSID_ERP, ReadMode::ERP); |
There was a problem hiding this comment.
Route project/ERP concept classids by domain, not root-only exact ids.
BUILTIN_READ_MODES only registers 0x0100/0x0200. Any promoted concept id in 0x01XX or 0x02XX (e.g. 0x0102, 0x0206) currently falls through to ReadMode::DEFAULT, which misroutes value/edge decoding for those domains.
💡 Suggested fix
+use crate::ogar_codebook::{classid_concept_domain, ConceptDomain};
@@
pub fn classid_read_mode(classid: u32) -> ReadMode {
BUILTIN_READ_MODES
.get(&classid)
.copied()
- .unwrap_or(ReadMode::DEFAULT)
+ .unwrap_or_else(|| match classid_concept_domain(classid) {
+ ConceptDomain::ProjectMgmt => ReadMode::PROJECT,
+ ConceptDomain::Commerce => ReadMode::ERP,
+ _ => ReadMode::DEFAULT,
+ })
} #[test]
fn project_and_erp_classids_resolve_to_their_read_modes() {
@@
assert!(project.is_layout_preserving() && erp.is_layout_preserving());
+
+ // Non-root promoted concept ids in the same domains should resolve identically.
+ assert_eq!(classid_read_mode(0x0000_0102), ReadMode::PROJECT);
+ assert_eq!(classid_read_mode(0x0000_0206), ReadMode::ERP);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/lance-graph-contract/src/canonical_node.rs` around lines 876 - 879,
The current implementation in BUILTIN_READ_MODES only registers exact classids
NodeGuid::CLASSID_PROJECT and NodeGuid::CLASSID_ERP, but this causes promoted
concept IDs within the broader 0x01XX and 0x02XX domain ranges to fall through
to ReadMode::DEFAULT, resulting in incorrect routing. Instead of exact ID
matching, implement domain-based routing that checks the domain prefix of
classids and maps the entire 0x01XX range to ReadMode::PROJECT and the 0x02XX
range to ReadMode::ERP. This ensures all concept IDs within these domains are
routed correctly regardless of whether they are the base classids or promoted
variants.
| /// The **project-management** domain (classid [`NodeGuid::CLASSID_PROJECT`], | ||
| /// OGAR `0x01XX`): OpenProject ↔ Redmine work items. Family = project / version; | ||
| /// `in_family` = relates-to, `out_family` = blocks (cross-project dependency). | ||
| /// Anchor families are caller-supplied (the milestone / release hubs). | ||
| pub const PROJECT: DomainSpec = DomainSpec { | ||
| classid: NodeGuid::CLASSID_PROJECT, | ||
| name: "Project", | ||
| anchor_families: &[], | ||
| in_family_edge: "relates-to", | ||
| out_family_edge: "blocks", | ||
| member_edge: "in-project", | ||
| }; | ||
|
|
||
| /// The **commerce / ERP** domain (classid [`NodeGuid::CLASSID_ERP`], OGAR | ||
| /// `0x02XX`): Odoo ↔ OSB invoices / partners / taxes. Family = partner / journal; | ||
| /// `in_family` = line-of, `out_family` = paid-by (cross-partner settlement). | ||
| /// Anchor families are caller-supplied (the key accounts / journals). | ||
| pub const ERP: DomainSpec = DomainSpec { | ||
| classid: NodeGuid::CLASSID_ERP, | ||
| name: "ERP", | ||
| anchor_families: &[], | ||
| in_family_edge: "line-of", | ||
| out_family_edge: "paid-by", | ||
| member_edge: "in-ledger", | ||
| }; | ||
|
|
There was a problem hiding this comment.
Add focused tests for new PROJECT/ERP DomainSpec wiring.
These new exported constants carry behavior-critical config (classid + edge labels), but there’s no direct test locking their field values.
✅ Suggested focused test
#[cfg(test)]
mod tests {
use super::*;
@@
+ #[test]
+ fn project_and_erp_domain_specs_are_wired_as_expected() {
+ assert_eq!(PROJECT.classid, NodeGuid::CLASSID_PROJECT);
+ assert_eq!(PROJECT.name, "Project");
+ assert_eq!(PROJECT.in_family_edge, "relates-to");
+ assert_eq!(PROJECT.out_family_edge, "blocks");
+ assert_eq!(PROJECT.member_edge, "in-project");
+
+ assert_eq!(ERP.classid, NodeGuid::CLASSID_ERP);
+ assert_eq!(ERP.name, "ERP");
+ assert_eq!(ERP.in_family_edge, "line-of");
+ assert_eq!(ERP.out_family_edge, "paid-by");
+ assert_eq!(ERP.member_edge, "in-ledger");
+ }As per coding guidelines: crates/**/*.rs: Add Rust unit tests alongside implementations via #[cfg(test)] modules; prefer focused scenarios over broad integration tests.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// The **project-management** domain (classid [`NodeGuid::CLASSID_PROJECT`], | |
| /// OGAR `0x01XX`): OpenProject ↔ Redmine work items. Family = project / version; | |
| /// `in_family` = relates-to, `out_family` = blocks (cross-project dependency). | |
| /// Anchor families are caller-supplied (the milestone / release hubs). | |
| pub const PROJECT: DomainSpec = DomainSpec { | |
| classid: NodeGuid::CLASSID_PROJECT, | |
| name: "Project", | |
| anchor_families: &[], | |
| in_family_edge: "relates-to", | |
| out_family_edge: "blocks", | |
| member_edge: "in-project", | |
| }; | |
| /// The **commerce / ERP** domain (classid [`NodeGuid::CLASSID_ERP`], OGAR | |
| /// `0x02XX`): Odoo ↔ OSB invoices / partners / taxes. Family = partner / journal; | |
| /// `in_family` = line-of, `out_family` = paid-by (cross-partner settlement). | |
| /// Anchor families are caller-supplied (the key accounts / journals). | |
| pub const ERP: DomainSpec = DomainSpec { | |
| classid: NodeGuid::CLASSID_ERP, | |
| name: "ERP", | |
| anchor_families: &[], | |
| in_family_edge: "line-of", | |
| out_family_edge: "paid-by", | |
| member_edge: "in-ledger", | |
| }; | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| fn project_and_erp_domain_specs_are_wired_as_expected() { | |
| assert_eq!(PROJECT.classid, NodeGuid::CLASSID_PROJECT); | |
| assert_eq!(PROJECT.name, "Project"); | |
| assert_eq!(PROJECT.in_family_edge, "relates-to"); | |
| assert_eq!(PROJECT.out_family_edge, "blocks"); | |
| assert_eq!(PROJECT.member_edge, "in-project"); | |
| assert_eq!(ERP.classid, NodeGuid::CLASSID_ERP); | |
| assert_eq!(ERP.name, "ERP"); | |
| assert_eq!(ERP.in_family_edge, "line-of"); | |
| assert_eq!(ERP.out_family_edge, "paid-by"); | |
| assert_eq!(ERP.member_edge, "in-ledger"); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/lance-graph-contract/src/soa_graph.rs` around lines 111 - 136, The
newly exported PROJECT and ERP DomainSpec constants contain behavior-critical
configuration including classids and edge labels, but lack direct unit tests to
verify their field values. Add a #[cfg(test)] module in the soa_graph.rs file
with focused unit tests that validate each field of both the PROJECT and ERP
constants: verify that classid matches the corresponding
NodeGuid::CLASSID_PROJECT and NodeGuid::CLASSID_ERP, confirm the name fields are
"Project" and "ERP" respectively, ensure anchor_families are empty slices, and
validate that in_family_edge, out_family_edge, and member_edge contain the
correct edge label strings as documented in their comments.
Source: Coding guidelines
…GAR separation) The OGAR half of the operator's clean separation: lance-graph-ontology = OGIT (TTL/RDF hydration spine) lance-graph-ogar = OGAR (Active-Record Class / ClassView / adapters) OGAR is the Active-Record Core and ALREADY speaks the contract: ogar-class-view::OgarClassView impl lance_graph_contract::ClassView (32 promoted concepts), ogar-vocab::Class = the AR shape (attributes + family Associations), canonical_concept_id == ClassId == NodeGuid.classid low u16. So the lance-graph side needs ACTIVATION, not a new bridge. NEW crates/lance-graph-ogar (EXCLUDED, own [workspace]): - re-exports the FULL OGAR AR surface: ogar-vocab + ogar-class-view + ogar-ontology + ogar-adapter-surrealql (+ OgarClassView / Class / contract) - parity-guard (parity::assert_codebook_parity): bijective ogar_codebook::CODEBOOK <-> ogar_vocab::class_ids::ALL + domain agreement; FAILS THE BUILD on codebook drift - features: default (light, emit-only) / surrealql-parser (the unmap() parser half, rust 1.95+) / serde - git-deps OGAR@main + lance-graph-contract@main = ONE contract source (same as ogar-class-view's) so the impl ClassView matches the trait the guard checks — NO [patch] (the symbiont alignment); a path+git mix = two types - .gitignore drops Cargo.lock (re-resolve to branch tip; living-activation, symbiont pattern) Auto-activation = Cargo presence (no runtime detection): a build pulling this crate gets the real OGAR Class/ClassView/codebook + full from_alias normalizer + the drift fuse; a build without it carries the contract's zero-dep ogar_codebook mirror + the bare ClassView trait (OGAR stays headless-capable — depending on the zero-dep contract is the compile-time handshake, not "needing lance-graph"). Contract stays zero-dep; OGAR never deps the lance-graph engine. Verified: cargo test --manifest-path crates/lance-graph-ogar/Cargo.toml 3/3 (parity bijection >=32 concepts, classid<->codebook-id identity, OgarClassView-is-a-ClassView); contract resolved to ONE source (git main #ff1a3452 = merged #563); clippy -D warnings + fmt clean; root workspace metadata valid (ogar correctly excluded). Board: AGENT_LOG (cont.13), LATEST_STATE IN-PR entry, EPIPHANIES E-OGAR-IS-AR-CORE-AUTOACTIVATED-BY-CARGO-PRESENCE, plan D-OVC-5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
Resolves ISS-CLASSID-OGAR-DRIFT (operator-signed). Fresh work atop
main(the prior jirak content merged via #561/#562; this branch was reset to main + this one commit, so the new PR is a clean fast-forward — no cherry-pick needed).The merged contract classids disagreed with OGAR
ogar-vocab's domain-encoded0xDDCCcodebook:CLASSID_OSINT=0x0007landed in OGAR's Reserved domain (OSINT is0x07XX),CLASSID_FMA=0x0008landed in OGAR's OCR block (FMA/anatomy is clinical → Health0x09XX). This realigns the contract onto the canonical0xDDCCscheme and hosts the codebook-id layer in the contract.D-OVC-2/3 — realign (
canonical_node.rs, layout-preserving)Const value changes (not a bit-layout reclaim) → no
ENVELOPE_LAYOUT_VERSIONbump:CLASSID_OSINT 0x0007 → 0x0700(OSINT domain root,>>8 == 0x07)CLASSID_FMA 0x0008 → 0x0901(anatomy concept in Health domain;0x0900= Health root)CLASSID_PROJECT = 0x0100+CLASSID_ERP = 0x0200ReadMode::{PROJECT, ERP}(Cognitive/CoarseOnly) registered inBUILTIN_READ_MODESsoa_graph::{PROJECT, ERP}DomainSpecs, re-exported fromlib.rs>>8domain-byte asserts; newproject_and_erp_classids_resolve_to_their_read_modesD-OVC-1/4 — NEW
contract::ogar_codebook(zero-dep, wire-compat, NO OGAR↔contract dependency)The
u16LE wire is the only contract; a drift-guard test pins the shared0xDDCCids so both crates update together.ConceptDomain(7 domains) +canonical_concept_domain(id>>8)classid_concept_domain(classid)— D-OVC-4 classid→domain routesource_domain_concept("project"|"erp"|"german-erp")CODEBOOK(26 project0x01XX+ 6 commerce0x02XX, mirrored from OGARogar-vocablib.rs:1073) +canonical_concept_idLabelDTO { label, id, canonical }+from_canonical+id_le— namedfrom_canonical(not OGAR'sfrom_alias) because the contract carries the codebook-id layer, not OGAR's curator-alias normalizer (canonical_concept, which stays inogar-vocab)Operator decisions applied (plan §5)
0xDDCC✅ 2. Dependency direction = wire-compat (no new dep) ✅ 3. FMA = Health0x0901,CC=0= domain root reserved everywhere ✅Verification
lance-graph-contract --lib: 710 (default) / 716 (guid-v2-tail)lance-graph-callcenter --features query: 211-D warnings --all-targets+cargo fmtclean on both configsaiwar/codebook/hhtl/nan_projectioncomment-alignment + line-wrapping.)Board:
AGENT_LOG(cont.¹²),LATEST_STATEIN-PR entry,ISSUESISS-CLASSID-OGAR-DRIFT→ RESOLVING, planogar-vocab-contract-codebook-migration-v1(D-OVC-1/2/4 SHIPPED, D-OVC-3 PARTIAL — canon-doc cross-ref pending).🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit
New Features
Chores