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/EPIPHANIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
**Why it matters beyond the bug:** the F32-17D tenant is the **single bit-exact ground-truth** the rest of the SoA migration (S3 driver-flip, future quantization tiers bf16/i4) measures against — every step can diff against it. Alternatives (bf16/f16-17D) are deferred; bf16 is integer-exact only to 256, f16 to 2048, and neither is f32-bit-exact, so they could not satisfy the bit-exact tests as the anchor.

**Tests:** singleton `--features with-engine` 6/6 (the 3 un-ignored + 2 + the C8 corner corpus `codebook_index ∈ {0,255,256,1234,4095,65535}`); mailbox-arm `--features with-engine,mailbox-thoughtspace` 5/5 (headline+energy bit-exact via the f32 tenant; non-headline top_k=0 by design, C5/D-DIST-5 preserved); default lib 20/20 (cfg field absent — no hot-path change). fmt clean; my code clippy-clean.
## 2026-06-18 — E-ODOO-SELECTION-REPLACE-EXTEND-POISON — `selection=` closes, `selection_add=` extends, a dynamic base poisons; conflating them produces wrong `ASSERT IN`

**Status:** FINDING (codex review on merged #532; fixed forward). The first `selection_value` cut conflated two Odoo idioms into one flat value list, producing two ways to emit a **wrong** `ASSERT $value IN [...]` — the exact silent-data-rejection class that matters for "Odoo as faithful DDL": (1) a static `selection_add=` onto a **dynamic** base (`Selection(STATE_CONST)` / `selection='_compute_x'`) wrongly *closed* an open domain; (2) the cross-class merge unioned **every** declaration, so an `_inherit` class that *redeclares* `selection=[…]` left stale keys from the replaced set.

**The correct three-way semantics** (`spo_enrich._extract_selection` → `(base_dynamic, base_keys, add_keys)`): a full static `selection=` **REPLACES** the domain (a redeclaration drops earlier keys); `selection_add=` **EXTENDS** (unions across classes); a provided-but-unresolvable base **POISONS** the field (never emitted as a closed set, sticky). **Critically, replace/extend/poison must be tracked SEPARATELY through the whole scan and collapsed only at the end** (`_new_sel_acc` per field + `_resolve_selections` in `build_all_facts`) — a third codex finding (#536 follow-up) caught that the first fix collapsed them per-class, so under `glob.iglob`'s unordered walk a `selection_add` scanned *before* the base `selection=` was clobbered by the REPLACE and the legal added key was lost. Collapsing-during-scan is order-dependent; collapsing-at-resolve is not. The `ASSERT IN` is only emitted when the *whole* domain is statically known. 62 python tests (was 57); three findings have paired regression tests (`test_dynamic_base_plus_add_stays_poisoned`, `test_full_redeclaration_replaces_not_unions`, `test_selection_add_before_base_is_order_independent`). Also: `mro.rs` was landed un-`cargo fmt`'d on #533 — fixed in the same follow-up. General lesson: a "merge across classes" on a field whose declaration can either *replace* or *extend* must (a) distinguish the two, (b) keep their state separate until a final resolve so the result is scan-order-independent, and (c) never silently close an open/dynamic domain.

## 2026-06-18 — E-ODOO-VIRTUALLY-OVERRIDES-COMPUTED — the wishlist's last item is a *derived* ClassView relation, not harvest predicate #6

**Status:** FINDING. `virtually_overrides` — the one open odoo-rs wishlist item — lands as a **computed** Core/ClassView capability (`odoo_blueprint::mro`), closing the wishlist consistently with the Core-first correction (E-ODOO-CORE-FIRST-STRUCTURAL) directly above. Per the doctrine the `(has_function / inherits_from / virtually_overrides)` triad is the ClassView method-resolution manifest: `has_function` + `inherits_from` are *facts*; `virtually_overrides` is the **derivation** (`M.m` overrides `B.m` iff `M` declares `m`, `B` is up `M`'s `_inherit` chain, and `B` also declares `m`). Adding a `virtually_overrides` AST pass to `spo_enrich.py` would have been the drift; computing it from the manifest is the Core-correct move.
Expand Down
23 changes: 10 additions & 13 deletions crates/lance-graph-ontology/src/odoo_blueprint/mro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,8 @@ fn nearest_base_declaring(
seen.insert(start.to_string());
// Frontier holds (model) at increasing chain distance; a BTreeSet per level
// gives the sorted tie-break, then feeds the next level.
let mut frontier: VecDeque<String> = bases_of
.get(start)
.into_iter()
.flatten()
.cloned()
.collect();
let mut frontier: VecDeque<String> =
bases_of.get(start).into_iter().flatten().cloned().collect();

while let Some(node) = frontier.pop_front() {
if !seen.insert(node.clone()) {
Expand Down Expand Up @@ -220,11 +216,7 @@ mod tests {
#[test]
fn nearest_base_wins_over_farther_base() {
// child → mid → far ; all declare _x. Override targets `mid` (nearest).
let m = methods(&[
("child", &["_x"]),
("mid", &["_x"]),
("far", &["_x"]),
]);
let m = methods(&[("child", &["_x"]), ("mid", &["_x"]), ("far", &["_x"])]);
let b = bases(&[("child", &["mid"]), ("mid", &["far"])]);
let ov = resolve_overrides(&m, &b);
assert_eq!(ov.len(), 2, "child→mid and mid→far both resolve");
Expand All @@ -249,7 +241,9 @@ mod tests {
let m = methods(&[("a", &["_x"]), ("b", &["_x"])]);
let b = bases(&[("a", &["b"]), ("b", &["a"])]);
let ov = resolve_overrides(&m, &b); // must not hang
assert!(ov.iter().any(|o| o.child_model == "a" && o.base_model == "b"));
assert!(ov
.iter()
.any(|o| o.child_model == "a" && o.base_model == "b"));
}

#[test]
Expand Down Expand Up @@ -279,7 +273,10 @@ mod tests {
// non-trivial and the call is total, not that overrides exist.
let (m, b) = manifest_from_curated_core();
assert!(!m.is_empty(), "curated core declares methods");
assert!(b.contains_key("account_move"), "account_move has _inherit bases");
assert!(
b.contains_key("account_move"),
"account_move has _inherit bases"
);
let _ = resolve_overrides(&m, &b); // terminates, deterministic
}
}
Loading
Loading