Skip to content

Commit 42f245a

Browse files
authored
Merge pull request #594 from AdaWorldAPI/claude/dolce-odoo-hydrator-silent-suffix-bugs
fix(hydrator/dolce_odoo): close two silent suffix-rule drifts; comments now match real Odoo class names
2 parents ebeacde + f6472e6 commit 42f245a

2 files changed

Lines changed: 120 additions & 2 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
1+
## 2026-06-23 — E-DOLCE-ODOO-SILENT-SUFFIX-DRIFT — two hydrator-side suffix rules silently failed their own comments; cross-validation against `od_ontology::alignment` caught it (odoo-rs PR #15)
2+
3+
**Status:** FINDING (cross-validation result from a sibling repo). Two
4+
`lance_graph_ontology::hydrators::dolce_odoo` suffix rules carried module-doc
5+
comments claiming to match canonical Odoo classes, but the rules as written
6+
DID NOT actually match those classes:
7+
8+
| Suffix rule | Comment claimed to match | What it actually matched | Canonical Odoo class |
9+
|---|---|---|---|
10+
| `.group` | `res.groups`, `account.tax.group` | only `account.tax.group` | `res.groups` (PLURAL) was silently missed |
11+
| `.config` | `*.config.settings` | only `*.config` direct models | `res.config.settings`, `sale.config.settings` were silently missed |
12+
13+
The hydrator's own tests didn't catch either bug — they tested
14+
`account.move`/`res.partner` (covered by other rules) and never asserted the
15+
class names the comments claimed.
16+
17+
**How it was caught.** `odoo-rs` PR #14 pulled the alignment table into
18+
`od_ontology::alignment` and added cross-validation pins; PR #15 enriched the
19+
classifier by pulling from this hydrator and added test cases for
20+
`res.groups` and `res.config.settings`, both of which then failed against the
21+
original rules. The fix added `.groups` (plural) to `QUALITY_SUFFIXES` and
22+
`.settings` to `ABSTRACT_SUFFIXES` while keeping the singular `.group` /
23+
`.config` for the cases they always caught. Module-doc comments now record
24+
the convention explicitly.
25+
26+
**Two-classifier disagreement context.** lance-graph hosts **two** DOLCE
27+
classifiers for Odoo: this one (`ontology::hydrators::dolce_odoo`) and
28+
`callcenter::odoo_alignment::dolce_odoo`. They disagree on `.tax` (Quality vs
29+
Abstract), `.template` resolution (special-cased vs swept), `hr.*` default,
30+
and the unmatched-default behavior (Endurant vs Unknown). odoo-rs PR #15
31+
documented the full reconciliation table in `crates/od-ontology/src/alignment.rs`
32+
§ "Two-classifier disagreement". This finding scopes only to the silent
33+
suffix-rule drift; the broader two-classifier reconciliation is a Phase-3
34+
OGAR-side decision (cross-ref `od_ontology::specs/REPATRIATION-FRAME.md`
35+
Phase 3).
36+
37+
**Action shipped:**
38+
- `crates/lance-graph-ontology/src/hydrators/dolce_odoo.rs`: added `.groups`
39+
to `QUALITY_SUFFIXES`, `.settings` to `ABSTRACT_SUFFIXES`, module-doc
40+
notes record the rationale.
41+
- Four new regression tests pin: `res.groups` → Quality, `*.config.settings`
42+
→ AbstractEntity, plus the additive guards that the original singular
43+
rules still match.
44+
45+
**Source-of-finding:**
46+
[odoo-rs PR #15](https://github.com/AdaWorldAPI/odoo-rs/pull/15) +
47+
[odoo-rs PR #16](https://github.com/AdaWorldAPI/odoo-rs/pull/16)
48+
(repatriation-frame citations).
49+
150
## 2026-06-21 — E-EQUIVALENCE-IS-THE-CRUX — template-equivalence is the load-bearing verifier of the whole loop; it MUST fail closed, and it rides on transparent Lance versioning (surrealdb #50)
251

352
**Status:** FINDING (cross-session feedback, 2026-06-21). Reframing that

crates/lance-graph-ontology/src/hydrators/dolce_odoo.rs

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,36 @@ const PERDURANT_SUFFIXES: &[&str] = &[
6464
];
6565

6666
/// Name suffixes indicating a Quality (attribute / classification / rate).
67+
///
68+
/// NOTE on `.groups` (plural): the canonical Odoo class is `res.groups`,
69+
/// which ends in `.groups` (plural), NOT `.group`. The bare `.group` rule
70+
/// alone misses `res.groups` despite the comment claiming to match it.
71+
/// Both forms are listed so plural-form Odoo classes are caught. Surfaced
72+
/// 2026-06-23 by cross-validation against `od_ontology::alignment` (see
73+
/// `.claude/board/EPIPHANIES.md § E-DOLCE-ODOO-SILENT-SUFFIX-DRIFT`).
6774
const QUALITY_SUFFIXES: &[&str] = &[
6875
".tag", // crm.tag, account.account.tag
6976
".category", // product.category, res.partner.category
7077
".type", // account.account.type, sale.order.type
71-
".group", // res.groups, account.tax.group
78+
".groups", // res.groups (the canonical Odoo class — PLURAL)
79+
".group", // account.tax.group, *.group (singular variants)
7280
".tax", // account.tax (it's a rate, a quality, not an event)
7381
];
7482

7583
/// Name suffixes indicating an AbstractEntity (reference / config / template).
84+
///
85+
/// NOTE on `.settings`: the canonical Odoo settings shape is
86+
/// `*.config.settings` (e.g. `res.config.settings`, `sale.config.settings`),
87+
/// which ends in `.settings`, NOT `.config`. The bare `.config` rule alone
88+
/// misses `*.config.settings` despite the comment claiming to match it.
89+
/// Both forms are listed so settings models are caught regardless of the
90+
/// trailing segment. Surfaced 2026-06-23 by cross-validation against
91+
/// `od_ontology::alignment` (see
92+
/// `.claude/board/EPIPHANIES.md § E-DOLCE-ODOO-SILENT-SUFFIX-DRIFT`).
7693
const ABSTRACT_SUFFIXES: &[&str] = &[
7794
".template", // mail.template, account.chart.template
78-
".config", // *.config.settings
95+
".settings", // *.config.settings (res.config.settings, sale.config.settings)
96+
".config", // *.config (catches direct *.config models)
7997
".policy", // any *.policy
8098
".rule", // account.reconcile.model rules
8199
".formula", // hr.payroll.structure.line formulas
@@ -160,4 +178,55 @@ mod tests {
160178
DolceCategory::Endurant
161179
);
162180
}
181+
182+
// ─── Regression: silent suffix-rule drift caught by cross-validation ───
183+
//
184+
// The two assertions below would fail BEFORE the 2026-06-23 fix in this
185+
// module that added `.groups` (plural) to QUALITY_SUFFIXES and `.settings`
186+
// to ABSTRACT_SUFFIXES. The previous module-doc comments claimed to match
187+
// `res.groups` (via `.group`) and `*.config.settings` (via `.config`) but
188+
// the rules didn't actually catch those class names. See
189+
// `.claude/board/EPIPHANIES.md § E-DOLCE-ODOO-SILENT-SUFFIX-DRIFT`.
190+
191+
#[test]
192+
fn classifies_res_groups_as_quality_via_plural_suffix() {
193+
// `res.groups` (plural) is the canonical Odoo class — `res.users`'s
194+
// role/permission cohort. Must classify as Quality (a classification
195+
// dimension), not get swept into the default (Endurant) bucket.
196+
assert_eq!(classify_odoo("res.groups"), DolceCategory::Quality);
197+
assert_eq!(classify_odoo("odoo:res.groups"), DolceCategory::Quality);
198+
}
199+
200+
#[test]
201+
fn classifies_config_settings_models_as_abstract_via_settings_suffix() {
202+
// `*.config.settings` is the canonical Odoo settings shape — a
203+
// configuration form, an AbstractEntity (rule/policy/template). The
204+
// `.settings` suffix catches it; `.config` alone never matched it.
205+
assert_eq!(
206+
classify_odoo("res.config.settings"),
207+
DolceCategory::AbstractEntity
208+
);
209+
assert_eq!(
210+
classify_odoo("sale.config.settings"),
211+
DolceCategory::AbstractEntity
212+
);
213+
assert_eq!(
214+
classify_odoo("odoo:res.config.settings"),
215+
DolceCategory::AbstractEntity
216+
);
217+
}
218+
219+
#[test]
220+
fn singular_dot_group_still_matches_for_tax_group_style_classes() {
221+
// Adding `.groups` must NOT accidentally drop the original `.group`
222+
// singular match for `*.group` / `account.tax.group` / etc.
223+
assert_eq!(classify_odoo("account.tax.group"), DolceCategory::Quality);
224+
}
225+
226+
#[test]
227+
fn singular_dot_config_still_matches_for_direct_config_classes() {
228+
// Adding `.settings` must NOT drop the original `.config` match for
229+
// direct `*.config` models.
230+
assert_eq!(classify_odoo("crm.config"), DolceCategory::AbstractEntity);
231+
}
163232
}

0 commit comments

Comments
 (0)