Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude/board/LATEST_STATE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# LATEST_STATE — What Just Shipped (read this FIRST)

## 2026-07-11 — branch `claude/medcare-ruff-codebook-handover-5ulx0i` — `ClassView::menu_address` — the runtime Klickwege-menu radix projection

### Current Contract Inventory — new entry

- **`class_view::ClassView::menu_address(class) -> Vec<ClassId>`** (NEW default trait method). The RUNTIME projection of the harvest-side ruff `nav_digest` `[menu-quad]` `loc=` field: walks [`is_a_parent`] root-first to lower a class's menu LOCATION into the existing classid ontology as a radix-trie path `[root, …, parent, class]` — the concept ontology **is** the radix trie; the menu address is a path through it, never a stored ordinal (V3 LE-contract §3). A renderer lays out the menu by prefix from the path alone, zero value decode. Same 16-hop cap + on-stack visited cycle guard as the sibling `resolve_render_class` (never loops/panics; only alloc is the returned path). Default method → every existing `ClassView` impl gains it for free. Tests: `menu_address_walks_is_a_root_first` (30 is_a 20 is_a 7 → `[7,20,30]`; root → `[7]`), `menu_address_cycle_terminates` (2-cycle + self-loop bounded). Completes the "digest-now-ClassView-after" plan (ruff #82 = digest lowering; this = runtime projection). No new type/module; pure additive trait surface.

## 2026-07-10 — branch `claude/review-claude-board-files-nhqgx1` — `contract::style_family::StyleFamily` — M9 ThinkingStyle dedup shipped (D-TSC-1, first 5+3 council run)

### Current Contract Inventory — new entry
Expand Down
70 changes: 70 additions & 0 deletions crates/lance-graph-contract/src/class_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,48 @@ pub trait ClassView {
}
}

/// The **menu address** of `class` — the radix-trie path lowered from the
/// `is_a` rail, root-first: `[root_ancestor, …, parent, class]`.
///
/// This is the runtime projection of the harvest-side `nav_digest`
/// `[menu-quad]` `loc=` field (ruff `ruff_spo_triplet::nav_digest`): the
/// existing classid ontology **is** the radix trie, and a node's menu
/// LOCATION is a path through it — walking [`is_a_parent`](ClassView::is_a_parent),
/// never a stored ordinal (V3 LE-contract §3 forbids a position slot). A
/// renderer lays out the menu by prefix from this path alone, with zero
/// value decode (OGAR "the key prerenders nodes"). A root class (no
/// `is_a_parent`) returns `[class]` — its own single-element address.
///
/// **Cycle- and depth-safe, zero-dep:** the SAME 16-hop cap + on-stack
/// visited guard as [`resolve_render_class`](ClassView::resolve_render_class).
/// A `subClassOf` cycle terminates at the first repeat (never loops, never
/// panics); the only allocation is the returned path.
fn menu_address(&self, class: ClassId) -> Vec<ClassId> {
const MAX_HOPS: usize = 16;
let mut path: Vec<ClassId> = Vec::new();
let mut visited: [ClassId; MAX_HOPS] = [class; MAX_HOPS];
let mut current = class;
let mut depth = 0usize;
loop {
path.push(current);
if depth >= MAX_HOPS {
break; // depth cap → truncated (still root-first, monotonic)
}
visited[depth] = current;
depth += 1;
let parent = match self.is_a_parent(current) {
Some(p) => p,
None => break, // taxonomy root
};
if visited[..depth].contains(&parent) {
break; // subClassOf cycle → stop at the first repeat
}
current = parent;
}
path.reverse(); // root-first: the radix-trie address
path
}

/// Which edge-codec flavor this class reads its node edge block with.
///
/// Default is
Expand Down Expand Up @@ -1596,6 +1638,34 @@ mod tests {
assert_eq!(classes.resolve_render_class(7), 7);
}

/// `menu_address`: the radix-trie path lowered from the `is_a` rail,
/// root-first — the runtime twin of the nav_digest `[menu-quad]` `loc=`.
#[test]
fn menu_address_walks_is_a_root_first() {
// 30 is_a 20 is_a 7 → the menu address is [7, 20, 30] (root-first).
let classes = FakeClasses::new().with_isa(30, 20).with_isa(20, 7);
assert_eq!(
classes.menu_address(30),
vec![7, 20, 30],
"the address is the is_a ancestor chain, broadest-first"
);
// A root class (no is_a parent) is its own single-element address.
assert_eq!(classes.menu_address(7), vec![7]);
}

/// A `subClassOf` cycle terminates: the path is the bounded chain up to the
/// first repeat, never an infinite loop (same guard as the render ladder).
#[test]
fn menu_address_cycle_terminates() {
let classes = FakeClasses::new().with_isa(40, 41).with_isa(41, 40);
let addr = classes.menu_address(40);
// 40 → 41 → (41's parent is 40, already visited) stop: path = [41, 40].
assert_eq!(addr, vec![41, 40], "cycle stops at the first repeat");
// A self-loop likewise terminates with a single element.
let selfloop = FakeClasses::new().with_isa(50, 50);
assert_eq!(selfloop.menu_address(50), vec![50]);
}

/// An orphan (no is_a parent, no fields) resolves to ITSELF — the monotonic
/// bottom rung: the caller renders the generic facet dump.
#[test]
Expand Down
Loading