Skip to content

feat(graph): Hamming-plane distance — the first value-tier DistanceMeans (costed, stacked on #544)#545

Merged
AdaWorldAPI merged 3 commits into
claude/inc0-mailbox-soa-backendfrom
claude/value-tier-hamming-plane
Jun 19, 2026
Merged

feat(graph): Hamming-plane distance — the first value-tier DistanceMeans (costed, stacked on #544)#545
AdaWorldAPI merged 3 commits into
claude/inc0-mailbox-soa-backendfrom
claude/value-tier-hamming-plane

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

The first value-tier DistanceMeans — Hamming over an identity plane

Stacked on #544 (base = claude/inc0-mailbox-soa-backend). The node-distance surface from #544 gains its first value-decode means, kept on a separate branch with its own cost gate so #544's "all key-resident, zero value decode" claim stays clean.

What lands

  • contract soa_view: IdentityPlane enum (Content/Topic/Angle — the orthogonal perspective axes) + MailboxSoaView::identity_plane_at(row, plane) -> Option<&[u64]> (deferred-binding default None; the in-RAM MailboxSoA owner carries the W1b content/topic/angle planes).
  • core mailbox_scan: DistanceMeans::Hamming(IdentityPlane) + the node_distance arm — popcount(XOR) over the two rows' fingerprint planes.

Cost class (the point of the separate branch)

This is the costed tier: it reads the value-side plane, so — unlike PrefixDepth — it is NOT zero value decode. It's the right use of popcount (homogeneous 16K-bit plane, not the heterogeneous GUID key — that distinction from E-TENANT-ANGLE-RANK-IS-CAM-PQ-ADC). On the zero-decode GuardedSoa it simply returns None (planes unmaterialized), so #544's F2 gate is untouched.

Tests (contract 4/4, core 12/12, fmt + clippy clean)

  • popcount-XOR: 0b1011 vs 0b1101 → 2;
  • self-distance = 0 (metric);
  • all-zero vs all-ones over 2×u64 = 128 bits;
  • unmaterialized plane → None (costed-tier fallback);
  • a plane the view doesn't carry (Topic/Angle) → None.

Next value means (named, not yet wired)

HelixAngular (Signed360 exact-orthogonal location, E-HELIX-IS-EXACT-LOCATION) and PqAdc (CAM-PQ asymmetric distance, IVF probe + tables) — same costed tier, land as they come.

🤖 Generated with Claude Code


Generated by Claude Code

…istanceMeans (costed)

The first value-decode means on the GUID-keyed node-distance surface, kept on its
own branch (stacked on the inc0 facets) so it never dilutes the zero-decode claim.

- contract soa_view: IdentityPlane enum (Content/Topic/Angle — the orthogonal
  perspective axes) + MailboxSoaView::identity_plane_at(row, plane) -> Option<&[u64]>
  (deferred-binding default None; the in-RAM MailboxSoA owner carries the W1b planes).
- core mailbox_scan: DistanceMeans::Hamming(IdentityPlane) + node_distance arm —
  popcount(XOR) over the two rows' fingerprint planes. The RIGHT use of popcount
  (homogeneous 16K-bit plane, not the heterogeneous GUID key,
  E-TENANT-ANGLE-RANK-IS-CAM-PQ-ADC). COSTED: reads the value-side plane, so NOT
  zero value decode — explicitly the other side of the F2 line.
- Tests: popcount-XOR (0b1011 vs 0b1101 = 2), self=0, all-0 vs all-1 over 128b =
  128, unmaterialized plane = None, non-carried plane (Topic/Angle) = None.
  contract soa_view 4/4, core mailbox_scan 12/12, fmt + clippy clean.

Further value means (HelixAngular Signed360, PqAdc CAM-PQ) named in the enum,
wired as they land on the same costed tier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: be52d9e7-96f1-4442-a314-8d9b778ddf34

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4145e9cdf8

ℹ️ 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".

Comment on lines +261 to +262
let fa = view.identity_plane_at(a, plane)?;
let fb = view.identity_plane_at(b, plane)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire Hamming planes into the real MailboxSoA view

When callers use node_distance(..., DistanceMeans::Hamming(_)) with the in-RAM MailboxSoA, these lookups still hit the new trait default (crates/lance-graph-contract/src/soa_view.rs:152) and return None: at this commit the only non-test owner I found, crates/cognitive-shader-driver/src/mailbox_soa.rs, has content_row/topic_row/angle_row accessors but its MailboxSoaView impl at lines 705-767 does not override identity_plane_at. That makes the advertised Hamming distance unavailable on the real view that carries the planes; add the override (or wire the real owner elsewhere) so this arm can actually compute distances outside the unit-test fake.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 73d8d59: added the identity_plane_at override on the real MailboxSoA<N> impl (cognitive-shader-driver), mapping Content/Topic/Anglecontent_row/topic_row/angle_row, guarded by populated (a row beyond the logical size returns None, never a zero-padded capacity row — same discipline as n_rows() / the #544 clamp). So DistanceMeans::Hamming now computes on the live view, not just the test fake. +1 test (identity_plane_at_returns_planes_and_guards_padding). Also merged the #544 clamp fix into this branch.


Generated by Claude Code

claude added 2 commits June 19, 2026 05:56
# Conflicts:
#	crates/lance-graph/src/graph/mailbox_scan.rs
…_at on the real owner (codex P2 #545)

The Hamming-plane DistanceMeans returned None on the live MailboxSoA<N> because
its MailboxSoaView impl didn't override identity_plane_at (only the unit-test
fake did). The owner DOES carry the W1b content/topic/angle planes, so the
override maps Content/Topic/Angle -> content_row/topic_row/angle_row, guarded by
`populated` (a row beyond the logical size returns None, never a zero-padded
capacity row — same discipline as n_rows() / the #544 clamp). +1 test:
populated row returns the plane byte-identical to content_row; padding row None.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
@AdaWorldAPI AdaWorldAPI merged commit c74e6ee into claude/inc0-mailbox-soa-backend Jun 19, 2026
6 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Jun 19, 2026
…ing gates F1-F5

Companion to docs/OGAR_AR_SHAPE_ENDGAME.md (the operator-ratified doctrine).
Each Inc promotes one CONJECTURE from §12 to FINDING (or demotes it on
falsifier).

Inc 1 — ClassView::policies typed THINK slot. F1: one ThinkSpec returns
Verdict::Reject on inconsistent row, ProceedAs on consistent. ~4 files.
Inc 2 — OgarAst enum + TripletProjection round-trip. F2: roundtrip_eq
green on 4-variant input. ~3 files.
Inc 3 — ArmDecision + Executor::{NativeLance, SurrealAst} stubs. F3:
same OgarAst::Do routed via 2 executors → same state_after. ~5 files.
Inc 4 — Curator promotion probe (≥2-curator rule mechanized). F4: ≥4
primitives surface under ≥2 curators with different syntactic forms.
~5 files.
Inc 5 — Litmus probe: same OgarAst::Do(PostInvoice, ...) ≡ across 4
executors. F5: semantic identity green; crown-detection sub-probe fails
with the named error message format. ~6 files.

Gates ordered F1 → F2 → F3 → F4 (parallel-OK) → F5.

This PR is the PLAN itself; no code lands here. 5+3 council runs pre-merge
on the plan; the gates above are PLAN-RATIFIED or REJECT-WITH-REASONS per
Inc. Each Inc opens its own PR off the doctrine branch post-ratification.

Cross-plan integration table (§4) ties this plan to
cypher-kanban-ast-unification-v1, lite-unified-surrealql-lance-v1,
probe-excel-compute-dag-v1, E-AR-PROJECTION-CORRECTION-1 Phase 1/2.

Open council questions §9: THINK slot ownership (ClassView vs sibling
ClassPolicies); BindingSet zero-dep tradeoff; crown-detection error
message format lock; F4 corpus surveyability (vendor vs zipball);
cross-plan ClassView layout collision.

Branch rebased onto origin/main (post-#544/#545 merge) — 5 commits ahead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xzyc27Nx3f8WC5KzwfWfjx
AdaWorldAPI added a commit that referenced this pull request Jun 19, 2026
feat(graph): re-land Hamming-plane DistanceMeans on main (stranded by #545 stacked-merge order)
AdaWorldAPI pushed a commit that referenced this pull request Jun 19, 2026
…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
AdaWorldAPI pushed a commit that referenced this pull request Jun 19, 2026
…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
AdaWorldAPI pushed a commit that referenced this pull request Jun 20, 2026
…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
AdaWorldAPI pushed a commit that referenced this pull request Jun 20, 2026
…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
AdaWorldAPI pushed a commit that referenced this pull request Jun 21, 2026
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants