feat(odoo-spo): FK target/inverse_name (P1) + deep reads_field (P0) corpus enrichment#523
Conversation
|
Warning Review limit reached
More reviews will be available in 54 minutes and 54 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 (7)
📝 WalkthroughWalkthroughA new stdlib-only Python module ChangesOdoo SPO Corpus Enrichment
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. 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 |
…orpus enrichment
Implements the odoo-rs UPSTREAM_WISHLIST P1 (FK target override) + coupled P0
(deep reads_field) enrichment of the Odoo SPO corpus, lance-graph only.
The coupling: P0 is built ON P1 — lifting @api.depends('line_ids.amount_residual')
into a deep read requires resolving line_ids → account.move.line, which IS the P1
target map. So P1 first (relation map), then P0 resolves dotted depends through it.
New stdlib-only generator
tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py:
- builds (model, field) → (comodel, inverse) from /home/user/odoo/addons via ast
(the same source the ORM extractor parses);
- P1: emits target/inverse_name sibling triples keyed by the relation IRI,
matching the cross-language-ratified shape from AdaWorldAPI/ruff#18
(WorkPackage.owner, class_name, "User") → (account_move.line_ids, target,
"account.move.line") + (…, inverse_name, "move_id"). Object is the raw dotted
comodel string so od-ontology RelationMap::from_corpus reads it directly;
- P0: resolves each dotted @api.depends path through the target map and lifts a
deep reads_field onto the field's emitting method, in addition to the existing
shallow relation read. Self-loops dropped; unknown-hop paths skipped + counted.
Additive, deterministic, idempotent ((s,p,o) dedup; re-run adds 0).
Corpus odoo_ontology.spo.ndjson regenerated: 22 245 → 23 701 triples
(+618 target, +102 inverse_name, +736 deep reads_field; 567 unknown-hop skips).
odoo_ontology.rs: module-doc + provenance updated, count test 22 245→23 701,
histogram extended (target/inverse_name/reads_field), 2 new enrichment tests.
14 new spo_enrich unit tests.
Generation-path finding: the corpus's documented original generator
(emit_ontology2.py over methods.parquet) is absent from the tree — only its
output is committed; the blueprint extractor emits typed Rust OdooEntity consts
(a separate artifact). Enrichment runs over the shipped corpus + present Odoo
source, the correct additive stage.
Cross-repo validation (local only, no odoo-rs commit): od_ontology::RecomputeDag
on a slice-2-scoped enriched fixture. Baseline 0 cross-model compute edges →
enriched 27, with the edge account_move_line._compute_amount_residual →
account_move._compute_amount now PRESENT (the wishlist's P0 ask delivered). But
the audit's MISSED-1 is a unidirectional ORDERING edge, not a cycle (move depends
on line; line does not depend back on move's totals), so odoo-rs's
slice_2_compute_subset_no_cross_model_cycle no-cycle assertion legitimately still
holds. Reported, not faked.
Tests: cargo test -p lance-graph --lib odoo_ontology 9/9 + action_emitter/spo
green; python -m unittest tests.test_spo_enrich 14/14 + uom smoke green.
cargo fmt clean; lance-graph crate clippy clean (the -D warnings deprecation
errors are pre-existing v2-layout debt in causal-edge/p64-bridge/lance-graph-
planner, untouched here).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
37b827f to
69a3b0a
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37b827faca
ℹ️ 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".
| # field IRI → emitting method IRI (for the deep-read lift target) | ||
| field_emitter: Dict[str, str] = { | ||
| t["s"]: t["o"] for t in triples if t["p"] == "emitted_by" | ||
| } |
There was a problem hiding this comment.
Preserve all emitters when lifting deep reads
When a field has multiple emitted_by triples, this dict keeps only the last method, so each deep depends_on path is lifted to one emitter and the other methods lose their reads_field edges. The shipped corpus has such cases: stock_move.quantity is emitted by both _compute_quantity and _onchange_product_uom_qty, but only the latter gets the deep reads for move_line_ids.quantity/product_uom_id, leaving downstream recompute ordering incomplete for the computed method. Store all emitters and emit the deep read for each one.
Useful? React with 👍 / 👎.
| if model_name is not None: | ||
| mu = model_to_underscore(model_name) | ||
| for field_name, (comodel, inverse) in local_fields.items(): | ||
| # Last write wins across _inherit reopenings of the same model; | ||
| # comodel for a given (model, field) is stable in practice. | ||
| relmap[(mu, field_name)] = (comodel, inverse) |
There was a problem hiding this comment.
Honor _inherit-only model extensions
When an Odoo addon extends an existing model with _inherit = "some.model" and no _name, which is the common extension form, model_name remains None and the relational fields collected in local_fields are discarded here. Those fields then never receive target/inverse_name, and deep depends paths through them are skipped as unknown hops; the existing class parser in this package already maps _inherit to the model name, so the enrichment should do the same.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
tools/odoo-blueprint-extractor/tests/test_spo_enrich.py (1)
131-233: ⚡ Quick winAdd a regression test for
_inherit-only model extensions.Current tests validate
enrich/resolve_path, but not the AST extraction path that buildsrelmapfrom_inherit-only classes. A focused fixture test would prevent silent regressions in relation discovery.🤖 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 `@tools/odoo-blueprint-extractor/tests/test_spo_enrich.py` around lines 131 - 233, Add a new test class or test method to validate the AST extraction path for _inherit-only model extensions. The test should create a fixture that represents a model using only _inherit (without any new fields), then verify that the relmap is correctly built from this inheritance-only class definition and that relation discovery works properly. This will prevent regressions in the AST extraction and relmap building logic for models that extend other models through inheritance alone.
🤖 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 @.claude/board/AGENT_LOG.md:
- Around line 3-10: The scope description in the first line stating the work is
"lance-graph only" is inaccurate because the shipped components include both the
Python extractor module
(tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py) and its
test suite (tools/odoo-blueprint-extractor/tests/test_spo_enrich.py) in addition
to the lance-graph changes. Update the scope text to accurately reflect that
this work spans both the Python extraction tooling and the lance-graph corpus
enrichment, removing the "lance-graph only" restriction so the board log matches
the actual diff surface.
In @.claude/board/EPIPHANIES.md:
- Around line 9-12: The document claims 27 compute edges and no-cycle acyclicity
that were verified locally but the verification cannot be reproduced. Add a
persistent test fixture or validation mechanism: either create a slice-2-scoped
enriched corpus within this repo with a validation script that runs the
enrichment generator and asserts both the edge count and acyclicity using
RecomputeDag, or add a documented cross-repo reference to odoo-rs's test suite
that validates these claims against the no-cycle assertion. This makes the claim
auditable and re-runnable by future reviewers instead of relying on discarded
local worktree validation.
In `@tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py`:
- Around line 124-129: The _scan_file function currently only binds relational
fields when the _name attribute is present, causing fields declared on extension
classes that use _inherit without _name to be dropped from the relation map. Add
handling for the _inherit attribute similar to the existing _name handling: when
target_name equals _inherit, extract the inherited model name(s) using
_const_str and store them appropriately so that relational fields on
inheritance-only classes are properly captured. Apply this fix in the code block
checking target_name == "_name" and also in the additional location mentioned at
lines 166-171 where similar model name binding occurs.
---
Nitpick comments:
In `@tools/odoo-blueprint-extractor/tests/test_spo_enrich.py`:
- Around line 131-233: Add a new test class or test method to validate the AST
extraction path for _inherit-only model extensions. The test should create a
fixture that represents a model using only _inherit (without any new fields),
then verify that the relmap is correctly built from this inheritance-only class
definition and that relation discovery works properly. This will prevent
regressions in the AST extraction and relmap building logic for models that
extend other models through inheritance alone.
🪄 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: bb59f93e-15b6-40d1-a829-2d3e64419e26
📒 Files selected for processing (7)
.claude/board/AGENT_LOG.md.claude/board/EPIPHANIES.md.claude/board/LATEST_STATE.mdcrates/lance-graph/src/graph/spo/odoo_ontology.rscrates/lance-graph/src/graph/spo/odoo_ontology.spo.ndjsontools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.pytools/odoo-blueprint-extractor/tests/test_spo_enrich.py
| **Main thread (Opus) — single implementer**, branch `claude/odoo-spo-fk-target-deep-reads`. Implements the odoo-rs `UPSTREAM_WISHLIST` P1 (FK `target`/`inverse_name`) + coupled P0 (deep `reads_field`) corpus enrichment, lance-graph only. | ||
|
|
||
| **Shipped:** | ||
| - `tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py` (new, stdlib-only): builds a `(model, field) → (comodel, inverse)` relation map from `/home/user/odoo/addons` via `ast`, then (P1) emits `target`/`inverse_name` sibling triples keyed by the relation IRI (ruff#18 shape, raw dotted comodel object) for every relational field on a corpus-declared model, and (P0) resolves each dotted `@api.depends` path through the map and lifts a deep `reads_field` onto the field's emitting method. Additive, deterministic, idempotent (`(s,p,o)` dedup); self-loops dropped; unknown-hop paths skipped + counted. CLI: `python3 -m odoo_blueprint_extractor.spo_enrich --corpus … --addons …`. | ||
| - `crates/lance-graph/src/graph/spo/odoo_ontology.spo.ndjson` regenerated: 22 245 → **23 701** triples (**+618 `target`, +102 `inverse_name`, +736 deep `reads_field`**; 567 dotted-path skips for unknown hops). | ||
| - `crates/lance-graph/src/graph/spo/odoo_ontology.rs`: module-doc updated (new predicates + provenance/regeneration note), triple-count test 22 245→23 701, histogram test extended (`target`=618, `inverse_name`=102, `reads_field`=2 831), 2 new tests (`enrichment_emits_fk_target_and_inverse_name`, `enrichment_emits_cross_model_deep_reads_field`). | ||
| - `tools/odoo-blueprint-extractor/tests/test_spo_enrich.py` (new): 14 unittest cases (path resolution, P1/P0 emission, dedup/idempotence, self-loop drop, unknown-model guard). | ||
|
|
There was a problem hiding this comment.
Scope claim is too narrow.
This entry says the PR is "lance-graph only", but the shipped list also includes the Python extractor and its test suite. Please widen the scope text so the board log matches the actual diff surface.
🤖 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 @.claude/board/AGENT_LOG.md around lines 3 - 10, The scope description in the
first line stating the work is "lance-graph only" is inaccurate because the
shipped components include both the Python extractor module
(tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py) and its
test suite (tools/odoo-blueprint-extractor/tests/test_spo_enrich.py) in addition
to the lance-graph changes. Update the scope text to accurately reflect that
this work spans both the Python extraction tooling and the lance-graph corpus
enrichment, removing the "lance-graph only" restriction so the board log matches
the actual diff surface.
| **The honest correction — MISSED-1 is an ordering edge, not a cycle (cross-repo-verified).** The task framing expected the deep-read lift to make odoo-rs's `slice_2_compute_subset_no_cross_model_cycle` test's no-cycle assertion *fail* (a cycle appears). It does **NOT**, and faking it would be wrong. Verified locally against `od_ontology::RecomputeDag` on a slice-2-scoped enriched fixture: | ||
| - **Baseline (surface reads):** 0 cross-model compute edges. `_compute_amount`'s only relevant read is the relation `account_move.line_ids` (no emitter → no edge). The line→move dependency is structurally invisible. | ||
| - **Enriched (deep reads):** the edge `account_move_line._compute_amount_residual → account_move._compute_amount` IS present (27 compute edges total). This is **exactly the wishlist's P0 acceptance** — the cross-model recompute-ordering dependency becomes a structural annotation, retiring the hand-audit ritual. | ||
| - **But still acyclic.** The dependency is *unidirectional*: `account.move._compute_amount` reads line fields; `account.move.line._compute_amount_residual` reads only same-model line fields and never reads back any field `_compute_amount` emits (verified: no method reads any `_compute_amount` output). The audit's "logically circular" framing is a *semantic* circularity (filtering by a computed `reconciled` field inside the body), not a `reads_field`↔`emitted_by` structural back-edge. So `RecomputeDag` correctly sees an ordering edge (line-before-move), not a cycle, and the no-cycle assertion legitimately still holds. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "account_move_line\\._compute_amount_residual|account_move\\._compute_amount|RecomputeDag|slice-2|27 compute edges|MISSED-1" \
crates tools .claude/boardRepository: AdaWorldAPI/lance-graph
Length of output: 14845
🏁 Script executed:
# Check if the 27-edge count is validated in any test
rg -n "27.*compute.*edge|edge.*count|compute_edges|RecomputeDag" \
--type rust --type python crates tools .claude/board -A 2 | head -100Repository: AdaWorldAPI/lance-graph
Length of output: 98
🏁 Script executed:
# Look for test files related to RecomputeDag in odoo-rs (might be referenced)
fd -type f \( -name "*test*" -o -name "*spec*" \) crates tools | grep -i odooRepository: AdaWorldAPI/lance-graph
Length of output: 238
🏁 Script executed:
# Check if spo_enrich.py or its tests validate the edge enrichment
cat -n tools/odoo-blueprint-extractor/tests/test_spo_enrich.py | grep -i "27\|edge\|comput" | head -20Repository: AdaWorldAPI/lance-graph
Length of output: 618
🏁 Script executed:
# Search for edge count validation in tests and source
rg "27" tools/odoo-blueprint-extractor/tests/test_spo_enrich.pyRepository: AdaWorldAPI/lance-graph
Length of output: 49
🏁 Script executed:
# Check the actual test file to understand what it validates
head -50 tools/odoo-blueprint-extractor/tests/test_spo_enrich.pyRepository: AdaWorldAPI/lance-graph
Length of output: 1570
🏁 Script executed:
# Search for RecomputeDag tests in odoo-rs references or any test that validates the graph
rg -i "no.*cycle|acyclic|topolog" crates tools .claude/boardRepository: AdaWorldAPI/lance-graph
Length of output: 50380
🏁 Script executed:
# Check if there's a reference to the odoo-rs repository commit or test in the board
rg -i "d8a270d|odoo-rs.*commit" .claude/boardRepository: AdaWorldAPI/lance-graph
Length of output: 5307
Point to a reproducible test or fixture that validates the 27-edge + no-cycle claim.
The enrichment generator (tools/odoo-blueprint-extractor/spo_enrich.py) and its unit tests are auditable, but the entry's "verified locally" claim lacks a persistent reference. The local worktree validation was discarded. Add either:
- A test fixture within this repo (slice-2-scoped enriched corpus + a validation script that asserts edge count and acyclicity), or
- A cross-repo reference to odoo-rs's test suite (once they adopt the enriched corpus) that validates the no-cycle assertion against
RecomputeDag.
The board doc (AGENT_LOG.md) captures the findings, but not in a form a reviewer can re-run.
🤖 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 @.claude/board/EPIPHANIES.md around lines 9 - 12, The document claims 27
compute edges and no-cycle acyclicity that were verified locally but the
verification cannot be reproduced. Add a persistent test fixture or validation
mechanism: either create a slice-2-scoped enriched corpus within this repo with
a validation script that runs the enrichment generator and asserts both the edge
count and acyclicity using RecomputeDag, or add a documented cross-repo
reference to odoo-rs's test suite that validates these claims against the
no-cycle assertion. This makes the claim auditable and re-runnable by future
reviewers instead of relying on discarded local worktree validation.
| # _name = 'account.move.line' | ||
| if target_name == "_name": | ||
| s = _const_str(stmt.value) | ||
| if s is not None: | ||
| model_name = s | ||
| continue |
There was a problem hiding this comment.
Handle _inherit-only model classes in relation-map extraction.
_scan_file only binds fields when _name exists, so relational fields declared on extension classes that define _inherit (without _name) are dropped from relmap. That causes missing enrichment output and incomplete deep-hop resolution.
Proposed fix
- model_name: Optional[str] = None
+ model_names: List[str] = []
local_fields: Dict[str, Tuple[str, Optional[str]]] = {}
@@
- # _name = 'account.move.line'
- if target_name == "_name":
+ # _name = 'account.move.line'
+ if target_name == "_name":
s = _const_str(stmt.value)
if s is not None:
- model_name = s
+ model_names = [s]
+ continue
+
+ # _inherit = 'account.move' OR _inherit = ['account.move', ...]
+ if target_name == "_inherit" and not model_names:
+ s = _const_str(stmt.value)
+ if s is not None:
+ model_names = [s]
+ elif isinstance(stmt.value, (ast.List, ast.Tuple)):
+ vals = [
+ _const_str(elt)
+ for elt in stmt.value.elts
+ if _const_str(elt) is not None
+ ]
+ if vals:
+ model_names = vals
continue
@@
- if model_name is not None:
- mu = model_to_underscore(model_name)
- for field_name, (comodel, inverse) in local_fields.items():
- # Last write wins across _inherit reopenings of the same model;
- # comodel for a given (model, field) is stable in practice.
- relmap[(mu, field_name)] = (comodel, inverse)
+ for model_name in model_names:
+ mu = model_to_underscore(model_name)
+ for field_name, (comodel, inverse) in local_fields.items():
+ relmap[(mu, field_name)] = (comodel, inverse)Also applies to: 166-171
🤖 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 `@tools/odoo-blueprint-extractor/odoo_blueprint_extractor/spo_enrich.py` around
lines 124 - 129, The _scan_file function currently only binds relational fields
when the _name attribute is present, causing fields declared on extension
classes that use _inherit without _name to be dropped from the relation map. Add
handling for the _inherit attribute similar to the existing _name handling: when
target_name equals _inherit, extract the inherited model name(s) using
_const_str and store them appropriately so that relational fields on
inheritance-only classes are properly captured. Apply this fix in the code block
checking target_name == "_name" and also in the additional location mentioned at
lines 166-171 where similar model name binding occurs.
…t-only Post-merge follow-up to #523 (merged at 69a3b0a before these landed). Both codex findings were real correctness gaps in the enrichment: 1. P1 multi-emitter deep-reads: the field→emitter index kept only the LAST emitted_by method, so a field emitted by several methods (e.g. stock_move.quantity ← _compute_quantity AND _onchange_product_uom_qty) lifted the deep reads_field to only one — the _compute_* lost its recompute-ordering edge. Now field → sorted set of emitters; deep read emitted per emitter (self-loop drop preserved). 2. P2/Major _inherit-only model extensions: _scan_file bound fields only when _name was present, dropping the COMMON Odoo extension form (_inherit="x" / ["a","b"] with no _name) — those relational fields never got target/inverse_name and deep hops through them were skipped. Now model_names binds from _name OR _inherit (string/list), mapping local fields onto each. Corpus regenerated: target 618→842, inverse_name 102→144, reads_field 2831→3030 (the previously-dropped _inherit fields + per-emitter deep reads). Doc nits: AGENT_LOG scope widened (Python extractor + tests, not "lance-graph only"); EPIPHANIES "verified locally" claim now cites an in-repo fixture test (test_spo_enrich) instead of the discarded worktree. Tests: extractor 14→20 (6 new fixture tests for both cases), lance-graph odoo_ontology 9→11, fmt clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
…ep-reads fix(odoo-spo): #523 review follow-up — multi-emitter deep-reads + _inherit-only models
What
Enriches the Odoo SPO corpus with two predicate families the odoo-rs
UPSTREAM_WISHLISTasks for — additive only, lance-graph side, no ClassView (Core-first: emit what the consumer assumes). ServesAdaWorldAPI/odoo-rsas a downstream consumer; no odoo-rs change here (their adoption is their call, per their "silence is alignment").Predicates added (corpus 22,245 → 23,701)
target×618 —(odoo:<model>.<rel>, target, "<comodel.dotted>"), the ruff#18 sibling-triple shape. Kills the phantom-target failure:(odoo:account_move.invoice_line_ids, target, "account.move.line")instead of an inventedrecord<invoice_line>.inverse_name×102 — One2many inverse field.reads_field×736 (2,095 → 2,831) —@api.depends('rel.leaf')now lifts the transitive(method, reads_field, <target_model>.<leaf>)using the new target map (P1 is the prerequisite for P0). 567 dotted-path hops skipped where the relation target is outside the scanned source (counted, never faked).Where it lives
The corpus's original generator (
emit_ontology2.pyovermethods.parquet) is absent from the tree — only its output is committed. Added a stdlib-only, deterministic, idempotenttools/odoo-blueprint-extractor/.../spo_enrich.pythat runs over the shipped corpus + the present Odoo source. The Rust loader (odoo_ontology.rs) gains the matching parse/emit surface.Honest correction — it delivers recompute ordering, not a cycle
The wishlist (and the original audit) framed MISSED-1 as a "P0 dependency cycle." Local validation against odoo-rs's shipped
RecomputeDag(throwaway worktree, nothing committed there) shows:account_move_line._compute_amount_residual → account_move._compute_amountis structurally invisible (0 cross-model compute edges).DEFINE FIELD/DEFINE FUNCTIONin the correct topological order, retiring the hand-audit. That is the wishlist's actual P0 value._compute_amountemits. The audit's "circular" framing is a semantic circularity (the body filters on a computedreconciled), not a structuralreads_field↔emitted_byback-edge.RecomputeDagcorrectly sees a line-before-move ordering edge, and itsslice_2no-cycle assertion legitimately still holds. The enrichment was not forced to fake a cycle.Tests
cargo test -p lance-graph --lib odoo_ontology9/9 (2 new:enrichment_emits_fk_target_and_inverse_name,enrichment_emits_cross_model_deep_reads_field);action_emitter/spogreen (function count 3,328 unchanged).python3 -m unittest tests.test_spo_enrich14/14 + existing uom smoke.cargo fmtclean;lance-graphcrate clippy clean (the workspace-D warningsdeprecation errors are pre-existing v2-layout debt incausal-edge/p64-bridge/lance-graph-planner— untouched; this diff has zeroCausalEdge64/temporal/inference_typerefs).Board:
EPIPHANIES.md(E-ODOO-FK-DEEP-READS — the P1→P0 coupling, the ruff#18-ratified shape, and the cycle-vs-ordering-edge correction),AGENT_LOG.md,LATEST_STATE.md— same commit.🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit
Release Notes
New Features
Tests
Documentation