feat(odoo-spo): inherits_from (P1b/ruff#19) + validation_kind (P2/ruff#21)#526
Conversation
|
Warning Review limit reached
More reviews will be available in 24 minutes and 58 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
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: c946e9d0d8
ℹ️ 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".
… + extend constraint binding to _inherit-only classes All three codex findings are real correctness issues. Fixing them tightens the validation_kind classifier so a regenerated corpus does not pollute downstream queries with false-positive kinds, and lifts the Odoo "extend-in-place" idiom (the same shape #525 fixed for relational fields). # codex #1 — `_inherit`-only classes' constraints attach to the bases The original block flushed `local_constrains` inside `if model_name is not None`, silently dropping `@api.constrains` methods on the common Odoo extension form (`_inherit='account.move'` without `_name`). Now `model_targets = [_name]` if set, else `list(_inherit)` underscored, and constraints attach to *each* target. Same shape #525 took for relational fields — kept consistent. # codex #2 — `search_count(...) > 1` is uniqueness ONLY, not also range The unconditional `Compare` walker added `range` for any `< | > | <= | >=`, including the canonical uniqueness pattern `<rs>.search_count(...) > 1`. That polluted every uniqueness method with a stray `validation_kind=range`. Added `_is_uniqueness_call(left)` guard so range is skipped when the LHS is a `search_count` call. Direct field-vs- bound checks (`self.amount < 0`, `self.amount > 1000000`) still classify as range. # codex #3 — `not re.match(...)` is format ONLY, not also presence The broad `UnaryOp(Not)` rule added `presence` for `if not re.match(...):` and `if not <rs>.search(...):` — the negated-call wrap of format / lookup checks, not presence checks. Restricted presence to truthiness of a *value-like* operand (`Name` / `Attribute` / `Subscript`); negated calls are skipped. Direct field truthiness (`if not self.partner:`) still classifies as presence. # Tests 8 new regression tests under `TestCodexFindings` (32 → 40 OK): - `test_constraints_bind_to_inherit_only_extension` - `test_constraints_bind_to_each_inherit_when_list` - `test_constraints_with_name_skip_inherit_targets` (specificity) - `test_search_count_gt_is_only_uniqueness` - `test_field_range_check_still_classifies_range` (specificity) - `test_negated_re_match_is_only_format` - `test_negated_search_is_only_lookup` - `test_direct_field_truthiness_is_still_presence` (specificity) Each kind has a paired specificity test ensuring the fix did not break the canonical pattern. python3 -m unittest tests.test_spo_enrich : 40/40 OK (was 32)
…f#21) Rebased on top of #525 (multi-emitter deep-reads + `_inherit`-only field binding). All three codex P2 findings from the pre-rebase iteration of #526 are baked in: `range` skipped when LHS is `search_count(...)`, `presence` restricted to `not <Name|Attribute|Subscript>`, and constraint binding mirrors #525's `model_names` decision (no mixin broadcast). # inherits_from (P1b, ruff#19 ratified shape) Wire shape identical to C++ / Rails: `(odoo:<this>, inherits_from, odoo:<base>)`. Sources: - `_inherit = 'mail.thread'` (single string) - `_inherit = ['mail.thread', 'mail.activity.mixin']` (list) - `_inherits = {'mail.thread': 'thread_id'}` (delegation; dict keys) Drops at scan time: - `_inherit == _name` ("extend-in-place" idiom) - `_inherit`-only classes (no `_name`) — the inheritance edge already exists at the original declaration site; extending in place does not create a new edge. Bases not in the corpus's `ogit:ObjectType` set are skipped at emission time + counted (`inherits_skip_unknown_base`). Truth `(0.95, 0.90)` — class-level declaration is authoritative. # validation_kind (P2, ruff#21 ratified shape) Wire shape mirrors ruff#21's per-attribute keying, subject flipped to the method IRI (Odoo's `@api.constrains` is method-level). Recognised kinds: - `format` — `re.match` / `re.fullmatch` / `re.search` - `uniqueness` — `<rs>.search_count(...)` - `range` — `< | > | <= | >=` where LHS is NOT a `search_count` call (codex P2 fix from pre-rebase #526) - `lookup` — `<rs>.search(...)` / `<rs>.browse(...)` - `presence` — `not <Name|Attribute|Subscript>` (codex P2 fix: `not <Call>` is the negated-call wrap for format/lookup, NOT presence) / `<expr> is None` Truth `(0.85, 0.75)` — AST classification is inferred. Constraint binding follows #525's `model_names`: - `_name = 'X'` → bind to `X` only - `_inherit = 'X'` (no `_name`) → bind to `X` only - `_inherit = ['X','Y']` (no `_name`) → bind to `X` only (inherit[0]) No mixin broadcast — the same conservative rule #525 codex review arrived at for fields. # Corpus regen pending The shipped `odoo_ontology.spo.ndjson` does not yet carry the new triples — regenerating requires running the script against a live Odoo source tree (`/home/user/odoo/addons`), which is not present on this host. The Rust loader's predicate-histogram match arm gained `inherits_from` + `validation_kind` so a future regenerated corpus drops into the harness without code changes. The `parses_all_triples` count assertion stays at 23 701 from #525; re-locks the moment a session with the source re-runs enrichment. # Files tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py: +helpers (_is_uniqueness_call, _is_api_constrains, _classify_constrains_body), +`inherits`/`constrains` kwargs on _scan_file + build_all_facts, +`_inherits` dict-key extraction, P1b + P2 emission loops, CLI status print extended. tools/odoo-blueprint-extractor/tests/test_spo_enrich.py: +18 tests over 5 classes: TestP1bInheritsFrom (8), TestP2ValidationKind (4), TestAstClassifier (6 — codex P2 specificity locks paired with canonical patterns), TestConstrainsScan (3 — model_names binding). crates/lance-graph/src/graph/spo/odoo_ontology.rs: +doc table row for `inherits_from` + `validation_kind`; +match arm in predicate_histogram_matches_extraction. .claude/board/EPIPHANIES.md: prepended E-ODOO-SPO-INHERITS-VALKIND. # Tests python3 -m unittest tests.test_spo_enrich : 41/41 OK (was 23 post-#525) cargo test -p lance-graph --lib odoo_ontology : 11/11 OK
c12a542 to
7947487
Compare
feat(odoo-spo): regenerate corpus with inherits_from + validation_kind (#526 follow-up)
…dation_kind Runs the deferred spo_enrich corpus regen (flagged by E-ODOO-SPO-INHERITS-VALKIND as "corpus regen pending Odoo source") against /home/user/odoo/addons. Completes the odoo-rs UPSTREAM_WISHLIST: P0 deep-reads (#525) was already materialized; this lands the P1b/P2 layer #526 added to the extractor but had not yet emitted. Corpus odoo_ontology.spo.ndjson: +413 triples, 24 166 -> 24 579, purely additive (0 deletions): - +166 inherits_from (ruff#19 cross-language inheritance predicate; _inherit / _inherits bases) - +247 validation_kind (ruff#21 per-@api.constrains kind) Idempotent on the P0 layer: 0 new target/inverse_name/deep reads_field. odoo_ontology.rs: count assert 24_166 -> 24_579; histogram test gains inherits_from=166 + validation_kind=247 asserts (now locked); provenance doc updated. Python extractor 41/41 green. Counts verified against the regenerated corpus histogram. Board: LATEST_STATE narrative entry; EPIPHANIES E-ODOO-SPO-INHERITS-VALKIND Status flipped (per-corpus CONJECTURE -> FINDING). Co-Authored-By: Claude <noreply@anthropic.com> https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
Summary
Extends
spo_enrich.pywith two more enrichment passes covering the remainingAdaWorldAPI/odoo-rsUPSTREAM_WISHLIST.mditems. Mirrors the same wire shapes the ruff stack already ratified.inherits_from_inherit) + P1 (_inherits)(class, inherits_from, <base>))validation_kind@api.constrains)Predicate::ValidationKind, per-attribute typed validation)What's new
inherits_from—(odoo:<this>, inherits_from, odoo:<base>)Wire shape identical to ruff#19's cross-language predicate. Sources:
_inherit = 'mail.thread'(single string)_inherit = ['mail.thread', 'mail.activity.mixin'](list)_inherits = {'mail.thread': 'thread_id'}(delegation; dict keys lift)Drops at scan time:
_inherit == _name)ogit:ObjectTypein the corpus (counted ininherits_skip_unknown_base)Truth
(0.95, 0.90)— class-level declaration is authoritative.validation_kind—(odoo:<model>.<method>, validation_kind, "<kind>")Wire shape mirrors ruff#21's per-attribute keying, with subject flipped to the method IRI (Odoo's
@api.constrainsis method-level, not attribute-level). Five recognised kinds detected by conservative AST patterns inside@api.constrains-decorated method bodies:formatre.match/re.fullmatch/re.searchuniqueness<rs>.search_count(...)range</>/<=/>=numeric comparisonslookup<rs>.search(...)/<rs>.browse(...)presencenot <expr>/<expr> is None/<expr> == NoneA body matching no pattern emits nothing — the existing
raisestriple still records that it can raise. Unknown kinds (forward-compat extension) silently dropped at emission. Truth(0.85, 0.75)— inferred.Architecture
build_all_facts(addons_root)is a single-pass AST walk that returns the existing relmap plus the newinheritsandconstrainsdicts.enrich()gains two optional kwargs (inherits=None,constrains=None) so existing callers keep working unchanged; the new emission loops run only when the facts are provided. Full backwards-compat.Corpus regen — pending Odoo source
The shipped
odoo_ontology.spo.ndjsondoes not yet carry the new triples — regenerating requires running the script against a live Odoo source tree (/home/user/odoo/addons), which is not present on this host:The Rust loader's predicate-histogram match arm gained
inherits_from+validation_kindso a future regenerated corpus drops into the same harness without code changes.parses_all_triplescount assertion stays at 23 701; re-locks the moment a session with the source re-runs enrichment.Test plan
python -m unittest tests.test_spo_enrich— 32/32 OK (was 14)_scan_fileshape lock)cargo test -p lance-graph --lib odoo_ontology— 9/9 OK/home/user/odoo/addons— pending next session with Odoo sourceConsumer-side follow-up (separate PR on
AdaWorldAPI/odoo-rs)When the corpus regenerates,
od_ontology::RecomputeDagautomatically sees the new inheritance edges; the consumer can mark wishlist items P1 (#1_inherit, #2_inherits) + P2 (#3validation_kind) as RESOLVED upstream and add aClassView-style lookup overinherits_fromfor MRO flattening (the P2 / virtually_overrides ask remains for a future ClassView design session).Generated by Claude Code