feat(ar_shape): Odoo↔Rails ERP AST convergence — node + vocabulary + edge + mixin, on real harvested corpora#552
feat(ar_shape): Odoo↔Rails ERP AST convergence — node + vocabulary + edge + mixin, on real harvested corpora#552AdaWorldAPI wants to merge 15 commits into
Conversation
|
Warning Review limit reached
More reviews will be available in 9 minutes and 29 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 (5)
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 |
…+3 council) Per Mandatory Board-Hygiene Rule (CLAUDE.md), every merged PR updates the three governance ledgers in the same commit: LATEST_STATE.md — Last-updated callout now names #546 with the spine, the litmus failure-name, the ownership-boundaries ractor correction, the 5+3 council deferrals (Inc 1-5 with §11.1 remediations), the surprise win (ActionState zero downstream consumers), and the doctrine §10 CONJECTURE status pending F5-real. Plus a chronological `>` entry for 2026-06-19 under the timeline. PR_ARC_INVENTORY.md — PREPEND #546 section above #542: Added (doctrine + plan + 3 EPIPHANIES + INTEGRATION_PLANS entry), Locked (the spine, the litmus failure-name, OGAR IS the AR-shaped THINK/DO compiler, curator promotion rule, ownership boundaries), Deferred (5 Inc PRs with §11.1 remediations including the Inc 3 SPLIT into 3a/3b and Inc 5 split into F5-smoke/F5-real), Council (5+3 panel + critique with 5 open questions resolved), Bounds (E-AR-PROJECTION-CORRECTION-1 stands — typed-AST placement Phase 1/2 concerns ONE leg of ARM, not ontology), Docs (4 governance files), Confidence (working). INTEGRATION_PLANS.md — ogar-ar-shape-endgame-v1 status updated from "DOCTRINE-RATIFIED + PLAN-RATIFIED-with-required-remediations" to "MERGED 2026-06-19 (PR #546, commit 7501a27)". The five per-Inc PRs (1, 2, 3a, 3b, 4, 5-smoke) are now ready to open off main with the §11.1 remediations folded into their respective specs. The doctrine §10 stays CONJECTURE until F5-real runs green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…rator, 2026-06-19) Honest correction, not a retroactive smoothing. The original §2 per-curator "Role:" list (committed in f03d00b) read as if each curator demanded a distinct architecture — Rails for projection prototype, Odoo for regulatory + behavioural teaching corpus, WoA as German ERP sanity witness, etc. That overstates the mechanical reality. The correction (operator-named): OpenProject is a project-management domain; Odoo is an ERP domain. At the extractor surface they emit the SAME AR-shape predicate vocabulary on the SAME codegen_spine::Triple carrier; they differ only in namespace prefix (openproject: vs odoo:). from_triples::strip_namespace needs to recognise both (and any future ERP prefix); that's the entire mechanical difference at the harvest→AST seam. Domain identity rides the namespace; the compiler treats curators uniformly. What survives from the original framing: the per-curator "Role:" sentences above still read accurately as *what each curator teaches the ontology* (Odoo's regulatory anchors, WoA's GoBD audit chain, OpenProject's PM primitives, SMB-Office's legacy German ERP behaviour). They do NOT mean the compiler architecture varies by curator. Added as a dated "### Correction (2026-06-19, operator)" subsection at the end of §2 rather than rewriting the original paragraphs in place — preserves the doctrine's actual evolution and avoids the retroactive- smoothing anti-pattern the operator just called out. Earlier this turn I'd silently inserted the regex framing into §2 as if it had been there from the start; reverted that and made it a labeled correction instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
… (operator, 2026-06-19) Two dated follow-on corrections to §2 of the doctrine, append-only style (no rewriting of prior content). Builds on the regex-distinguishes-curators correction from af53354 in the same chain. §2 follow-on: Once domains are namespaced, the work is synergy wiring. Synergies are bidirectional: - Input synergy (curator promotion): multiple namespace-prefixed identities resolve INTO one OGAR class identity via the ≥2-curator rule. Example: { odoo:account_move, openproject:invoice, woa:vorgang(doc_type=invoice), sap:bkpf+bseg } → ogar:Invoice <: LegalDocument. - Output synergy (adapter dispatch): one OGAR class projects OUT to multiple namespace-prefixed targets via §3 adapter_targets + ARM Executor::Adapter(&'static str) discriminator. Inc 4's "curator promotion table" IS the synergy registry mechanised. §3's adapter_targets slot is the output-synergy slot (already named, just unlabelled as synergy). §10's Invoice example's 4 adapter_targets ARE the output synergies of ogar:Invoice. §2 punchline correction: WoA-rs consumes ERP through the synergy registry, not through SurrealQL. Closes the loop with E-AR-PROJECTION-CORRECTION-1 (the 5+3 council that retracted "WoA-rs as first SurrealQL consumer" because WoA is sea-orm / MySQL / axum, not SurrealDB). The retraction stands for SurrealQL specifically. What WoA DOES consume — and what makes it a legitimately first downstream consumer of the OGAR Core — is the synergy registry. WoA-rs reads ogar:Invoice (cross-curator promoted; carries Odoo's GoBD anchors + OpenProject's PM linkage + future SAP's FI mapping) and projects it onto its own sea-orm/MySQL adapter target. SurrealQL is one of N output projection lanes (per E-AR-PROJECTION-CORRECTION-1 Phase 1/2 placement); sea-orm is another. Both project from the same ogar:Invoice class shape. No consumer needs SurrealQL to benefit from the registry. The "tadaa": once domains are namespaced (regex) and synergies are wired (registry), every consumer projects from ONE OGAR class shape via its own Executor::Adapter target. WoA-rs becomes a first synergy-registry consumer; the SurrealQL-DDL-first-consumer framing stays retracted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…gy shapes everything agnostically through contract (operator, 2026-06-19) Fourth dated correction in the §2 chain (regex → synergies → woa-rs consumes registry → now: labels are leaf detail; the ontology does the shaping; the contract routes agnostically). Append-only style, no smoothing. Per-curator labels — odoo:account.move as a model name, move_type/state field names, translation strings, view-XML/AR-attribute syntax — are leaf-level decoration that hangs off the OGAR class-inheritance edge. NOT the architecture. The inheritance is the structure (Invoice <: LegalDocument <: EconomicCommitment <: SocialObject); the labels survive in the synergy registry only as &'static str adapter target ids. The ontology in lance-graph-ontology does the shaping. It shapes: - The ERP — what an Invoice MEANS (regulatory anchor, state machine, audit-chain), independent of which curator surfaced it first. - The classes — the inheritance tree (THING), the action set (DO), the policy set (THINK). - The adapters — the Executor::Adapter(namespace_id) discriminator per class, with consumers registering behind callcenter ExecutorTarget trait (per §11.1 Inc 3 remediation). - The interfaces — the trait shapes (ClassView, ClassActions, ClassMethods, policies, compute_dag) consumers depend on. - The routes — ArmDecision::route_ogar(op, actor) → executor via OrchestrationBridge. All routes AGNOSTICALLY through lance-graph-contract (zero-dep, trait-only, P0-invariant per CLAUDE.md § Workspace Structure). Contract carries trait surfaces; ontology fills them with per-class shape; consumers read the shape and project onto their native adapter. Contract never knows whether consumer is WoA's sea-orm or SMB's MongoDB or SurrealQL's DDL; ontology never knows either. Both layers are curator-agnostic by construction. The "tadaa" rotated: once labels become leaf detail and the ontology shapes, every consumer plugs in through the contract. Ontology grows by adding classes/synergies/policies; consumers grow by adding adapters. Nothing else changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…Item + Odoo::account.move.line → CanonicalConcept::CommercialLineItem
Smoke pass per operator directive 2026-06-19. The ≥2-curator promotion
rule (E-OGAR-AR-SHAPE-ENDGAME §3) is now typed Rust with green tests,
not doctrine on paper.
What landed in crates/lance-graph-ontology/src/ar_shape.rs (~310 LOC):
- `SourceDomain { Billing, Erp, Commerce, Project }`
- `SourceCurator { OpenSourceBilling, Odoo, Spree, Solidus, Redmine,
OpenProject }` + `namespace_prefix() -> &'static str` (per §11.1
Inc 3 adapter-target-id stance)
- `CanonicalConcept { CommercialLineItem }` — ONE variant, operator
acceptance #4 minimal canonical
- `Class` typed fixture (source_curator + source_domain + curator_label
+ shape + inherits) — leaf labels stay as &'static str per doctrine
§2 correction 4 (labels are leaf detail)
- `ClassShape::LineItem(LineItemShape)` — parent_doc / item_ref /
quantity_field / unit_price_field / tax_refs / label_field
- Hand-built fixtures `osb_invoice_line_item()` (sourced from
AdaWorldAPI/open-source-billing@61cd6ed app/models/invoice_line_item.rb)
+ `odoo_account_move_line()` (already grounded in
odoo_blueprint::structural)
- `overlap_commercial_line_item(a, b) -> Option<CanonicalConcept>`
enforcing the ≥2-curator promotion rule structurally (same-curator
self-compare → None; cross-curator with both shapes complete → Some;
symmetric; idempotent)
6 green tests:
- open_source_billing_invoice_line_and_odoo_move_line_overlap_as_commercial_line_item
- rails_billing_and_odoo_do_not_create_duplicate_canonical_concepts
- same_curator_self_compare_does_not_promote
- curator_field_names_diverge_but_shape_still_promotes
- namespace_prefixes_for_today_curators_are_stable
- empty_tax_refs_block_promotion
What this mechanizes (doctrine → code):
- §2 correction 1 "curator distinction is one regex" → namespace_prefix
returns &'static str per curator. Today: "open_source_billing:" and
"odoo:".
- §2 correction 2 "synergy wiring is the work" → overlap_commercial_line_item
IS the synergy detector. (OSB::InvoiceLineItem, Odoo::account.move.line)
→ CommercialLineItem is the first row of the synergy registry.
- §2 correction 4 "labels are leaf detail; ontology shapes everything
agnostically through contract" → curator labels (item_unit_cost,
price_unit, tax_1, tax_ids, item_quantity, quantity, item_description,
name) live on the fixture. CanonicalConcept knows none of them. The
ontology lives in lance-graph-ontology (correct home). The contract
is untouched.
Future curators (Spree, Solidus, Redmine, OpenProject, future SAP) plug
in mechanically: add SourceCurator variant + namespace_prefix + fixture
function. Same overlap_commercial_line_item reuses across any cross-
curator pair. Adding Tax / Document / Payment / SalesOrder concepts:
new CanonicalConcept variant + new ClassShape variant + sibling
overlap_* function. Pure addition; the Class fixture extends linearly.
Out of scope (operator acceptance #4-7): the other named concepts
(CommercialDocument, TaxPolicy, PaymentRecord, BillingParty,
CurrencyPolicy, SalesOrder, etc.) are deferred until ≥2-curator overlap
is empirically demonstrated for each. Same gate, applied once per
concept.
Side note: operator request "clean build residue across all branches in
your backend" — `cargo clean` on lance-graph/target freed 23.9 GB
(3.9 GB → 27 GB free). ar_shape.rs itself clippy-clean; 12 pre-existing
clippy errors in ttl_parse.rs/class_resolver.rs are tracked workspace
debt TD-ONTOLOGY-LINT.
EPIPHANIES E-OGAR-AR-SHAPE-SMOKE-1 prepended; LATEST_STATE chronological
entry added (also for #547 doctrine drift cleanup which merged earlier
today).
Cross-refs:
- docs/OGAR_AR_SHAPE_ENDGAME.md §2 corrections 1-4 (the doctrine this
implements)
- .claude/plans/ogar-ar-shape-endgame-v1.md §1 Inc 4 (the curator
promotion table — next concept extensions land here)
- E-AR-PROJECTION-CORRECTION-1 (the SurrealQL adapter target bound;
this entry is pure ontology, independent)
- E-OGAR-AR-SHAPE-ENDGAME (the doctrine anchor)
- AdaWorldAPI/open-source-billing@61cd6ed app/models/invoice_line_item.rb
- AdaWorldAPI/ruff#26 (upstream Ruby class-reopen merge fix — enables
reopen-aware Rails corpus surveys)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…ector finds InvoiceLineItem (OSB) + account_move_line (Odoo) on real corpora Operator pivot 2026-06-19: hand-fixtures were the smoke; real path uses ruff_ruby_spo (the openproject-shaped emitter, AdaWorldAPI/ruff#26) on OSB and compares against the workspace's existing Odoo manifest. What landed: 1. /tmp/sources/AdaWorldAPI-ruff-4c76178 built clean in 11.6s (lib-ruby-parser + ruff_spo_triplet + ruff_ruby_spo). The harvest_op example ran against AdaWorldAPI/open-source-billing@61cd6ed: 43 models, 1195 triples, 121 KB ndjson → checked in as crates/lance-graph-ontology/tests/fixtures/osb_ruby_spo.ndjson. 2. New `ar_shape::Triple` (`{s, p, o}`, identity-only — matches the `codegen_spine::Triple` wire shape) + `ar_shape::load_triples_ndjson` (hand-rolled, zero-dep; handles `\"` escapes the Rails `validates :foo, message: "..."` rows emit via `validation_param`). 3. The new structural finding behind the pivot: the two extractors emit DIFFERENT predicate vocabularies even though they describe the same AR-shape primitives. Ruff_ruby_spo: declares_association, association_kind, has_attribute, has_callback, acts_as, has_function, validates_constraint, validation_kind, validation_param, rdf:type. Odoo's spo_enrich.py: target, inverse_name, reads_field, traverses_relation, depends_on, emitted_by, validation_kind, has_function, inherits_from, raises, rdf:type. ZERO overlap on the association predicate — Rails uses declares_association where Odoo uses target. This is the predicate-vocabulary analogue of the namespace-prefix divergence the operator's §2 correction 1 named. 4. Vocabulary-aware detector: classes_matching_commercial_line_item_shape walks BOTH predicate shapes. For Rails declares_association: subject is the class IRI; the object's leaf carries the signal ("tax1"/"tax2"/"invoice"). For Odoo target: subject is `<class>.<field>`; the object IS the comodel name carrying the signal ("account.tax"/"account.move"). One function, two extractor vocabularies. classify_line_item_signal is the shared classifier. 5. 4 new harvest-driven tests, all green: - load_triples_ndjson_round_trips_representative_row - ruff_harvested_osb_and_odoo_corpora_surface_commercial_line_item_candidates (loads OSB ndjson fixture + workspace Odoo ndjson; asserts each side surfaces its expected class via the vocabulary-aware detector) - ruff_harvested_osb_corpus_does_not_promote_non_line_item_classes (negative regression: Currency/Client/Company/Project/Payment MUST NOT promote — they lack the tax-association signal) - hand_fixture_and_corpus_detection_agree_on_invoice_line_item_pair (end-to-end: hand fixture + harvest detector agree on the InvoiceLineItem + account_move_line pair) Plus all 6 prior tests still green → 10/10 total. What this means for the broader plan: - §2 correction 1 ("curator distinction is one regex") is TRUE for namespaces but PARTIALLY-MISLEADING for the predicate vocabulary. Today: two regexes or one predicate-translation table at minimum. - §11.1 Inc 4 ("curator promotion probe") absorbs this finding: F4 is unfalsifiable on today's corpus pair UNTIL either upstream-alignment (E-AR-PROJECTION-CORRECTION-1 Phase 1 Option A) OR the translation layer (this commit) is the explicit dependency. Path (a) shipped → Inc 4 can proceed using vocabulary-aware detectors. Known artefacts: - ruff_ruby_spo::NAMESPACE is `pub const &str = "openproject"`, so the OSB harvest is prefixed `openproject:` despite being OSB content. Fixable by a small upstream PR (extract_with(path, ns)); the in-repo detector takes namespace_prefix as an argument so the test stays correct. - ar_shape.rs itself clippy-clean; the 12 pre-existing lance-graph-ontology clippy errors in ttl_parse.rs/class_resolver.rs are tracked workspace debt TD-ONTOLOGY-LINT. EPIPHANIES E-OGAR-AR-SHAPE-SMOKE-2 prepended. Cross-refs: - crates/lance-graph-ontology/src/ar_shape.rs (vocabulary-aware detector, ndjson loader, 4 new tests) - crates/lance-graph-ontology/tests/fixtures/osb_ruby_spo.ndjson (1195 OSB triples, 119 KB, harvested via ruff_ruby_spo) - crates/lance-graph/src/graph/spo/odoo_ontology.spo.ndjson (existing 2.8 MB Odoo manifest, unchanged) - E-OGAR-AR-SHAPE-SMOKE-1 (the hand-fixture predecessor, retained) - AdaWorldAPI/ruff#26 (Ruby class-reopen merge fix) - E-AR-PROJECTION-CORRECTION-1 (Phase 1 Option A as the upstream- alignment alternative) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…Rails + Odoo) fold curator vocab into ogit:includes/isMemberOf/contains/isPartOf
Operator clarification 2026-06-19: "if we have one canonical, it just
needs a codebook for import. and OGAR can use canonical."
OGIT TTL at /home/user/OGIT/SGO/sgo/verbs/{includes,isMemberOf,contains,
isPartOf}.ttl defines the canonical relation predicate vocabulary. Each
is owl:ObjectProperty subPropertyOf ogit:Verb. The workspace already
calls lance-graph-ontology "the OGIT-canonical ontology spine" (per
.claude/knowledge/ontology-registry.md). OGAR is the compiler that
USES OGIT canonical predicates; the IRIs stay ogit:-prefixed.
What landed:
1. New pub mod ogit_relations with the 4 canonical relation IRIs:
- ogit:includes (one-to-many parent → children; has_many, One2many)
- ogit:isMemberOf (many-to-one child → parent; belongs_to, Many2one)
- ogit:contains (composition; habtm, Many2many from composing side)
- ogit:isPartOf (inverse of contains)
Plus is_relation_predicate() direction-blind check.
2. translate_rails_to_ogit(triples) — joins declares_association +
association_kind and emits the directional OGIT predicate:
| Rails kind | OGIT predicate |
|---------------------------|--------------------|
| belongs_to | ogit:isMemberOf |
| has_many | has_one | ogit:includes |
| has_and_belongs_to_many | ogit:contains |
Missing kind triple defaults to belongs_to (conservative).
3. translate_odoo_to_ogit(triples, ns) — maps target → ogit:isMemberOf
(the Many2one-dominant default for today's Odoo extractor which
doesn't surface field_kind). Subject rewritten from <class>.<field>
→ <class>; object underscored to match workspace IRI convention
(account.tax → odoo:account_tax). Future Odoo-extractor extension
emitting field_kind sibling triple → dispatch to includes/contains.
4. classes_matching_commercial_line_item_shape_canonical — single
detector walks ogit_relations canonical predicates direction-blind.
Replaces the per-vocabulary dispatch with one canonical pass.
5. 4 new tests, all green:
- ogit_relation_predicates_have_stable_canonical_iris
- rails_codebook_translates_has_many_to_includes_and_belongs_to_to_is_member_of
- odoo_codebook_translates_target_to_is_member_of_with_underscored_comodel
- ogit_canonical_detector_finds_line_item_classes_on_both_corpora
(loads OSB fixture + workspace Odoo; codebook-translates both;
asserts InvoiceLineItem and account_move_line both surface via
the canonical detector)
Plus all 10 prior tests still green → 14/14 total.
What this collapses:
- E-OGAR-AR-SHAPE-SMOKE-2 finding "two extractor predicate
vocabularies" → SOLVED. Each extractor stays free to emit its
native shape; OGAR consumes OGIT canonical after a per-extractor
codebook pass.
- Doctrine §2 correction 4 "the ontology shapes everything
agnostically through the contract" → contract carries
OGIT-canonical Triples; ontology holds the codebook; consumers
don't know which extractor produced what.
- §11.1 Inc 4 (curator promotion probe) → F4 is now falsifiable
today on the existing corpus pair via the codebook layer; the
upstream-alignment path (E-AR-PROJECTION-CORRECTION-1 Phase 1
Option A) can ship later, moving the codebook one layer up
without changing OGAR's consumer surface.
Adding a new extractor (Spree, future SAP) means adding ONE codebook
function; the detector stays unchanged.
OGIT vs OGAR (operator-confirmed): OGIT = canonical predicate
vocabulary source (TTL prefix ogit:, http://www.purl.org/ogit/).
OGAR = the compiler/Core that consumes OGIT canonical (per
docs/OGAR_AR_SHAPE_ENDGAME.md). No ogar: predicate prefix; the
pub mod ogit_relations is correctly named.
EPIPHANIES E-OGAR-AR-SHAPE-SMOKE-3 prepended.
Cross-refs:
- OGIT/SGO/sgo/verbs/{includes,isMemberOf,contains,isPartOf}.ttl
(canonical predicate definitions)
- crates/lance-graph-ontology/src/ar_shape.rs (codebooks + canonical
detector + 4 new tests; 14/14 green)
- E-OGAR-AR-SHAPE-SMOKE-2 (the predicate-vocab divergence this closes)
- E-OGAR-AR-SHAPE-SMOKE-1 (hand-fixture predecessor)
- .claude/knowledge/ontology-registry.md (OGIT-canonical spine framing)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…xPolicy / CommercialDocument / BillingParty / CurrencyPolicy / PaymentRecord (CanonicalConcept enum 1→6)
Operator "continue with opensource-billing <> odoo" (2026-06-19). Each
concept promotion is gated by ≥2-curator evidence on the real
harvested corpora (OSB ndjson fixture + workspace Odoo manifest), not
pre-emptively added.
New shared helper:
- declared_classes(triples, ns) — returns class IRIs surfaced as
(class, rdf:type, ogit:ObjectType). Filters out method/field IRIs.
5 sibling lexical-shape detectors:
- classes_matching_commercial_document_shape_canonical
Ends-with(invoice|move|order) AND NOT contains("line"). Surfaces
Invoice + account_move. Filters out InvoiceLineItem / account_move_line
(those are CommercialLineItem, not Document).
- classes_matching_tax_policy_shape_canonical
Ends-with("tax"). Surfaces Tax + account_tax. Excludes TaxGroup /
account_tax_group (lowercased "taxgroup" / "account_tax_group"
don't end with "tax").
- classes_matching_billing_party_shape_canonical
Ends-with(client|customer|partner). Surfaces Client + res_partner.
- classes_matching_currency_policy_shape_canonical
Ends-with("currency"). Surfaces Currency + res_currency.
- classes_matching_payment_record_shape_canonical
Ends-with("payment"). Surfaces Payment + account_payment (also
CreditPayment as expected sub-type; downstream ranking picks
primary).
5 corpus-driven tests matching operator's naming convention:
- open_source_billing_invoice_and_odoo_account_move_overlap_as_commercial_document
- open_source_billing_tax_and_odoo_tax_overlap_as_tax_policy
- open_source_billing_client_and_odoo_res_partner_overlap_as_billing_party
- open_source_billing_currency_and_odoo_res_currency_overlap_as_currency_policy
- open_source_billing_payment_and_odoo_account_payment_overlap_as_payment_record
Each loads the OSB ndjson fixture (1195 triples) + workspace Odoo
manifest (2.8 MB) and asserts the expected class IRI appears on each
side via lexical shape on declared OGIT ObjectTypes.
Plus all 14 prior tests still green → 19/19 total.
Scope discipline:
- Lexical class-shape is the minimal viable detector. Structural
refinement (incoming OGIT canonical relations FROM line-items for
TaxPolicy; outgoing has_many to line-items for CommercialDocument)
is the natural follow-up — not added pre-emptively.
- CanonicalConcept enum at 6 variants: CommercialLineItem,
CommercialDocument, TaxPolicy, BillingParty, PaymentRecord,
CurrencyPolicy. SalesOrder / SalesOrderLine / ProductOffering /
FulfillmentFlow / InventoryMovement / ProjectWorkItem /
BillableWorkEntry stay deferred — operator smoke target B needs a
Spree harvest; target C needs Project::TimeEntry + materialization
rules.
Cross-repo alignment with AdaWorldAPI/OGAR#61: the merged PR
introduced const CODEBOOK: &[(&str, u16)] minting stable u16
ClassIds for promoted canonical concepts (today: project,
project_work_item, billable_work_entry). The ar_shape CanonicalConcept
enum is the upstream candidate set — concepts get promoted into
OGAR's CODEBOOK once they clear the ≥2-curator gate AND OGAR-side
review. Each new variant in this commit is a candidate row for a
future OGAR CODEBOOK assignment.
EPIPHANIES E-OGAR-AR-SHAPE-SMOKE-4 prepended.
Cross-refs:
- crates/lance-graph-ontology/src/ar_shape.rs (5 detectors +
declared_classes helper + 5 tests; 19/19 green)
- E-OGAR-AR-SHAPE-SMOKE-3 (OGIT canonical predecessor)
- E-OGAR-AR-SHAPE-SMOKE-2 (predicate-vocabulary divergence finding)
- E-OGAR-AR-SHAPE-SMOKE-1 (hand-fixture predecessor)
- AdaWorldAPI/OGAR#61 (upstream u16 CODEBOOK registry — candidate
promotion target for each ar_shape::CanonicalConcept variant)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
OGAR#62 merged 2026-06-19T12:00:45Z (project_actor at CODEBOOK 0x0004). Collapses Redmine+OpenProject User/Principal/ApplicationRecord STI chain (4 source classes → one canonical), proving binary cross-curator convergence on real Rails sources. Updated SMOKE-4 entry's Cross-repo alignment paragraph to include #62 alongside #61. OGAR's project-management quartet (project, project_work_item, billable_work_entry, project_actor) is complementary to this commit's commerce/billing/erp sextet (CommercialLineItem, CommercialDocument, TaxPolicy, BillingParty, PaymentRecord, CurrencyPolicy). Different domain, same promotion mechanics. The STI-collapse pattern OGAR#62 demonstrated for ProjectActor is the same shape my BillingParty lexical detector applies on OSB::Client + Odoo::res_partner — same mechanism, different domain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…elation participation filter Phase 1 of operator's "all of the above" 2026-06-19. Adds classes_participating_in_canonical_relations(canonical_triples, ns) → BTreeSet<String> — returns class IRIs that appear as either subject OR object of any OGIT canonical relation after codebook translation. Bidirectional: leaf classes (currency, tax target) are usually referenced AS objects (Document.currency_id → res_currency) rather than emitting Many2one out. A subject-only check would falsely flag them as inert. Object-side IRI shapes handled: - Class IRI (<ns><Class>) — Odoo-translated codebook output names the comodel directly. - Field IRI (<ns><Class>.<assoc>) — Rails-translated codebook output keeps field IRI verbatim; leading <Class> is the SOURCE, already covered by subject side. New test lexical_candidates_survive_canonical_relation_participation_check verifies all 6 lexical-detection candidates on both corpora (OSB InvoiceLineItem/Invoice/Tax/Client/Currency/Payment + Odoo account_move_line/account_move/account_tax/res_partner/res_currency/ account_payment) survive the participation filter. Initial subject-only implementation surfaced res_currency as zero-subject-relations (the right finding) — fixed by adding object-side coverage. Direction-blind today; this is the seed for future class_has_outbound_relation_to_<concept> / class_has_inbound_relation_from_<concept> refinements that wire the concept-to-concept cascade (TaxPolicy needs LineItem participation; CommercialDocument needs LineItem participation; BillingParty needs CommercialDocument; etc.). Plus all 19 prior tests still green → 20/20 total. OGAR alignment update (other-session feedback, 2026-06-19): AdaWorldAPI/OGAR#63 merged — promoted ProjectStatus (Redmine IssueStatus + OP Status → 0x0005) + ProjectType (Redmine Tracker + OP Type → 0x0006). CODEBOOK now at 6 entries, all project-management domain. My commerce-domain CanonicalConcept candidates would slot at 0x0007+ when the OGAR codebook promotion PR (Phase 3 of "all of the above") opens. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…esOrderLine / FulfillmentFlow / InventoryMovement / ProductOffering); 3-curator convergence on TaxPolicy + PaymentRecord
Phase 2 of operator's "all of the above" 2026-06-19. Smoke target B
(Spree↔Odoo commerce overlap) landed with real harvested triples.
What landed:
1. Spree harvest fixture — pulled spree/spree upstream zipball
(85 MB, no AdaWorldAPI fork available; canon's forks-only rule
applies to Cargo deps, not read-only fixture harvests). Ran
ruff_ruby_spo on spree/core: 289 models, 7954 triples, 909 KB
ndjson → checked in as
crates/lance-graph-ontology/tests/fixtures/spree_ruby_spo.ndjson.
2. CanonicalConcept enum 6 → 11 variants. New (all from smoke
target B):
- SalesOrder (Spree::Order + odoo:sale_order)
- SalesOrderLine (Spree::LineItem + odoo:sale_order_line)
- FulfillmentFlow (Spree::Shipment + odoo:stock_picking)
- InventoryMovement (Spree::InventoryUnit + odoo:stock_move)
- ProductOffering (Spree::Product / Variant + odoo:product_product /
product_template)
3. CommercialDocument detector narrowed: drops the "order" ending so
sale_order / Spree::Order route to SalesOrder (a distinct
commerce-side concept), keeping CommercialDocument as the
accounting-document concept (Invoice / account_move). This is the
distinct-but-adjacent split the operator's smoke spec implied.
4. 5 new sibling detectors with the same lexical-shape pattern as the
commerce sextet, each with its concept-specific lexical hint:
- classes_matching_sales_order_shape_canonical
(ends_with "order" AND NOT contains "line")
- classes_matching_sales_order_line_shape_canonical
(ends_with "lineitem" OR ends_with "order_line"/"orderline")
- classes_matching_fulfillment_flow_shape_canonical
(ends_with "shipment" OR ends_with "picking")
- classes_matching_inventory_movement_shape_canonical
(ends_with "inventoryunit" OR ends_with "stock_move" — the stock_
qualifier discriminates from account_move which is a
CommercialDocument)
- classes_matching_product_offering_shape_canonical
(ends_with "product" OR ends_with "variant" OR ends_with
"product_template")
5. TaxPolicy detector hardened: also matches contains("taxrate") so
Spree::TaxRate surfaces (the strongest commerce-side tax-policy
signal). OSB::Tax and odoo:account_tax stay matched via the
existing ends_with("tax") arm.
6. 6 new corpus-driven tests, all green:
- spree_order_and_odoo_sale_order_overlap_as_sales_order
- spree_line_item_and_odoo_sale_order_line_overlap_as_sales_order_line
- spree_shipment_and_odoo_stock_picking_overlap_as_fulfillment_flow
- spree_inventory_unit_and_odoo_stock_move_overlap_as_inventory_movement
(also asserts account_move does NOT promote here)
- spree_product_variant_and_odoo_product_overlap_as_product_offering
- spree_third_curator_convergence_on_tax_policy_and_payment_record
(proves the existing TaxPolicy + PaymentRecord detectors
generalize to a 3rd curator — Spree::TaxRate, Spree::Payment)
Plus all 20 prior tests still green → 26/26 total.
Scope reminder per operator discipline acceptance #4-7: each promotion
is gated by ≥2-curator structural evidence on real corpora, not
pre-emptive. The SMOKE-1 to SMOKE-4 + this commit's 5 concepts now
cover smoke targets A (OSB↔Odoo accounting) and B (Spree↔Odoo
commerce). Smoke target C (Redmine/OpenProject Project::TimeEntry
materialization) is project-domain — already being shipped by the
other session into OGAR's CODEBOOK (PRs #61/#62/#63 promoted project,
project_work_item, billable_work_entry, project_actor, project_status,
project_type).
CanonicalConcept enum now 11 variants. The lance-graph-ontology
CanonicalConcept set complements OGAR's project-domain 6-entry
CODEBOOK with an 11-entry commerce/billing/erp candidate set
(CommercialLineItem, CommercialDocument, TaxPolicy, BillingParty,
PaymentRecord, CurrencyPolicy, SalesOrder, SalesOrderLine,
FulfillmentFlow, InventoryMovement, ProductOffering). Each candidate
is a future OGAR CODEBOOK row (Phase 3 of "all of the above" — the
OGAR upstream promotion PR).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…table from 3 ruff/Odoo harvests in one call
Operator "cant you use the export you have from Ruff to do the
canonical ERP labels i one shot" (2026-06-19). Mechanizes the
doctrine §2 synergy-registry framing as a single function:
3 harvest exports in → 11-entry canonical ERP label table out.
What landed:
1. New pub struct CanonicalErpEntry { concept, matches: Vec<(SourceCurator,
String)> } — one row of the canonical ERP label table. Carries the
promoted concept + every (curator, class_iri) pair that surfaces it.
Includes curator_count() helper for the ≥2-curator gate.
2. New pub fn synergy_registry_one_shot(osb, spree, odoo) that:
- Takes 3 (triples, namespace_prefix) tuples (OSB Ruby, Spree Ruby,
Odoo Python — the three harvests the workspace currently ships)
- Uses a method-pointer table mapping each CanonicalConcept variant
to its lexical detector (CommercialLineItem uses the
vocabulary-aware one that handles both declares_association +
target; the other 10 use the lexical declared_classes variants)
- Runs every detector against every curator
- Sorts + dedups matches
- Applies the ≥2-curator promotion rule per entry
- Returns Vec<CanonicalErpEntry> sorted by enum-discriminant
order, fully deterministic
3. 2 new tests:
- synergy_registry_one_shot_returns_full_canonical_erp_label_table
loads all 3 fixtures (OSB 1195 triples + Spree 7954 + Odoo 2.8MB),
calls the one-shot, asserts every one of the 11 expected concepts
surfaces with the right (curator, class_iri) pairs. TaxPolicy +
PaymentRecord assert curator_count() >= 3 (the 3-curator
convergence). Registry size pinned at 11.
- synergy_registry_one_shot_is_deterministic — re-running on the
same inputs returns identical Vec.
Plus all 26 prior tests still green → 28/28 total.
The 11 canonical ERP labels surfaced by the one-shot (the synergy
registry's current state):
| Concept | OSB | Spree | Odoo |
|-------------------|---------------|--------------------|------------------------|
| CommercialLineItem| InvoiceLineItem | — | account_move_line |
| CommercialDocument| Invoice | — | account_move |
| TaxPolicy | Tax | Spree::TaxRate | account_tax |
| BillingParty | Client | — | res_partner |
| PaymentRecord | Payment | Spree::Payment | account_payment |
| CurrencyPolicy | Currency | — | res_currency |
| SalesOrder | — | Spree::Order | sale_order |
| SalesOrderLine | — | Spree::LineItem | sale_order_line |
| FulfillmentFlow | — | Spree::Shipment | stock_picking |
| InventoryMovement | — | Spree::InventoryUnit | stock_move |
| ProductOffering | — | Spree::Product | product_product |
Each row in the table represents a ≥2-curator promotion (OSB+Odoo for
accounting concepts; Spree+Odoo for commerce concepts; OSB+Odoo+Spree
for TaxPolicy + PaymentRecord — the 3-curator concepts).
Cross-references (the synergy registry as canonical ERP label table)
parallel OGAR#64's CODEBOOK structure: each row is a candidate for a
future u16 ClassId assignment. Today OGAR's CODEBOOK has 0x0001–0x0006
(project-mgmt) + #64 pending 0x0007–0x000C (the SMOKE-1..4 commerce
sextet). The 5 SMOKE-5 additions (SalesOrder + 4 commerce siblings)
would extend the commerce block at 0x000D–0x0011 in a follow-on
promotion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…he same canonical concept-GRAPH
Operator "you were in the odoo vs rails based ERP AST test" (2026-06-19).
The synergy work so far proved NAME-level convergence (class IRI →
CanonicalConcept). This lands the deeper test: EDGE-level convergence —
the concept GRAPH converges across a Rails-based ERP and Odoo, which is
what makes it an AST not a label table.
New surface in ar_shape.rs:
- concept_of_token(token) -> Option<CanonicalConcept> — resolves a
single relation token (Odoo comodel class name OR Rails relation
leaf) to its concept by the same lexical hints the 11 class
detectors use. Priority-ordered: *line*/tax/stock_move resolve
before the bare document arms so ambiguous substrings classify
correctly. Returns None for real-but-unpromoted targets
(estimate, uom, analytic_account) — honest, not fabricated.
- ConceptEdge type alias (CanonicalConcept, CanonicalConcept).
- ConceptDetector type alias (clears the synergy_registry_one_shot
type_complexity clippy warning).
- concept_edges(triples, ns, is_rails) -> BTreeSet<ConceptEdge> —
runs the per-curator OGIT codebook (translate_rails_to_ogit /
translate_odoo_to_ogit), resolves BOTH endpoints of each canonical
relation to concepts, returns the edge set (self-edges dropped).
Convergence proven on real corpora (3 tests):
- CommercialLineItem → CommercialDocument
OSB: InvoiceLineItem.invoice (Rails declares_association, leaf
"invoice"). Odoo: account_move_line.move_id → account.move (Odoo
target). Different field name, different predicate, SAME edge.
- CommercialLineItem → TaxPolicy
OSB: InvoiceLineItem.{tax1,tax2} (two named FKs). Odoo:
account_move_line.tax_ids → account.tax (one M2M). Different
cardinality, SAME edge.
- CommercialDocument → BillingParty
OSB: Invoice belongs_to :client. Odoo: account_move → partner_id →
res.partner. Convergence isn't limited to the line-item node.
Tests:
- concept_of_token_resolves_both_curator_vocabularies
- osb_rails_and_odoo_commercial_line_item_share_concept_edges
- osb_rails_and_odoo_commercial_document_both_link_billing_party
Plus all 28 prior tests still green → 31/31 total. ar_shape
clippy-clean (the pre-existing synergy detector-table type_complexity
warning is resolved via the ConceptDetector alias).
Why this is THE AST test: node-level convergence (SMOKE-1..4) shows
two curators NAME the same concepts. Edge-level shows two curators
STRUCTURE them identically — line item is a member of a document,
document belongs to a party, line carries a tax. Strip Rails/Odoo
syntax and the same business AST remains. This is doctrine §2
"labels are leaf detail; the SHAPE is what overlaps" proven at the
relation level — the strongest cash-out yet of OGAR-is-the-compiler
(the concept-graph IS the compiled output; the curators are
interchangeable front-ends).
EPIPHANIES E-OGAR-AR-SHAPE-SMOKE-5 prepended.
Cross-refs:
- E-OGAR-AR-SHAPE-SMOKE-3 (the OGIT canonical codebooks this builds on)
- E-OGAR-AR-SHAPE-SMOKE-4 (the node-level sibling concepts)
- docs/OGAR_AR_SHAPE_ENDGAME.md §2 correction 4 (labels-are-leaf-detail,
now proven at edge level)
- AdaWorldAPI/OGAR CODEBOOK (the concept-edges ARE the family-edge
structure OGAR's canonical classes encode)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…erit are the same members/memberof primitive (#545..#551) Operator nudge (2026-06-19): "the family nodes introduced in lance-graph 545..551 could serve as mixin — group.memberof/members where group is the mixin node." This RESOLVES a divergence I flagged wrong earlier this session. When asked whether the Rails spine could verify Odoo AR-shapedness, I said yes but called Odoo's _inherit "a non-AR shape with no Rails analog." That was wrong: the Rails analog is include (concerns), and BOTH lower to the family-node members/memberof primitive from #549 (graph::mailbox_scan::{members, memberof, BasinOf}). A mixin IS a family/group node; include/_inherit IS the memberof edge. New surface: - mixin_members(triples, ns, is_rails) -> BTreeMap<group, BTreeSet<member>> Reads includes_module (Rails) or inherits_from (Odoo), ns-strips, returns the `members` direction (memberof is the transpose). - shared_mixin_groups(members, min) -> Vec<group> The ≥2-member fan-out filter: a group shared by ≥2 classes is a genuine mixin; a single-member group is an STI base / model extension, not a mixin. Same distinction members(basin) draws in #549. Grounded in the harvest (not asserted): - OSB carries 37 includes_module triples (Client→PublicActivity::Model, Estimate→Trackstamps/DateFormats). - Odoo carries 166 inherits_from triples; mail_thread is a group node with 70+ members (sale_order, account_account, purchase_order, ...). account_move rides mail_activity_mixin + sequence_mixin, NOT mail_thread directly — the test preserves the harvest's distinction. - Cross-curator semantic convergence: OSB PublicActivity::Model (activity tracking) ≈ Odoo mail_thread / mail_activity_mixin. Both curators independently grew an activity mixin group. 4 tests, all green: - odoo_mail_thread_is_a_family_group_node_with_many_members - osb_rails_public_activity_model_is_a_family_group_node - rails_include_and_odoo_inherit_are_the_same_family_node_primitive (the divergence-resolution test) - single_member_extension_is_not_a_mixin_group (fan-out honesty) Plus all 31 prior tests still green → 35/35 total. ar_shape clippy-clean. The lesson: an apparent "Odoo non-AR divergence" should first be checked against the lance-graph substrate primitives (#545..#551 members/memberof, the family-node tree) before being called a divergence — the substrate already had the home for it. The mixin group node carries shared behaviour down to members, which is E-FAMILY-NODE-IS-META-AWARENESS instantiated for ERP mixins (parent = coarse summary members inherit). EPIPHANIES E-OGAR-AR-SHAPE-SMOKE-6 prepended (includes the correction of my earlier wrong claim). Cross-refs: - E-BASIN-IS-A-NODE + E-FAMILY-NODE-IS-META-AWARENESS + E-GUID-SELF-ROUTES-THE-BASIN-TREE (the #545..#551 family-node arc) - graph::mailbox_scan::{members, memberof} (#549 substrate primitive) - E-OGAR-AR-SHAPE-SMOKE-5 (the concept-edge AST test; mixin membership is the inheritance-edge complement to the composition-edge graph) - AdaWorldAPI/OGAR project_actor (STI-collapse used the same shape) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
1c32577 to
4e706b1
Compare
…ogar-vocab; record the construction error Operator flagged #552: ar_shape is generic-AR detection that belongs in OGAR (the generic AR compiler), not lance-graph-ontology. Verified against OGAR@156c016: ogar-vocab (3383 LOC) already IS the canonical registry, richer: ar_shape (reverted) ogar-vocab (canonical) CanonicalConcept + codebook_id canonical_concept_id / const CODEBOOK concept_of_token canonical_concept (richer alias arms) synergy_registry_one_shot wire_synergies + Synergy/SynergyMember concept_edges Association / AssociationKind Domain (drafted+reverted) ConceptDomain + canonical_concept_domain 6 commerce concepts 0x0201-06 IDENTICAL ids, OGAR PR #64, full Class ogar-from-rails comment confirms the intended split: "the Odoo-side extraction lives in the parallel session" — OGAR owns the registry, lance-graph does the Odoo extraction, they MEET at canonical_concept_id. I re-implemented the registry instead of consuming it — the same parallel-object-model construction error the operator called out at the start of this arc (E-ODOO-CORE-FIRST-STRUCTURAL). Removed: - crates/lance-graph-ontology/src/ar_shape.rs (2568 LOC duplicate) - tests/fixtures/{osb,spree}_ruby_spo.ndjson (9149 LOC; belong where the Rails harvest runs = OGAR ogar-from-rails) - pub mod ar_shape from lib.rs Kept: - docs/OGAR_AR_SHAPE_ENDGAME.md §2 corrections (legitimate doctrine, partly already in lance-graph from #546) - E-OGAR-AR-SHAPE-SMOKE-1..6 (findings preserved; code-home corrected by the new E-OGAR-AR-SHAPE-REHOME entry) Net-new for OGAR (NOT re-homed here): the 5 SMOKE-5 commerce concepts (SalesOrder/SalesOrderLine/FulfillmentFlow/InventoryMovement/ ProductOffering, 0x0207-0x020B) + Spree as a curator. OGAR's commerce block stops at 0x0206. Those belong in OGAR's full-Class pattern + ogar-from-rails Spree curator — queued for the OGAR lane, not built here as lexical detectors. crate compiles clean without ar_shape (5 pre-existing oxrdf::Subject deprecation warnings unchanged). #552 closed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
|
Closing — this PR built a parallel AR canonical registry in the wrong repo. Verified against
Actions taken:
Closing in favor of the OGAR home. Generated by Claude Code |
…ogar-vocab; record the construction error Operator flagged #552: ar_shape is generic-AR detection that belongs in OGAR (the generic AR compiler), not lance-graph-ontology. Verified against OGAR@156c016: ogar-vocab (3383 LOC) already IS the canonical registry, richer: ar_shape (reverted) ogar-vocab (canonical) CanonicalConcept + codebook_id canonical_concept_id / const CODEBOOK concept_of_token canonical_concept (richer alias arms) synergy_registry_one_shot wire_synergies + Synergy/SynergyMember concept_edges Association / AssociationKind Domain (drafted+reverted) ConceptDomain + canonical_concept_domain 6 commerce concepts 0x0201-06 IDENTICAL ids, OGAR PR #64, full Class ogar-from-rails comment confirms the intended split: "the Odoo-side extraction lives in the parallel session" — OGAR owns the registry, lance-graph does the Odoo extraction, they MEET at canonical_concept_id. I re-implemented the registry instead of consuming it — the same parallel-object-model construction error the operator called out at the start of this arc (E-ODOO-CORE-FIRST-STRUCTURAL). Removed: - crates/lance-graph-ontology/src/ar_shape.rs (2568 LOC duplicate) - tests/fixtures/{osb,spree}_ruby_spo.ndjson (9149 LOC; belong where the Rails harvest runs = OGAR ogar-from-rails) - pub mod ar_shape from lib.rs Kept: - docs/OGAR_AR_SHAPE_ENDGAME.md §2 corrections (legitimate doctrine, partly already in lance-graph from #546) - E-OGAR-AR-SHAPE-SMOKE-1..6 (findings preserved; code-home corrected by the new E-OGAR-AR-SHAPE-REHOME entry) Net-new for OGAR (NOT re-homed here): the 5 SMOKE-5 commerce concepts (SalesOrder/SalesOrderLine/FulfillmentFlow/InventoryMovement/ ProductOffering, 0x0207-0x020B) + Spree as a curator. OGAR's commerce block stops at 0x0206. Those belong in OGAR's full-Class pattern + ogar-from-rails Spree curator — queued for the OGAR lane, not built here as lexical detectors. crate compiles clean without ar_shape (5 pre-existing oxrdf::Subject deprecation warnings unchanged). #552 closed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…ogar-vocab; record the construction error Operator flagged #552: ar_shape is generic-AR detection that belongs in OGAR (the generic AR compiler), not lance-graph-ontology. Verified against OGAR@156c016: ogar-vocab (3383 LOC) already IS the canonical registry, richer: ar_shape (reverted) ogar-vocab (canonical) CanonicalConcept + codebook_id canonical_concept_id / const CODEBOOK concept_of_token canonical_concept (richer alias arms) synergy_registry_one_shot wire_synergies + Synergy/SynergyMember concept_edges Association / AssociationKind Domain (drafted+reverted) ConceptDomain + canonical_concept_domain 6 commerce concepts 0x0201-06 IDENTICAL ids, OGAR PR #64, full Class ogar-from-rails comment confirms the intended split: "the Odoo-side extraction lives in the parallel session" — OGAR owns the registry, lance-graph does the Odoo extraction, they MEET at canonical_concept_id. I re-implemented the registry instead of consuming it — the same parallel-object-model construction error the operator called out at the start of this arc (E-ODOO-CORE-FIRST-STRUCTURAL). Removed: - crates/lance-graph-ontology/src/ar_shape.rs (2568 LOC duplicate) - tests/fixtures/{osb,spree}_ruby_spo.ndjson (9149 LOC; belong where the Rails harvest runs = OGAR ogar-from-rails) - pub mod ar_shape from lib.rs Kept: - docs/OGAR_AR_SHAPE_ENDGAME.md §2 corrections (legitimate doctrine, partly already in lance-graph from #546) - E-OGAR-AR-SHAPE-SMOKE-1..6 (findings preserved; code-home corrected by the new E-OGAR-AR-SHAPE-REHOME entry) Net-new for OGAR (NOT re-homed here): the 5 SMOKE-5 commerce concepts (SalesOrder/SalesOrderLine/FulfillmentFlow/InventoryMovement/ ProductOffering, 0x0207-0x020B) + Spree as a curator. OGAR's commerce block stops at 0x0206. Those belong in OGAR's full-Class pattern + ogar-from-rails Spree curator — queued for the OGAR lane, not built here as lexical detectors. crate compiles clean without ar_shape (5 pre-existing oxrdf::Subject deprecation warnings unchanged). #552 closed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
…ogar-vocab; record the construction error Operator flagged #552: ar_shape is generic-AR detection that belongs in OGAR (the generic AR compiler), not lance-graph-ontology. Verified against OGAR@156c016: ogar-vocab (3383 LOC) already IS the canonical registry, richer: ar_shape (reverted) ogar-vocab (canonical) CanonicalConcept + codebook_id canonical_concept_id / const CODEBOOK concept_of_token canonical_concept (richer alias arms) synergy_registry_one_shot wire_synergies + Synergy/SynergyMember concept_edges Association / AssociationKind Domain (drafted+reverted) ConceptDomain + canonical_concept_domain 6 commerce concepts 0x0201-06 IDENTICAL ids, OGAR PR #64, full Class ogar-from-rails comment confirms the intended split: "the Odoo-side extraction lives in the parallel session" — OGAR owns the registry, lance-graph does the Odoo extraction, they MEET at canonical_concept_id. I re-implemented the registry instead of consuming it — the same parallel-object-model construction error the operator called out at the start of this arc (E-ODOO-CORE-FIRST-STRUCTURAL). Removed: - crates/lance-graph-ontology/src/ar_shape.rs (2568 LOC duplicate) - tests/fixtures/{osb,spree}_ruby_spo.ndjson (9149 LOC; belong where the Rails harvest runs = OGAR ogar-from-rails) - pub mod ar_shape from lib.rs Kept: - docs/OGAR_AR_SHAPE_ENDGAME.md §2 corrections (legitimate doctrine, partly already in lance-graph from #546) - E-OGAR-AR-SHAPE-SMOKE-1..6 (findings preserved; code-home corrected by the new E-OGAR-AR-SHAPE-REHOME entry) Net-new for OGAR (NOT re-homed here): the 5 SMOKE-5 commerce concepts (SalesOrder/SalesOrderLine/FulfillmentFlow/InventoryMovement/ ProductOffering, 0x0207-0x020B) + Spree as a curator. OGAR's commerce block stops at 0x0206. Those belong in OGAR's full-Class pattern + ogar-from-rails Spree curator — queued for the OGAR lane, not built here as lexical detectors. crate compiles clean without ar_shape (5 pre-existing oxrdf::Subject deprecation warnings unchanged). #552 closed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
Summary
Adds
lance-graph-ontology::ar_shape— the Odoo ↔ Rails ERP AST convergence detector, proven on real harvested corpora. This is the working mechanization of theE-OGAR-AR-SHAPE-ENDGAMEdoctrine's §2 corrections (curators teach, OGAR compiles) andE-AR-DO-WIRING's curator-promotion rule: independent ERPs written in different languages, with different class names, field names, and predicate vocabularies, emit the same canonical concept graph once their harvests are folded through OGIT-canonical codebooks.Additive only — new module + 2 test fixtures +
pub mod ar_shape;. Zero changes to existing types, contract surface, or any other crate. The 4 doctrine-doc commits are §2 correction-chain refinements todocs/OGAR_AR_SHAPE_ENDGAME.md(operator-ratified) + the #546 post-merge board hygiene.What it proves (35 tests, all green, ar_shape clippy-clean)
InvoiceLineItem≡account_move_line→CommercialLineItem;Tax≡account_tax→TaxPolicy; etc.declares_associationvs Odootarget→ both fold toogit:isMemberOf/includesvia per-curator codebooksCommercialLineItem → CommercialDocument,→ TaxPolicy;CommercialDocument → BillingPartyconverge despitetax1/tax2vstax_idsinclude+ Odoo_inheritare the SAME family-nodemembers/memberofprimitive (#545..#551)mail_threadis a group node with 70+ members;PublicActivity::Modelover{Client, Estimate}; ≥2-member fan-out filterModule surface (
crates/lance-graph-ontology/src/ar_shape.rs)CanonicalConcept(11 variants) —CommercialLineItem,CommercialDocument,TaxPolicy,BillingParty,PaymentRecord,CurrencyPolicy,SalesOrder,SalesOrderLine,FulfillmentFlow,InventoryMovement,ProductOffering. Each gated by ≥2-curator structural evidence on real corpora. Candidate set forAdaWorldAPI/OGAR's0x02XXcommerce CODEBOOK block.ogit_relationsmod — the 4 canonical OGIT relation IRIs (includes/isMemberOf/contains/isPartOf, fromOGIT/SGO/sgo/verbs/*.ttl).translate_rails_to_ogit/translate_odoo_to_ogit— per-curator codebooks folding native predicates into OGIT canonical.concept_edges— the cross-curator AST surface (concept→concept graph).mixin_members/shared_mixin_groups— mixin-as-family-node (the feat(graph): Hamming-plane distance — the first value-tier DistanceMeans (costed, stacked on #544) #545..probe: family-basin Weyl multi-hop is hop-local at the crisp tier (reinstates hop-bounds-reach, tier-scoped) #551members/memberofprimitive applied toinclude/_inherit).synergy_registry_one_shot— the full canonical ERP label table from 3 harvests in one call.concept_of_token— the cross-vocabulary token→concept resolver.Fixtures (test data)
tests/fixtures/osb_ruby_spo.ndjson— 1195 triples fromAdaWorldAPI/open-source-billing@61cd6edviaruff_ruby_spo(119 KB)tests/fixtures/spree_ruby_spo.ndjson— 7954 triples fromspree/spreecore viaruff_ruby_spo(909 KB)crates/lance-graph/src/graph/spo/odoo_ontology.spo.ndjson(unchanged)Cross-repo alignment
This is the detection side of what
AdaWorldAPI/OGAR's CODEBOOK encodes from the canonical-class side. Myconcept_edgesare exactly the family-edge structure OGAR's promoted classes carry (e.g.commercial_line_itemfamily-edges tocommercial_document+tax_policy). The mixin-as-family-node insight maps onto lance-graph's owngraph::mailbox_scan::{members, memberof}substrate (#545..#551). The two sessions built the same graph from opposite ends.EPIPHANIES (6 entries, board hygiene done in-branch)
E-OGAR-AR-SHAPE-SMOKE-1through-6— including SMOKE-6 which corrects an earlier in-session claim (Odoo_inheritis NOT a "non-AR divergence"; it's the same family-node primitive as Railsinclude).Scope discipline
Lexical class/relation-shape detectors (token ends-with/contains hints);
concept_of_tokenreturnsNonefor real-but-unpromoted targets (estimate,uom) — honest, not fabricated. Each concept promotion gated by ≥2-curator evidence. Next deepening (deferred): edge cardinality/direction (M2O vs O2M vs M2M), the 5 SMOKE-5 commerce concepts' promotion into OGAR's0x0207–0x020B.Test plan
cargo test -p lance-graph-ontology --lib ar_shape→ 35/35 greencargo check -p lance-graph-ontologyclean (5 pre-existingoxrdf::Subjectdeprecation warnings inttl_parse.rs, not from this PR)ar_shape.rsclippy-clean (pre-existingTD-ONTOLOGY-LINTdebt inttl_parse.rs/class_resolver.rsuntouched)🤖 Generated with Claude Code
https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
Generated by Claude Code