diff --git a/.claude/board/AGENT_LOG.md b/.claude/board/AGENT_LOG.md index c27deb15..7e83afc5 100644 --- a/.claude/board/AGENT_LOG.md +++ b/.claude/board/AGENT_LOG.md @@ -1,3 +1,69 @@ +## [Main thread / Opus 4.7] odoo-classes-bitmask-render-v1 — authored bounded-weekend plan + 10-agent A2A wave split (pre-council) + +**Branch:** claude/activate-lance-graph-att-k2pHI | **Files (additive only):** +- `.claude/plans/odoo-classes-bitmask-render-v1.md` (+513 LOC NEW) — 9 deliverables D-CLS-1..9 across 5 waves, 10 agent runs (2 Opus + 9 Sonnet), full per-agent + per-file ownership matrix, A2A coordination protocol, risk register, 4 spec-owner pre-conditions +- `.claude/board/INTEGRATION_PLANS.md` — prepended new plan entry +- `.claude/board/STATUS_BOARD.md` — appended new section with 9 D-CLS-* rows (all `Blocked-on-OD`) +- `.claude/board/AGENT_LOG.md` — this entry + +**Cargo:** not invoked (plan-only; no code). + +**Outcome:** DONE. User asked: "create a meticulously detailed integration plan and provide a clean per agent and file split for Multiagent A2A." Delivered, then user immediately asked for "5x council and 3x brutally honest review" — 8 reviewers spawning in parallel after this commit. + +**Anchored doctrine line (classes.md:56-57 verbatim):** "The fix is bounded (a weekend, not a subsystem): discriminator + parent-pointer + parent-walking resolution against the existing cache. Full machinery (shape-compiler-to-grid, behavior/traits, SIMD kernels) is explicitly DEFERRED." Plan honours this. + +**Plan structure:** +- **9 non-goals** declared explicitly (no SoA hot-path wiring, no chess slice, no shape-compiler, no SIMD kernels, no Wikidata loader, no discovery_origin byte) +- **7 hard constraints C1-C7** (WAL open, presence-not-semantics, shape inherits not behaviour, frozen ISA, discovered taxonomy, additive only, board hygiene) +- **4 spec-owner gates** OD-DOLCE-CANONICAL/CLASSID-WIDTH/CLASSID-VS-ENTITYKIND/TEMPLATE-ENGINE — all Blocked until ratified +- **5 waves** with per-wave gates + DAG diagram +- **File-level ownership matrix** ensuring no two parallel agents touch the same file +- **A2A coordination protocol** quoting the agent-prompt preamble verbatim (mandatory reads, blackboard contract per CLAUDE.md Layer 2) +- **Risk register** with 11 risks + mitigations +- **3 sections** explicitly NOT in scope (OD-1/2/3 byte work, F4 universal ISA design, chess bring-up D-CHESS-BRINGUP-1) + +**LOC profile:** ~1,800 working LOC + ~2,400 LOC generated (66 snapshots + 15 askama templates). Across 1 NEW crate (`lance-graph-ontology-render` standalone like bgz17/deepnsm) + 3 modified crates (contract, ontology, arm-discovery local newtype + callcenter From impl). + +**Risk acknowledged ahead of council:** Wave-2 Aerial+ on N=66 input may be noisy (wikidata-hhtl-load.md:85 explicitly warned "not measurable on 10"). Plan has fallback: deterministic group-by on structural-hash if Aerial+ unstable. + +**Next:** spawning 5-savant council + 3 brutal critics (8 parallel Opus reviewers) per user instruction. + +--- + +## [Main thread / Opus 4.7] post-#438 integration recalibration — 4-savant council convened, verdicts logged, auto-resolved per protocol + +**Branch:** claude/activate-lance-graph-att-k2pHI (rebased onto post-#438 main, HEAD 8d75294b → new head this commit) | **Files:** +- `.claude/plans/post-438-integration-options-v1.md` (+146 LOC NEW) — the 8-option integration plan the council reviewed +- `.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md` §8 fixed (specs are on main, not on a separate branch — corrected the stale citation from pre-rebase v1) +- `.claude/handovers/2026-05-29-2230-odoo-blueprint-survival-dossier.md` §7 fixed (Wave 1 went D-ARM-13/14 via #436/#438, NOT D-ARM-1/2 as originally planned) +- `.claude/board/ISSUES.md` — 2 new escalations: OD-CANONICAL-SPEC-DISAGREEMENT-TIER-SET + OD-PROPOSER-ID-WIDTH-CHOICE (both flagged as SPEC-OWNER decisions, NOT Claude-session) +- `.claude/board/EPIPHANIES.md` — prepend E-DISCOVERY-ORIGIN-HOME-IS-ARIGRAPH-BRIDGE (R2's missing-integration finding: the byte's natural home is the AriGraph bridge column, not the mailbox-SoA byte) +- `.claude/board/STATUS_BOARD.md` — appended D-CHESS-BRINGUP-1 row to the streaming-arm-nars table (the canonical N4 falsifier, now unblocked by #436's Rust Aerial+) +- This entry + +**Cargo:** workspace `cargo check` clean (only pre-existing v1 CausalEdge deprecation warnings); no code touched this session. + +**Outcome:** DONE per options-doc §5 auto-resolve protocol. User asked: "create the integration plan as a list of possibilities, then use the council and brutally honest review to recalibrate, then continue autoattended autonomous decision making and auto resolve." Delivered. + +**Council convened in parallel (4 reviewers, Opus, single main-thread turn):** +- R1 (architectural-fit): B+C **conditional on adding N1 `class_id` in same pass + u16 width + treat Conjecture as proposer-local + Derived as orthogonal axis**. Author's u8/6-bit lean called penny-wise given class_id must also widen. +- R2 (prior-art): **B+H. Rejects C — re-litigates user-owned forks per core spec F4 + reconciliation OD-1.** Names G (chess) as the canonical N4 freeze-blocker per spec, not a peer option. Surfaces the AriGraph hot↔cold bridge as the ACTUAL integration target the options doc missed entirely (`E-ARIGRAPH-IS-AN-ISLAND` + `D-REUNIFY-1/2/3` prior art). Flags #439 may share CSI-1 ratification gate with sprint-11 queue. +- R3 (integration-coordination): **B+D. Rejects C — in-flight collision with #439** (same `lance-graph-contract` crate, 31 commits, unstable, `KanbanMove` `const _` size assertion ≤16B). Defer C until #439 lands. Names tier-set conflict as "correctness risk laundered as coordination risk." +- R4 (brutal-critic): **B+G. Rejects C as ego-shipping.** Calls author's §3 bias-confessing-then-doing-it-anyway. Names the two canonical specs disagreeing on tier set as SPEC FREEZE, not a Claude-session decision. Cites session's prior hallucinations (CLAUDE.md pin, "70" entities, plan §7.2 vs §8 contradiction) as pattern → "third strike waiting to happen." + +**Auto-resolve verdict (per §5):** +- **B unanimous (4/4) → EXECUTED** (the 2 stale-citation fixes in this commit). +- **C: 3/4 reject → NOT EXECUTED.** +- **OD-1/2/3 → escalated to ISSUES.md as SPEC-OWNER decisions** (R2 + R4 unanimous on this framing). +- **R2's AriGraph-bridge finding → captured as EPIPHANY** (genuinely new, missed by all prior session work). +- **G (chess bring-up): 2/4 endorse → NOT executed this branch** (R1 explicit "needs its own branch + freeze-decision authority"); queued to STATUS_BOARD as D-CHESS-BRINGUP-1. +- **D (#439 help): 1/4 → not executed** (below threshold; R3's lone endorsement). +- **H (cargo clean, 3.3G free): 1.5/4 (R2 + R3 in combos) → flagged for user; not auto-executed** (touches workflow not architecture). + +**What the user has on disk after this commit:** clean rebase onto post-#438 main, fixed stale citations, two new spec-owner decisions logged, one new epiphany surfaced by the council (AriGraph bridge as discovery_origin home), chess bring-up queued as the next canonical falsifier. No code modified. The byte-grammar fight is genuinely paused at the spec-owner gate; the contract crate is untouched. + +--- + ## [Main thread / Opus] D-ARM-14 Phase 1 — splat-top-k oracle + DOLCE skeleton projector **Branch:** claude/jolly-cori-clnf9-darm14 (off post-merge main) | **Files:** `crates/lance-graph-arm-discovery/src/{aerial/codebook.rs (+TopKDistance), aerial/ontology.rs (new), aerial/mod.rs, encode.rs (+checked_slot), lib.rs}` + STATUS_BOARD (D-ARM-14 → In progress). @@ -77,6 +143,61 @@ **D-ids:** D-ARM-13 (**Shipped on branch**); D-ARM-SYN-1/2/3 (**Queued**, council-gated). **Outcome:** DONE. Transcoded **Aerial+** (Karabulut 2025, 2504.19354v1) to zero-dep Rust — the autoencoder leg the plan §14 had explicitly deferred to Python; the user's directive ("transcode aerial rule mining to rust") supersedes that deferral. Faithful port: one-hot encoding → under-complete **denoising autoencoder** (per-feature softmax + cross-entropy, hand-written backprop, seeded SplitMix64 for reproducibility) → **Algorithm 1** reconstruction-probe rule extraction (mark antecedent, uniform elsewhere, forward, τ_a antecedent test + τ_c consequent test) → support/confidence confirmed on data → `CandidateRule`. Tests prove the AE learns a cross-feature dependency and Algorithm 1 recovers a planted rule while rejecting an independent feature. Translator `arm_to_nars` maps `(support, confidence, n) → NARS (f, c)` verbatim per paper §2/§3.3; `ndjson` emits the exact `{"s","p","o","f","c"}` line shape the SPO store loader reads. **Synergy finding:** the Aerial leg is the *runtime-data* frontend of a three-frontend/one-substrate/two-codegen bracket whose substrate (`ruff_spo_triplet::Triple`) and codegen (`ruff_python_codegen` ∥ `op_emitter.rs`) legs already exist in the ruff fork; `ruff_python_dto_check` is the *static-AST* sibling frontend. Key gap surfaced: `ruff_spo_triplet::Predicate` is a closed vocabulary with **no implication/association predicate**, so loading ARM rules through that ndjson path needs `Implies` added there first (D-ARM-SYN-1, deliberate ontology change → council-gated). Determinism boundary preserved: the nondeterministic AE stays a seeded *fan-in proposer*, out of the deterministic compile path, output gated by Stage D. PR to follow. +## [Main thread / Opus 4.7] discovery_origin / ProvenanceTier reconciliation — "what is ACTUALLY correct" (documentation only) + +**Branch:** claude/activate-lance-graph-att-k2pHI | **Commits:** `e727e636` (doc) + this board-hygiene commit | **Files:** +- `.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md` (+~190 LOC) — conflict matrix across code + my 2 committed docs + the 4 uploaded canonical specs; the arithmetic proving they can't all be right; what's correct per canonical; open decisions; §7 Jirak fully verified +- `.claude/board/EPIPHANIES.md` — prepended E-DISCOVERY-ORIGIN-WIDTH finding +- `.claude/board/AGENT_LOG.md` — this entry + +**Cargo:** not invoked (docs + board only; no code). + +**Outcome:** DONE. User: "only document the details ... the original was wrong, codex/coderabbit was wrong, you or the other session suggested to fix it or already fixed it and I don't know what is correct." Delivered a single source-of-truth reconciliation. NO code or plan modified — explicitly held all fixes for user decision. + +**Verified ground truth (file:line):** +- 66 OdooEntity consts reachable from #435 HEAD; "70" was a fuzzy grep; reconciles (53 EXT-6 + 13 Wave-3 = 66). +- `discovery_origin`/`ProvenanceTier` in ZERO `.rs` — only 7 `.claude/` docs. WAL not hardened; fix window open. +- `mod.rs:450 OdooConfidence` = {Curated, Extracted, Conjecture} (3) — the only provenance enum that compiles. +- proposer-id: committed 2 bits (full) < review 3 bits (8) < canonical "widen to 6 bits/64 or u16". Committed = most-wrong. +- ProvenanceTier: 6 names across corpus vs 2-bit/4-slot field; my plan self-contradicts (§7.2=4, D-ARM-1=5). +- Jirak: correct `n^{-(p/2-1)}`; plan lines 381+393 have reciprocal bug `n^{-1/(p/2-1)}` (line 375 correct); default p=3.0 == classical, use 2.5. Bug silently disables the noise floor (1e-20 vs 0.056 at n=1e5,p=2.5). + +**Held for user (not applied):** OD-1 width (6-bit vs u16); OD-2 Conjecture/Derived; OD-3 code/spec divergence; the 3-line Jirak patch (exact targets given in doc §7.4). + +--- + +## [Main thread / Opus 4.8] Odoo blueprint survival dossier — inventory + strategies + tools (session-survival; "doesn't get lost") + +**Branch:** claude/activate-lance-graph-att-k2pHI | **Files (all new, additive):** +- `.claude/knowledge/odoo-blueprint-inventory-v1.md` (+204 LOC) — index of all 66 `pub const OdooEntity` declarations across 15 lanes; per-lane summary; wave provenance; field/method density audit; EXT-2 coverage matrix; what's NOT in the corpus +- `.claude/knowledge/odoo-extraction-strategies-v1.md` (+~240 LOC) — the three proposer legs (Curated/Extracted/ArmDiscovered): what each sees, what each emits, throughput, confidence, council posture, convergence diagram, ProvenanceTier ordering, what the doctrine forbids +- `.claude/knowledge/odoo-extraction-tools-v1.md` (+~210 LOC) — tool stacks: Sonnet-agent fan-out, `tools/odoo-blueprint-extractor/` Python (654 LOC entry + 950 LOC parsers), planned `lance-graph-arm-discovery` Rust crate, `ruff_spo_triplet` cross-language SPO IR; run procedures; where each tool lives if the session dies +- `.claude/handovers/2026-05-29-2230-odoo-blueprint-survival-dossier.md` (+~140 LOC) — survival pointer + verified numbers + next-move priority list + +**Cargo:** not invoked (knowledge docs + handover; no code). + +**Outcome:** DONE. User asked: "create a PR with all 70, strategies for extraction and tools for extraction so it doesn't get lost and if the session dies the other session has EVERYTHING." Delivered. + +**Verified numbers (on-disk grep, 2026-05-29):** +- **66 pub const OdooEntity** (canonical count via `^pub const [A-Z_0-9]+: OdooEntity` — the "70" was a fuzzy match including nested struct refs) +- 11,563 LOC across 15 lane files; 130 lane tests +- 99,209 LOC of EXT-2 extracted backing in 11 addon files +- 48/53 = 90.6% TIER-1 coverage per EXT-6 +- Wave 1 (commit `f5702675`) = 21 entities, L1-L5, **5 Sonnet agents** (the user's "5 agents" question) +- Wave 2 dedicated (`d30186e5`) + Wave 2/3 (`333a1ff2`) + EXT-3 back-fill (`c04adf10`) = remaining 45 entities + +**Discovery during inventory pass:** +- The L1 const block has 52 field-kind hits + 41 method-kind hits + 8 decorator-kind hits — densest lane in the corpus alongside L5 (55 fields / 26 methods / 11 decorators) and L10 (36/16/2) +- State-machine presence audit needs a follow-up: multi-line `OdooStateMachine` formatting evades the indented-grep; manual verification needed for L1/L2/L5/L6/L7/L11 +- L12-L15 (Wave-3 curated additions) are POST-EXT-6 and need a fresh extractor pass to bring into `CURATED_EXTRACTED_PAIRS`; flagged as Stage-2 work +- 6 dark D-Atoms (Money/Quantity/ApplyRate/EmitAmount/Event/FiscalCtx) don't fire today because `return_kind` defaults to `Unit` and `semantic_role` defaults to `Other` across most extracted entries — extractor `parsers/methods.py` + `parsers/fields.py` are the enrichment targets + +**Cross-refs added (knowledge → plan / handover / paper):** +- `streaming-arm-nars-discovery-v1.md` (Leg 3 plan, this PR #435) +- `odoo-business-logic-blueprint-v1.md` (Leg 1 plan) +- `odoo-source-extraction-v1.md` (Leg 2 plan) +- `epiphany-brainstorm-council` (PR #433 ratification gate) +- Karabulut 2025 (arxiv 2504.19354v1) Leg-3 anchor; Abreu 2025 (arxiv 2511.13661v1) externalize-interpretation doctrine --- diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 7902a712..06b152b5 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -523,6 +523,20 @@ The SoA is FIXED (frozen byte-shape) ⇒ the "duplicate" is the SAME shape insta **BindSpace consumers (singleton→per-mailbox migration surface, grep 2026-05-30):** contract `{cognitive_shader,splat,lib}.rs`; planner `{cache/convergence,lib}.rs`; cognitive-shader-driver `{driver,wire,engine_bridge,mailbox_soa,proposal,serve,wire_dto,spo_bridge,cognitive_shader,cognitive_shader_dispatch,spo_witness,cam}.rs`. The singleton still threads through driver/engine_bridge/wire*; `spo_witness.rs` is the existing witness seam to reconcile with EW64. (Ref D-MBX-3/5: kill `BindSpace::zeros(4096)` singleton; migrate consumers to per-mailbox MailboxSoA.) **Cross-ref:** `E-ARIGRAPH-PAPER-GROUNDS-CE64-EW64`; `E-AERIAL-FEEDS-EW64-PREFETCH`; `E-ARIGRAPH-IS-AN-ISLAND`; `F-RESONANCEDTO-IS-LAYERED-NOT-DUP`; D-MBX-3/5/A5; I-VSA-IDENTITIES. +## 2026-05-30 — E-DISCOVERY-ORIGIN-HOME-IS-ARIGRAPH-BRIDGE — the `discovery_origin` byte's natural home is the AriGraph hot↔cold bridge column, not the mailbox-SoA byte — surfaced by council prior-art savant 2026-05-30 + +**Status:** FINDING (R2 council verdict, 2026-05-30, recorded in `AGENT_LOG.md`). Documentation-only; no code or plan modified. Cross-ref: `E-ARIGRAPH-IS-AN-ISLAND` (the prior finding the options doc author missed), `E-ARIGRAPH-PAPER-GROUNDS-CE64-EW64` (CE64 = AriGraph semantic edge; EW64 = AriGraph episodic edge), `.claude/plans/post-438-integration-options-v1.md` §3 (the author's bias the council attacked). + +The 2026-05-30 council convening on the post-#438 integration options surfaced an integration target the options-doc author **completely omitted**: the AriGraph hot↔cold bridge. R2 cited `E-ARIGRAPH-IS-AN-ISLAND` + `D-REUNIFY-1/2/3` — the unwired bridge between `arigraph/triplet_graph.rs` (semantic Es) / `arigraph/witness_corpus.rs` (episodic Ee) and the hot SoA (CE64/EW64 cols) / cold Lance store. Per `E-ARIGRAPH-PAPER-GROUNDS-CE64-EW64`, CE64 IS an AriGraph semantic edge; EW64 IS an AriGraph episodic edge; the witness arc IS Ee. + +**The reframe:** if `discovery_origin` widens to `u16`, its natural carrier is **a column on the AriGraph bridge**, not the mailbox-SoA byte. Three reasons: +1. Provenance is a property of WHERE-A-FACT-CAME-FROM = what the AriGraph semantic edge encodes (CE64's `discovery_origin` slot lives natively there). +2. The mailbox-SoA byte is a hot-path performance compromise that bakes provenance into the row-level layout; the bridge is the architectural seam where hot meets cold and provenance MUST be materialized per witness-materialization invariant (cognitive-risc-core #5). +3. The wikidata spec's `Derived` tier is *already* declared as living on the reasoning store via `provenance=Derived` column (wikidata-hhtl-load.md:25). The same column home works for the other tiers; the byte is then a hot-path projection of the bridge column, not the source of truth. + +**Author's options-doc miss:** all 8 options framed `discovery_origin` as a mailbox-SoA byte question. None considered the bridge-column framing. The kind of architectural drift the prior-art-savant role exists to catch. + +**Consequence:** OD-1 (6-bit vs u16) is partially settled by this. If the byte is a projection of a bridge column, the column can be u16 (or any width) without binding the hot-path byte. The byte becomes a cache, not the ISA. --- @@ -682,6 +696,18 @@ The two-paper bracket (`streaming-arm-nars-discovery-v1.md`: Aerial+ discovery u The planner⟷ractor⟷surrealdb wiring (user-requested 2026-05-30) is realized WITHOUT a new DTO family (`lab-vs-canonical-surface.md` §"Decision Procedure"): extend the canonical `OrchestrationBridge`/`UnifiedStep` surface (`StepDomain::Kanban` + `"kanban."` prefix) + add `kanban::{KanbanColumn, KanbanMove}` + a zero-dep borrow trait `soa_view::MailboxSoaView` returning `&[T]` column slices. The borrow trait is the key move: it lets the in-RAM `MailboxSoA` (ractor-owned, in cognitive-shader-driver), a surreal kv-lance view, and the planner all read the SAME bytes through one vocabulary — the dependency-inversion pattern already used by `PlannerContract`/`OrchestrationBridge`, so the contract stays zero-dep (it cannot name `MailboxSoA` from another crate without a dep). The `MailboxSoaView` (read) vs `MailboxSoaOwner` (mutate `advance_phase`) split makes "the view is read-only" a *structural* guarantee (surreal implements only the read half) — honoring R1 by type, not convention. **Cross-ref:** `E-SOA-IS-THE-ONLY` (R1/R4 origin); `lab-vs-canonical-surface.md` §Decision Procedure; `unified-soa-convergence-v1.md §5+§8.4` (D-MBX-A6); LATEST_STATE Contract Inventory 2026-05-30. +## 2026-05-30 — E-DISCOVERY-ORIGIN-WIDTH — the discovery_origin byte is over-subscribed in BOTH fields, and the Jirak threshold has a 3-place reciprocal bug + +**Status:** FINDING (verified on-disk, file:line cited). Documentation-only; no code/plan changed. Full detail: `.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md`. Author-stated; not council-gated. + +Surfaced answering the user's "I don't know what is correct" about discovery_origin / ProvenanceTier, after they supplied the canonical `cognitive-risc-*` specs. + +1. **`discovery_origin` exists in ZERO `.rs` files** — only 7 `.claude/` docs. The WAL has not hardened around it; the "ISA-ossification trap" window is OPEN, fix cost = markdown edit, not migration. +2. **proposer-id width is wrong everywhere, monotonically.** Committed plan §7.2 = 2 bits (4 slots, full); #434 review = 3 bits (8); canonical core spec = 3 bits but declares it insufficient and says widen to **6 bits (64) or u16**. The committed version is the most-wrong. +3. **ProvenanceTier is ALSO over-subscribed (nobody had flagged this).** Six distinct tier names exist across the corpus — Curated/Extracted/Conjecture/ArmDiscovered/Ratified/Derived — but every layout gives the field 2 bits = 4 slots. My own ARM plan contradicts itself: §7.2 enumerates 4, D-ARM-1 enumerates 5. Code today (`mod.rs:450 OdooConfidence`) has only {Curated, Extracted, Conjecture}. The canonical specs disagree with *themselves* (core: ArmDiscovered/Ratified; wikidata: Derived). +4. **Jirak threshold reciprocal bug, verified in 2 of 3 places.** Correct rate `n^{-(p/2-1)}` (matches CLAUDE.md `I-NOISE-FLOOR-JIRAK` and the plan's own worked examples + blockquote line 375). Plan line 381 (prose) and line 393 (pseudocode `powf(-1.0/(p/2-1))`) write the reciprocal `n^{-1/(p/2-1)}`. At n=1e5, p=2.5 the bug makes the floor ~1e-20 instead of ~0.056 — i.e. silently disables the noise floor the iron rule calls "not optional." Also: default `p=3.0` sits exactly at the classical Berry-Esseen crossover, so the "stricter than IID" claim is false at the default; use `p≈2.5`. + +**Open decisions (user's, not resolvable by citation):** OD-1 6-bit vs u16; OD-2 fate of Conjecture + treat Derived as a separate reasoning-provenance axis; OD-3 code/spec divergence on Conjecture. None applied — held for the user. --- diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index 7a0cdf50..82821e30 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -1,3 +1,12 @@ +## 2026-05-31 — odoo-classes-bitmask-render-v1 (the bounded-weekend classes.md fix: `ClassId` discriminator + per-class `FieldPositionTable` + `FieldMask(u64)` + per-class askama templates + Aerial+ shape-family discovery over the 66 OdooEntities) + +**Status:** PROPOSAL / integration plan, pre-council. Design-spec only, no code. **Plan file:** `.claude/plans/odoo-classes-bitmask-render-v1.md`. **Anchored spec:** `cognitive-risc-classes.md` v0.2 §"Jinja = classes + presence bitmask" + §"NON-DEFERRABLE freeze-time moves" (N1, N3) — bounded-weekend doctrine (line 56-57): *"discriminator + parent-pointer + parent-walking resolution against the existing cache. Full machinery (shape-compiler-to-grid, behavior/traits, SIMD kernels) is explicitly DEFERRED."* +**Owns:** the structural fix for the union-disease classes.md known-debt names (line 55: "single-SoA is a union pretending to be uniform"). Adds 9 deliverables D-CLS-1…D-CLS-9 across 5 waves (10 agent runs: 2 Opus + 9 Sonnet, ~1,800 working LOC + ~2,400 LOC of generated snapshots/templates). One new excluded crate `lance-graph-ontology-render` (askama, F3 lean). Resolves the 4-way `DolceCategory` duplication on-disk via additive re-export + deprecation (zero rename per hard constraint C6). +**Anchored iron rules:** none new — this plan respects all existing iron rules. **Anchored epiphanies:** `E-DISCOVERY-ORIGIN-HOME-IS-ARIGRAPH-BRIDGE` (the structurally adjacent insight from yesterday's council), `E-TEMPLATE-IS-CHECKLIST-IS-DATOMS` + `E-RELIABILITY-IS-CHECKLIST-COVERAGE` (the parallel-session #439 ThoughtMask shape this plan generalises to FieldMask). +**Predecessors:** the 4 canonical cognitive-risc specs (now on main via commits `d1635dbe`, `93ac0463`, `a16d0f41`, `45276eb3`); PR #435 (the 66-entity survival dossier this plan operates on); PR #436 (the Aerial+ Rust crate D-CLS-3 calls); PR #439 (the `ThoughtMask` whose shape this plan generalises). **Spec-owner gates open:** OD-DOLCE-CANONICAL, OD-CLASSID-WIDTH, OD-CLASSID-VS-ENTITYKIND, OD-TEMPLATE-ENGINE — all 4 ratified before any agent starts (plan §2). + +--- + ## 2026-05-29 — streaming-arm-nars-discovery-v1 (upstream proposer leg: 20K-200K rows/window pair-stats + optional Aerial+ neural fan-in → NARS-truth translator → SpoStore hypothesis test → epiphany-council ratification → op_emitter codegen) **Status:** PROPOSAL / integration plan. Design-spec only, no code. **Plan file:** `.claude/plans/streaming-arm-nars-discovery-v1.md`. **Handover:** `.claude/handovers/2026-05-29-2030-arm-discovery-author-to-impl.md`. **Candidate epiphany:** `E-DISCOVERY-CODEGEN-BRACKET-1` (council-pending). diff --git a/.claude/board/ISSUES.md b/.claude/board/ISSUES.md index 3256889e..284babd6 100644 --- a/.claude/board/ISSUES.md +++ b/.claude/board/ISSUES.md @@ -1,5 +1,39 @@ # Issues Log — Open + Resolved (double-entry, append-only) +## 2026-05-30 — OD-CANONICAL-SPEC-DISAGREEMENT-TIER-SET — `cognitive-risc-core.md` and `wikidata-hhtl-load.md` disagree on the ProvenanceTier value-set; SPEC-OWNER decision, not Claude-session + +**Status:** Open · Owner: spec author (NOT a Claude session) · Blocks: D-ARM-1 (ProvenanceTier in `lance-graph-contract`), D-ARM-2 (`Proposer` trait + `CandidateRule`), D-ARM-SYN-1/2/3 (per PR #436 follow-ups). + +The four canonical specs at `.claude/specs/` disagree among themselves: +- `cognitive-risc-core.md:58` → tier set `{Curated, Extracted, ArmDiscovered, Ratified}` marked `[stable]`. +- `wikidata-hhtl-load.md:25` → tier set `{Curated, Extracted, Derived}`. +- `faiss-homology-cam-pq.md:14` → "Reasoning layer = separate indexed store, **Derived tier**" — argues `Derived` is a *separate axis*, not a tier value. +- Code today (`crates/lance-graph-ontology/src/odoo_blueprint/mod.rs:450`) → `OdooConfidence::{Curated, Extracted, Conjecture}` — a third value-set, neither matching the core spec nor the wikidata spec. + +4-of-4 council reviewers (2026-05-30, recorded in `AGENT_LOG.md` + `post-438-integration-options-v1.md` §4) verdict: do NOT ship `ProvenanceTier` into `lance-graph-contract` until the spec owner reconciles. Two of the four reviewers (R2 + R4) explicitly call this a SPEC FREEZE issue, not a Claude-session decision. + +**Council's recommended default if the spec owner wants one to ratify or reject:** keep the core's stable-4 as the on-byte tier; treat `Derived` as a separate orthogonal "reasoning provenance" axis (per faiss-homology + wikidata "orthogonal=beside, not mixed in"); decide `Conjecture`'s fate by either dropping it from code (it's unused per `git grep`) or mapping it to a proposer-local discovery-time label that never crosses the wire. + +Cross-ref: `.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md` §2.1 (full conflict matrix), §6 (OD-1/2/3), §8 (specs-on-branch correction). + +--- + +## 2026-05-30 — OD-PROPOSER-ID-WIDTH-CHOICE — 6-bit (64 slots, u8) vs `u16` for `discovery_origin` proposer-id field; SPEC-OWNER lean exists, decision is pending + +**Status:** Open · Owner: spec author · Blocks: D-ARM-1, D-MBX-A6-P3 (if `discovery_origin` rides alongside `KanbanMove`). + +`cognitive-risc-core.md:62` explicitly says "Widen proposer field (steal reserved → 6 bits/64, or go u16) before surrealkv WAL hardens the LE wire format" — names two alternatives, does not pick. `cognitive-risc-classes.md:64` restates the same problem as freeze-time move N2. + +The current `streaming-arm-nars-discovery-v1.md` §7.2 (committed on PR #435 branch, NOT in code) allocates 2 bits = 4 slots and is already full (AstWalker/PairStats/Aerial/Other). #436's PR-note explicitly defers the contract carrier to D-ARM-1. + +Council R1 (architectural-fit): u16 (because `class_id`/N1 must ship in the same freeze pass and u16 fits both decisions). Council R3 (integration-coordination): defer this choice until #439 lands (it's mid-flight on `lance-graph-contract`). + +**This issue and OD-CANONICAL-SPEC-DISAGREEMENT-TIER-SET are paired** — both touch the same byte grammar; ship them in one council-ratified pass or wait until both are settled. + +Cross-ref: `.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md` §6 OD-1; `cognitive-risc-classes.md` §"NON-DEFERRABLE freeze-time moves" N1+N2; PR #439 (open, kanban Phase 2). + +--- + > **Append-only ledger.** Every issue (bug, regression, invariant > violation, blocker) gets a dated entry here. Entries move from > Open → Resolved by status-flip; they are NEVER deleted. diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index f1894a92..7c9ac984 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -642,6 +642,25 @@ The missing UPSTREAM discovery leg. Today's proposers (curated L-docs + AST-extr | D-ARM-SYN-1 | Add `Implies`/`CoOccursWith` to `ruff_spo_triplet::Predicate` closed vocabulary (+ `Provenance` tier) so ARM rules load through the same `parse_triples` ndjson path as the static extractor | ruff/ruff_spo_triplet | 40 | MED | **Queued** | council-gated (deliberate ontology change); blocks SYN-2; see `.claude/knowledge/aerial-arm-ruff-spo-codegen-synergies.md` §1 | | D-ARM-SYN-2 | `CandidateRule → ruff_spo_triplet::ModelGraph` adapter so the Aerial runtime-data leg joins the `ruff_python_dto_check` static-AST leg in one graph before `expand()` | lance-graph-arm-discovery + ruff_spo_triplet | 120 | MED | **Queued** | depends on SYN-1; synergy doc §2 | | D-ARM-SYN-3 | Calibrate `ProvenanceTier::ArmDiscovered` `(f,c)` below the `op_emitter` ratification gate + below static `Inferred (0.85,0.75)` so un-ratified ARM truth is council-visible but codegen-filtered | lance-graph-contract + lance-graph-ontology::op_emitter | 30 | MED | **Queued** | depends on D-ARM-1 + SYN-1; synergy doc §3/§4 | +| **D-CHESS-BRINGUP-1** | **Chess-into-OWL falsification slice** — encode 3-5 opening positions as OWL/ttl (meaning in CONTENT, no chess-special SoA field), run `lance-graph-arm-discovery::AerialProposer` (the now-shipped #436 Rust transcode) over it, see whether GM-flavored *legal* candidates fall out, AND whether chess needs columns Odoo's SoA didn't have. The cognitive-risc-core/classes spec's emphatic **N4 freeze-time non-negotiable** — falsifies/confirms "one SoA serves all" cheaply on a board, before the WAL freezes. Council R2+R4 verdict 2026-05-30: this is FIRST, not a peer option. | lance-graph-arm-discovery (read) + new crate `chess-bringup-test` | ~300 | HIGH | **Queued** | NEW (council recalibration 2026-05-30). NOT in scope for branch `claude/activate-lance-graph-att-k2pHI` (per R1 — needs its own branch + freeze-decision authority). Unblocked by #436's Aerial+ shipping in Rust (user-flagged 2026-05-30 "aerial+ has been transcoded and is now a lance-graph-* crate"). Cross-ref `cognitive-risc-core.md` §"The bring-up test"; `cognitive-risc-classes.md:66` N4; `post-438-integration-options-v1.md` §1 Option G. | + +--- + +## odoo-classes-bitmask-render-v1 — bounded-weekend ClassId + FieldMask + per-class askama templates (Aerial+ discovers ~10-15 shape-families from 66 OdooEntities; presence-bitmask render path) + +The bounded-weekend fix `cognitive-risc-classes.md:56-57` prescribes (discriminator + parent-pointer + parent-walking; full machinery deferred). 4-way `DolceCategory` consolidation + `ClassId(u16)` hook + per-class `FieldPositionTable` + `FieldMask(u64)` + per-class askama templates. **All deliverables `Blocked-on-OD` until spec owner ratifies OD-DOLCE-CANONICAL, OD-CLASSID-WIDTH, OD-CLASSID-VS-ENTITYKIND, OD-TEMPLATE-ENGINE.** Plan: `.claude/plans/odoo-classes-bitmask-render-v1.md`. + +| D-id | Title | Crate | Lines | Conf | Status | Notes | +|---|---|---|---|---|---|---| +| **D-CLS-1** | Canonical `DolceCategory` in `lance-graph-contract` + re-exports from 3 sites + `From for DolceMarker` | lance-graph-contract + 3 modified | 80 | HIGH | **Blocked-on-OD** | Wave 1A, Sonnet. Additive per C6. Arm-discovery uses local newtype + TryFrom (zero-dep stance preserved) | +| **D-CLS-2** | Structural-signature audit of 66 OdooEntities → `.claude/knowledge/odoo-66-structural-signatures.psv` | lance-graph-ontology (read only) | 230 | HIGH | **Blocked-on-OD** | Wave 1B, Sonnet. Read-only emit; BLAKE3-128 truncated u64 per-entity hash | +| **D-CLS-3** | Aerial+ structural-hash → ~10-15 candidate shape-families + ratified `CANONICAL_CLASS_TABLE` | lance-graph-arm-discovery (example) | 350 | MED | **Blocked-on-OD** | Wave 2A, **Opus**. SPEC-OWNER GATE after output: names + ratifies clusters | +| **D-CLS-4** | New `lance-graph-ontology-render` crate skeleton + askama dep + `exclude=` workspace entry | new crate | 70 | HIGH | **Blocked-on-OD** | Wave 1C, Sonnet. Standalone like bgz17/deepnsm | +| **D-CLS-5** | `ClassId(u16)` newtype + `UNCLASSIFIED` const in `lance-graph-contract::cognition::entity` | lance-graph-contract | 40 | HIGH | **Blocked-on-OD** | Wave 3A, Sonnet. The N1 hook | +| **D-CLS-6** | `class_id: ClassId` field on `OdooEntity` + back-fill 66 consts via ratified CANONICAL_CLASS_TABLE | lance-graph-ontology (mod + 15 lanes + new CLASS_TABLE.rs) | 260 | HIGH | **Blocked-on-OD** | Wave 3B, Sonnet. Mechanical edit across 15 lane files | +| **D-CLS-7** | `FieldMask(u64)` + `FieldPositionTable` (N3 append-only positions) + per-class width audit | lance-graph-contract (new field_mask.rs) + lance-graph-ontology (CLASS_TABLE extend + class_audit.rs) | 250 | HIGH | **Blocked-on-OD** | Waves 3C + 3D + 3E split (5 Sonnet agents in Wave 3 total) | +| **D-CLS-8** | Per-class askama templates (~10-15 .txt.j2) + `render(entity, mask) -> String` + per-class smoke tests | lance-graph-ontology-render (lib + templates + tests) | 510 | MED | **Blocked-on-OD** | Wave 4 (3 Sonnet agents — templates, dispatch, tests) | +| **D-CLS-9** | Integration test 66 entities × class templates + C2 mutant-mask test + mask-density audit report | lance-graph-ontology-render (tests + audit + snapshots) | 2,310 | HIGH | **Blocked-on-OD** | Wave 5A, **Opus**. Bulk LOC is 66 generated snapshots + per-class density report | --- diff --git a/.claude/handovers/2026-05-29-2230-odoo-blueprint-survival-dossier.md b/.claude/handovers/2026-05-29-2230-odoo-blueprint-survival-dossier.md new file mode 100644 index 00000000..8c0f21b3 --- /dev/null +++ b/.claude/handovers/2026-05-29-2230-odoo-blueprint-survival-dossier.md @@ -0,0 +1,128 @@ +# Handover — Odoo blueprint survival dossier + +**Date:** 2026-05-29 22:30 UTC +**From session:** continuation on `claude/activate-lance-graph-att-k2pHI` (post PR #433 + PR #434 merges; PR #435 open). +**To:** any future session that needs to find the 66 curated Odoo entities, the three extraction strategies, the tools that back each, or the survival index of all of the above. + +--- + +## Purpose of this handover + +User-stated need (2026-05-29 21:30 UTC): "create a PR with all 70, strategies for extraction and tools for extraction so it doesn't get lost and if the session dies the other session has EVERYTHING." + +This dossier exists so that **a fresh session reading only this file + the three companion knowledge docs has full context** to: +1. Find any of the 66 curated `OdooEntity` consts by name, model, or lane. +2. Understand which extraction strategy each existed entity came from. +3. Run any of the three extraction tool stacks. +4. Identify gaps (Stage-2 dark atoms, TIER-2 deferrals, L12-L15 post-EXT-6 pairing). +5. Pick up the work on PR #435 (ARM-discovery plan + op_emitter) without re-deriving. + +--- + +## The four artifacts (all in `.claude/`) + +| Artifact | Path | Purpose | +|---|---|---| +| **Inventory** | `.claude/knowledge/odoo-blueprint-inventory-v1.md` | Index of all 66 entities (alphabetical), per-lane summary, wave provenance, field/method density audit | +| **Strategies** | `.claude/knowledge/odoo-extraction-strategies-v1.md` | The three proposer legs (Curated/Extracted/ArmDiscovered): what each sees, what each emits, throughput, confidence, council posture | +| **Tools** | `.claude/knowledge/odoo-extraction-tools-v1.md` | Where to find each tool, how to run it, current status, gaps | +| **This handover** | `.claude/handovers/2026-05-29-2230-odoo-blueprint-survival-dossier.md` | Pointer + survival summary | + +--- + +## The numbers (verified on disk 2026-05-29) + +- **66 `pub const OdooEntity` declarations** across 15 lane files. + - Wave 1 (commit `f5702675`): 21 entities, L1-L5, **5 Sonnet agents** (the question's "5 agents"). + - Wave 2 dedicated (`d30186e5`): 6 entities, L9 canonical FiscalPositionResolver. + - Wave 2 + Wave-3 trims (`333a1ff2`): 39 entities, L6-L10 (incremental) + L11-L15 (5 Sonnet agents again). + - EXT-3 follow-up (`c04adf10`): 0 entities added; back-fill of `OdooEntityKind` + `regulation_iri` across all 66. +- **11,563 LOC** across `crates/lance-graph-ontology/src/odoo_blueprint/l{1..15}.rs`. +- **130 lane tests** (Wave-1 left L1 with 0 tests at parent-module level; Wave 2+3 propagated per-lane test convention). +- **99,209 LOC of EXT-2 extracted backing** in `crates/lance-graph-ontology/src/odoo_blueprint/extracted/{account,…}.rs` (11 addon files). +- **48/53 = 90.6% TIER-1 coverage** per EXT-6 report (covered: L1-L11 to 100%; 5 TIER-2 deferrals on L11; L12-L15 are post-EXT-6 and need a fresh pass). +- **0 entities at risk of loss** — all 66 are in `main` as of `c04adf10`. + +--- + +## The three extraction strategies (one-paragraph each) + +1. **Curated (Leg 1, D-ODOO-BP-1b):** Sonnet agents project L-doc prose → typed `OdooEntity` consts. 5 agents per Wave, 1 per lane. Highest-trust tier. No council ratification needed (PR #433 council-bypass for human-authored content). +2. **Extracted (Leg 2, D-ODOO-EXT-2):** `tools/odoo-blueprint-extractor/` Python package walks Odoo source ASTs with stdlib `ast` module, emits `EXT_` consts with `OdooConfidence::Extracted`. 12 TIER-1 addons covered; TIER-2 deferred. `pairing.rs::CURATED_EXTRACTED_PAIRS` surfaces conflicts for human review (canonical-on-conflict rule: curated wins). +3. **ArmDiscovered (Leg 3, this PR's plan):** Streaming pair-stats over parquet windows (deterministic trunk) + optional Aerial+ neural-symbolic fan-in. Outputs candidate `(s,p,o,f,c)` triples; routes through NARS revision (Stage C); novel candidates queue for council ratification (Stage D, MANDATORY). The council gate IS the determinism firewall before codegen. + +--- + +## The three tool stacks (one-paragraph each) + +1. **Sonnet-agent fan-out (Leg 1).** Not a code tool — a prompt + spawn pattern. Main thread spawns N `Agent(general-purpose, sonnet)` calls in parallel; each agent reads one L-doc + the typed `OdooEntity` surface + (optionally) an exemplar lane; emits one `l.rs` file. Main thread reviews, trims, commits. +2. **`tools/odoo-blueprint-extractor/` (Leg 2).** Python 3 package, stdlib-only. 654 LOC entry + pairing + 950 LOC parsers (classes/fields/methods/state_machine/constraints/decorators/regulation). Run via `python -m odoo_blueprint_extractor --addons /home/user/odoo/addons --addon --out `. Stage-2 enrichment targets: richer `return_kind` inference in `parsers/methods.py`; lexical `semantic_role` mapper in `parsers/fields.py`. +3. **`lance-graph-arm-discovery` (Leg 3, queued).** Rust crate, specified in `.claude/plans/streaming-arm-nars-discovery-v1.md`. 2,400 LOC across 12 D-ARM-* deliverables. Default trunk is pair-stats (deterministic); `arm-aerial` feature gates Aerial+ Python subprocess IPC. Cross-cutting: `ruff_spo_triplet` in `AdaWorldAPI/ruff` provides the language-agnostic SPO IR. + +--- + +## Five fastest paths into the corpus (if you have a specific need) + +| If you need to … | Read this first | +|---|---| +| Find a specific entity by name | `odoo-blueprint-inventory-v1.md` §2 (alphabetical index) | +| Find all entities in a lane | `odoo-blueprint-inventory-v1.md` §1 (per-lane summary, opens lane file + line numbers) | +| Run the extractor on a new addon | `odoo-extraction-tools-v1.md` §2 (run procedure) | +| Light a Stage-2 dark D-Atom | `odoo-extraction-tools-v1.md` §2 (known gaps subsection — extractor enrichment targets) | +| Add a new ProvenanceTier (4th leg) | `odoo-extraction-strategies-v1.md` §6 (what this doctrine forbids — the rules for any new leg) | +| Implement ARM-discovery Wave 1 | **STALE** — Wave 1 went via PR #436 (D-ARM-13: Aerial+ Rust transcode as standalone crate `lance-graph-arm-discovery`) + PR #438 (D-ARM-14 P1: splat-top-k oracle + DOLCE projector), NOT D-ARM-1/2 as originally planned. The contract carriers (`ProvenanceTier`, `Proposer`, `CandidateRule`) are still NOT in `lance-graph-contract` per #436 PR note ("no contract carriers added yet — `rule`/`translator` are the local seam until D-ARM-1/2 land them"). New entry point: read `crates/lance-graph-arm-discovery/src/` + `.claude/plans/post-438-integration-options-v1.md` for the council recalibration. | + +--- + +## What's at risk of getting lost across sessions (and isn't anymore) + +**Pre-this-dossier risks:** +1. The 5-agent Wave pattern was tacit (no agent card formalized it). +2. The "Curated vs Extracted vs ArmDiscovered" doctrine was implicit across three different plans. +3. The 5 TIER-2 deferrals were buried in `extracted/COVERAGE.md` without cross-reference. +4. The L12-L15 post-EXT-6 pairing gap was unflagged. +5. The Stage-2 dark-atom enrichment targets were spread across `style_recipe.rs` comments, the extractor source, and the user's chat history. +6. The `ruff_spo_triplet` cross-language relevance was discoverable only via session-history pattern-matching. + +**Post-this-dossier:** all six are explicitly captured. A session that loads only `odoo-blueprint-inventory-v1.md` + `odoo-extraction-strategies-v1.md` + `odoo-extraction-tools-v1.md` + this handover has the complete map. + +--- + +## Concrete next moves (in priority order) + +1. **Council ratification of `E-DISCOVERY-CODEGEN-BRACKET-1`** + the §7 corrections to PR #434. Spawn the 5-savant council. Verdict: LAND / REVISE-to-LAND / REJECT. +2. **Apply the Jirak math fix** to `streaming-arm-nars-discovery-v1.md` §4 (formula `n^{-(p/2-1)}` not `n^{-1/(p/2-1)}`, default `p ≈ 2.5` not `3.0`) — see cross-session review from #434 author (in session record). +3. **Coordinate with #434 author on unified-soa-convergence-v1.1 amendment** for the `discovery_arc` D=8 column + `discovery_origin: u8` byte (preferred path per cross-session review: amend v1 plan rather than carve into D-ARM-6). +4. **Stage-2 extractor enrichment**: `parsers/methods.py` `return_kind` inference + `parsers/fields.py` `semantic_role` lexical mapper. Lights 6 dark D-Atoms (Money/Quantity/ApplyRate/EmitAmount/Event/FiscalCtx) — independent of ARM-discovery. +5. **Post-EXT-6 pairing pass** for L12-L15 to bring the Wave-3 curated entities into `CURATED_EXTRACTED_PAIRS`. +6. **OQ-ARM-11 batch**: workspace MCP allowlist update for `AdaWorldAPI/aerial-rule-mining` + `surreal_container` (cross-session review identified these as shared blocker class). +7. **ARM-discovery Wave 1**: D-ARM-1 (`ProvenanceTier` enum) + D-ARM-2 (`Proposer` trait). Contract additions; one PR, one Sonnet agent, main thread verifies cargo. + +--- + +## Risk to flag + +The discovery leg (Leg 3) ships a new upstream proposer node. If it's implemented without observing these three invariants the substrate gets polluted: + +1. **Don't promote ArmDiscovered → Ratified without the council** (the gate is non-negotiable; auto-ratification destroys the determinism firewall). +2. **Don't conflate the witness arc with a discovery arc** (committed revisions vs in-flight candidates are different audit trails; PR #435 §7 proposes the separation). +3. **Don't optimize before benchmarking** (D-ARM-12 bench establishes throughput envelope + `p_moment` empirically; premature SIMD/GPU work is a distraction). + +--- + +## Cross-refs + +- `odoo-blueprint-inventory-v1.md` — the 66-entity index. +- `odoo-extraction-strategies-v1.md` — the three legs. +- `odoo-extraction-tools-v1.md` — the tool stacks. +- `.claude/plans/odoo-business-logic-blueprint-v1.md` — Leg 1's plan. +- `.claude/plans/odoo-source-extraction-v1.md` — Leg 2's plan + EXT-6 report. +- `.claude/plans/streaming-arm-nars-discovery-v1.md` — Leg 3's plan (this branch, PR #435). +- `.claude/handovers/2026-05-29-2030-arm-discovery-author-to-impl.md` — Leg 3's implementation handover. +- `.claude/handovers/2026-05-29-1825-soa-convergence-author-to-impl.md` — PR #434's unified-SoA handover (the substrate that all three legs write into). +- `crates/lance-graph-ontology/src/odoo_blueprint/extracted/COVERAGE.md` — Leg 2's coverage report. +- `crates/lance-graph-ontology/src/odoo_blueprint/extracted/pairing.rs` — Leg 1 ↔ Leg 2 audit table. +- `CLAUDE.md` `E-SOA-IS-THE-ONLY`, `I-NOISE-FLOOR-JIRAK`, "The Click" — doctrinal anchors. +- Papers: Karabulut 2025 (arxiv 2504.19354v1) — Leg 3 anchor; Abreu 2025 (arxiv 2511.13661v1) — externalize-interpretation doctrine. + +End of survival dossier. diff --git a/.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md b/.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md new file mode 100644 index 00000000..319aed04 --- /dev/null +++ b/.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md @@ -0,0 +1,186 @@ +# discovery-origin-provenance-reconciliation-v1 — what is ACTUALLY correct vs what every source claims + +> **READ BY:** the user (who said "I don't know what is correct"); any session about to implement D-ARM-1 / discovery_origin; the council before ratifying the byte layout. +> **Status:** RECONCILIATION. Documents a conflict; does NOT resolve it in code — resolution is the user's decision. +> **Authored:** 2026-05-30. Grounded in on-disk grep with file:line citations, not memory. +> **Instruction honored:** "only document the details." Nothing here is applied to code or to the plans. This file only records, per source, what cannot be right and what the canonical answer is. + +--- + +## 0. The factual question first: "did all 70 get committed to #435?" + +**There are 66, not 70, and yes — all 66 are in #435.** Two senses, both verified: + +1. **Entity source is on the branch.** `git grep` on branch HEAD `4a6c2776` (branch `claude/activate-lance-graph-att-k2pHI`) counts **66** `pub const NAME: OdooEntity = OdooEntity` declarations across `l1.rs..l15.rs`. Committed in earlier Waves (`f5702675`, `d30186e5`, `333a1ff2`, `c04adf10`); reachable from the #435 HEAD. I did NOT re-commit them this session — they were already present. +2. **Full index of all 66 is in #435.** Commit `4a6c2776` added `odoo-blueprint-inventory-v1.md`, listing every one of the 66. + +**Why "70" was wrong:** it came from `grep -c 'OdooEntity {'`, which also matched nested struct refs. Canonical count = the **66** module-scope `pub const ... : OdooEntity`. EXT-6 COVERAGE.md says "53" because it predates the 13 Wave-3 additions (L11-L15). 53 + 13 = 66. All reconcile. **66 is correct.** + +--- + +## 1. GROUND TRUTH — what is actually in the code (the floor) + +### 1.1 The only provenance enum that EXISTS in code + +`crates/lance-graph-ontology/src/odoo_blueprint/mod.rs:450`: + +```rust +pub enum OdooConfidence { + Curated, // human-curated L-docs (D-ODOO-BP-1b) + Extracted, // Odoo source via Python ast (D-ODOO-EXT-2) + Conjecture, // inferred, not yet validated against either source +} +``` + +**Three variants: Curated, Extracted, Conjecture.** That is the entire provenance vocabulary that compiles today. No `ArmDiscovered`, no `Ratified`, no `Derived`, no `ProvenanceTier` in any `.rs` file. + +### 1.2 `discovery_origin` and `ProvenanceTier` do NOT exist in code + +`git grep -lE 'discovery_origin|ProvenanceTier' -- '*.rs'` returns nothing. Both tokens appear ONLY in seven `.claude/` files (3 board, 2 handovers, 1 knowledge, 1 plan). + +**This resolves the urgency.** The byte layout is a planning artifact; the WAL has NOT hardened around it because it is not in bytes yet. The canonical core spec's "ISA-ossification trap, live now" is real but the window is OPEN — fixing the width today costs a markdown edit, not a migration. No emergency; just a decision to get right before D-ARM-1 is written. + +--- + +## 2. THE CONFLICT MATRIX — every source, verbatim, with citations + +### 2.1 ProvenanceTier / confidence value-set, per source + +| Source | File:line | Tier values stated | Count | +|---|---|---|---| +| **CODE (authoritative for "today")** | `mod.rs:450` `OdooConfidence` | Curated, Extracted, Conjecture | **3** | +| **My ARM plan, bit layout** | `streaming-arm-nars-discovery-v1.md` §7.2 | Curated, Extracted, ArmDiscovered, Ratified | **4** | +| **My ARM plan, deliverable D-ARM-1** | same file §8 | Curated, Extracted, ArmDiscovered, Ratified, Conjecture | **5** | +| **My survival dossier** | `odoo-extraction-strategies-v1.md` §5 | order: Curated > Extracted > Ratified > ArmDiscovered > Conjecture | **5** | +| **Canonical core spec (uploaded)** | `cognitive-risc-core.md` | Curated, Extracted, ArmDiscovered, Ratified `[marked stable]` | **4** | +| **Canonical wikidata spec (uploaded)** | `wikidata-hhtl-load.md` | Curated, Extracted, Derived | **3** | + +**Union of all distinct tier names across all sources:** Curated, Extracted, Conjecture, ArmDiscovered, Ratified, Derived = **6 distinct concepts.** + +### 2.2 discovery_origin (u8) bit layout, per source + +| Source | tier bits | proposer-id bits | reserved | proposer-id capacity | +|---|---|---|---|---| +| **My ARM plan §7.2 (what is committed to #435)** | bits 0-1 (2) | **bits 2-3 (2)** = AstWalker/PairStats/Aerial/Other | bits 4-7 (4) | **4** | +| **#434 cross-session review (my held queue, NOT applied)** | bits 0-1 (2) | **bits 2-4 (3)** = 8 | bits 5-7 (3) | **8** | +| **Canonical core spec (uploaded)** | bits 0-1 (2) | **bits 2-4 (3)** = 8, 6 already named, `[marked GROWS, AT RISK]` | bits 5-7 (3) | **8, declared insufficient** | +| **Canonical core spec, its OWN recommendation** | — | **widen to 6 bits (64) or move whole byte to u16** | — | **64 or more** | + +--- + +## 3. THE ARITHMETIC — why these cannot all be right + +Two hard numeric facts settle most of the confusion: + +**Fact A — a 2-bit field holds exactly 4 values.** Every source that puts ProvenanceTier in bits 0-1 has 4 slots. But §2.1 shows the corpus names **6** distinct tiers. 6 > 4. Therefore: +- My ARM plan is internally inconsistent: §7.2 says 4 tiers (2 bits), but D-ARM-1 enumerates 5. **5 will not fit in 2 bits.** One of my own two statements is wrong. +- The code's `Conjecture` is dropped by the core spec's 4-set, and the wikidata spec's `Derived` is absent from the core spec's 4-set. So even the canonical specs, taken together, name 5 ({Curated, Extracted, ArmDiscovered, Ratified} ∪ {Derived}) and do not fit their own 2-bit field either. + +**Fact B — proposer-id capacity by width:** 2 bits = 4, 3 bits = 8, 6 bits = 64. +- The committed plan (§7.2) gives **4** proposer slots and names 4 (AstWalker/PairStats/Aerial/Other) — already full, zero headroom. +- The review/core layout gives **8** and names 6 — 2 headroom. +- The core spec explicitly argues that "business logic is just another proposer" + dual-IPC means proposers proliferate, so **8 is also insufficient** and recommends 64 (6 bits) or u16. + +**Conclusion the user can rely on:** the version currently committed to #435 (2-bit proposer-id, 4 slots, full) is the **most wrong** of all the layouts. Every later source widened it. The newest canonical source widens it furthest (to 64 or u16) and additionally exposes that the tier field is over-subscribed too. + +--- + +## 4. WHAT IS CORRECT — per the now-canonical uploaded specs + +The four uploaded `cognitive-risc-*` / `wikidata-hhtl-load` / `faiss-homology` specs are the user's blessed source of truth (they are explicitly "session-boot, invariants only, change only with a stated reason"). Per them, the correct positions are: + +1. **proposer-id width: widen to 6 bits (64 proposers) or move `discovery_origin` to `u16`.** Neither the committed 2-bit nor the reviewed 3-bit is correct. This is the headline correction. Reason (quoted from core spec): *"Widen proposer field (steal reserved -> 6 bits/64, or go u16) before surrealkv WAL hardens the LE wire format. Once it's in the byte grammar, widening = migration across every component."* + +2. **ProvenanceTier blessed set = {Curated, Extracted, ArmDiscovered, Ratified}, marked `[stable]`** in the core spec. This is the canonical 4. BUT see §6 — it omits both `Conjecture` (which is in code today) and `Derived` (which the wikidata spec uses). So "4 stable" is correct ONLY if Conjecture and Derived are handled outside this field (or the field is widened). That is an open decision, not a settled fact. + +3. **The byte does not exist in code yet**, so the correct move is to fix the width in the PLAN before any `.rs` is written. No migration is owed. + +4. **The homology is a cheat-sheet, not a dependency** (faiss-homology spec): do not pull FAISS/IVF code; the discovery_origin/provenance machinery is implemented natively. (Stated here because the specs foreground it and a future implementer might otherwise reach for FAISS.) + +--- + +## 5. THE ONE INTERNAL CONTRADICTION I SHIPPED (flagged, NOT fixed) + +In `streaming-arm-nars-discovery-v1.md` (committed to #435): + +- **§7.2** allocates ProvenanceTier to **2 bits** and enumerates **4** values (Curated/Extracted/ArmDiscovered/Ratified — no Conjecture). +- **§8, D-ARM-1** specifies the enum as **5** values: `ProvenanceTier::{Curated, Extracted, ArmDiscovered, Ratified, Conjecture}`. + +**5 values cannot be encoded in a 2-bit field.** This is a genuine bug in my own plan. I am NOT fixing it in this pass (instruction: only document). The fix is one of: (a) drop `Conjecture` from D-ARM-1 to match the 4-slot field and the core spec; (b) keep `Conjecture` and widen the tier field to 3 bits; (c) keep `Conjecture` in the code enum but exclude it from the on-wire `discovery_origin` byte (code enum and wire encoding need not be 1:1). My recommendation is (c) or (b), but the call is yours. + +--- + +## 6. THE GENUINELY OPEN DECISIONS (yours to make) + +These are NOT resolvable by citing a source — the sources disagree or are silent: + +- **OD-1 — proposer-id final width: 6 bits (64) vs `u16` discovery_origin.** Core spec offers both; does not pick. 6-bit keeps the byte at u8 (cheaper, 64 proposers); u16 gives room for both a wider tier field AND 256+ proposers. Recommendation: **u16** if you expect the tier field to also grow (see OD-2); **6-bit/u8** if tier stays at the stable 4. +- **OD-2 — fate of `Conjecture` and `Derived`.** Six concepts, four canonical slots. Options: (a) cap at the core spec's 4 and map Conjecture->one of them, treat Derived as a separate "reasoning-store provenance" field (the wikidata spec already treats Derived as orthogonal/reasoning-tier, which argues for a separate field); (b) widen tier to 3 bits and admit all 6. Recommendation: **(a)** — Derived is conceptually a different axis (logical inference) from ArmDiscovered (statistical mining) and Curated/Extracted (source); squeezing it into the same 2-bit field is the category error. +- **OD-3 — code/spec divergence on Conjecture.** Code has it; core spec dropped it. Either update the code enum or update the spec. Pick one source of truth. + +--- + +## 7. THE SECOND CONTESTED DETAIL — the Jirak noise-floor math (NOW FULLY VERIFIED against the live file) + +The other "original wrong / fix suggested / not applied / unsure what's correct" item. As of this pass it is **verified line-by-line against `streaming-arm-nars-discovery-v1.md` on disk** — no longer a hedge. + +### 7.1 The correct formula (settled — three independent confirmations) + +The Jirak (2016) weak-dependence Berry-Esseen rate is `n^{-(p/2-1)}` for `p ∈ (2, 3]`, saturating at the classical ceiling `n^{-1/2}` for `p ≥ 3`. Confirmed by: +1. **CLAUDE.md iron rule `I-NOISE-FLOOR-JIRAK`**: *"Rate: `n^(p/2-1)` for `p ∈ (2,3]`, `n^(-1/2)` in L^q for `p ≥ 4`."* (The sign is implied negative — it is a convergence rate.) +2. **The plan's OWN worked examples** (line 381): *"p=4 → `n^{-1/2}`, p=2.5 → `n^{-0.25}`."* Check: `-(2.5/2-1) = -0.25`. ✓ These examples fit `n^{-(p/2-1)}` exactly. +3. **The plan's OWN blockquote** (line 375) states the rate correctly as `n^(p/2-1)`. + +### 7.2 The bug — `n^{-1/(p/2-1)}` (reciprocal in the exponent), in 2 of the 3 places + +- **Line 381 (prose):** writes the rate as `n^{-1/(p/2-1)}`. WRONG. At p=2.5 this is `n^{-1/0.25} = n^{-4}`, which **contradicts the same line's own example `n^{-0.25}`.** +- **Line 393 (the pseudocode threshold):** `(window_size as f32).powf(-1.0 / (p_moment / 2.0 - 1.0))`. WRONG — same reciprocal bug, in code form. Correct: `.powf(-(p_moment / 2.0 - 1.0))`. +- **Line 375 (blockquote) is RIGHT.** So the plan contradicts itself internally: 375 correct, 381 + 393 wrong. + +**Magnitude of the bug at a real window:** n=100,000, p=2.5. Correct threshold `n^{-0.25} ≈ 0.056`. Buggy threshold `n^{-4} = 1e-20`. The buggy floor is effectively **zero**, so *every* candidate passes Stage A — the noise floor the rule calls "not optional" is silently disabled, producing exactly the substrate calcification `I-NOISE-FLOOR-JIRAK` exists to prevent. This is a real correctness bug, not cosmetic. + +### 7.3 The default-p problem (separate from the formula bug) + +Line 388: `p_moment: f32, // default 3.0`. With the CORRECT formula, p=3.0 gives `n^{-(3/2-1)} = n^{-1/2}` = classical Berry-Esseen exactly. So at the stated default the weak-dependence machinery yields **the same** bound as IID — the line-381 claim "much stricter than the IID `n^{-1/2}`" is false at p=3. To actually sit in the weak-dependence regime, default to **`p ≈ 2.5`** (`n^{-0.25}`, more conservative than classical). Either change the default to 2.5, or keep 3.0 and delete the "stricter than IID" claim. Recommended: **default 2.5.** + +### 7.4 The fix, exactly (for whoever applies it — NOT applied here) + +1. Line 381: `n^{-1/(p/2-1)}` -> `n^{-(p/2-1)}`; fix the garbled "p ≈ 3.0 ... giving `n^{-1}` decay" (it matches neither formula) to "p ≈ 2.5 ... giving `n^{-0.25}` decay, more conservative than the IID `n^{-1/2}`." +2. Line 393: `.powf(-1.0 / (p_moment / 2.0 - 1.0))` -> `.powf(-(p_moment / 2.0 - 1.0))`. +3. Line 388 + OQ-ARM-2: default `p_moment` 3.0 -> 2.5. +4. Leave line 375 and line 360 (D-ARM-7 spec) untouched — they are already correct. + +Not applied in this pass (instruction: document only). The fix is unambiguous and ready when you greenlight it. + +--- + +## 8. WHERE THE CANONICAL SPECS LIVE (and their own disagreement) + +The four uploaded specs now live on **this branch** at `.claude/specs/` (verified post-rebase 2026-05-30 onto `origin/main` HEAD `4b00d049`; the specs landed via main commits `d1635dbe`, `93ac0463`, `a16d0f41`, `45276eb3`): +- `.claude/specs/cognitive-risc-core.md` (v0.1) — substrate invariants; the discovery_origin layout. +- `.claude/specs/cognitive-risc-classes.md` (v0.2) — class/shape layer; restates proposer-id width as freeze-time move N2; promotes F1 to "frozen identity under live resolution" and adds N1 (`class_id`/`shape_id` column before freeze) as a paired non-deferrable. +- `.claude/specs/faiss-homology-cam-pq.md` (v0.1) — CAM/CAM_PQ; homology-is-cheat-sheet warning; "Reasoning layer = separate indexed store, Derived tier" line that argues `Derived` is a different axis from `ArmDiscovered/Ratified`. +- `.claude/specs/wikidata-hhtl-load.md` (v0.1) — load pipeline; uses the `Derived` tier name and explicitly calls reasoning "orthogonal=beside, not mixed in." + +**Correction of v1 of this doc:** the first version of this section said the specs lived only on `origin/claude/cognitive-risc-core-9PMW8`. That was true when written (pre-rebase) and became wrong when the specs were merged to main. Fixed 2026-05-30. + +**The canonical set disagrees with itself on ONE point:** the core spec's tier set is `{Curated, Extracted, ArmDiscovered, Ratified}`; the wikidata spec's is `{Curated, Extracted, Derived}`. `Derived` vs `ArmDiscovered`+`Ratified` is unreconciled IN the canonical corpus. **Council reading (2026-05-30, 4 of 4 reviewers): this is a SPEC-OWNER decision, not a Claude-session decision.** Filed to `.claude/board/ISSUES.md` as OD-1/2/3 + the canonical-self-disagreement; do NOT ship `ProvenanceTier` into code until the spec owner reconciles. The reading the faiss-homology + wikidata specs together support — `Derived` is a separate orthogonal "reasoning provenance" axis, not a tier value, so the core's stable-4 holds — is offered as the council's recommendation if the spec owner wants a default to ratify or reject. + +--- + +## 9. One-paragraph answer to "I don't know what is correct" + +The 66 entities are correct and safely in #435. The `discovery_origin` byte exists only in plans, so nothing has hardened and there is no migration debt. Of the byte-layout versions on record, the one committed to #435 (2-bit proposer-id, 4 slots, full) is the most wrong; the cross-session review widened it to 3 bits (8); the canonical core spec says even 8 is too few and to go to 6 bits (64) or u16 — that last is the correct target. The ProvenanceTier field is separately over-subscribed: six tier names exist across the corpus but only four slots, and my own plan contradicts itself (4 in §7.2, 5 in D-ARM-1) — that needs an explicit decision (recommended: keep the canonical stable 4, treat `Derived` as a separate reasoning-provenance axis, decide `Conjecture`'s fate). The Jirak formula fix direction is known (`n^{-(p/2-1)}`, default `p≈2.5`) but the exact in-file target must be re-verified against the live plan before patching. None of this is applied; all of it is documented here for your decision. + +--- + +## 10. Cross-refs + +- `odoo-blueprint-inventory-v1.md` — the 66-entity index (answers §0). +- `odoo-extraction-strategies-v1.md` §5 — my 5-tier partial order (one of the conflicting sources, §2.1). +- `.claude/plans/streaming-arm-nars-discovery-v1.md` §7.2 + §8 — the committed byte layout + the internal contradiction (§5). +- `mod.rs:450` `OdooConfidence` — code ground truth (§1.1). +- `remotes/origin/claude/cognitive-risc-core-9PMW8:.claude/specs/*` — the canonical specs (§8). +- The four uploaded specs: cognitive-risc-core/classes, faiss-homology, wikidata-hhtl-load. + +End of reconciliation. Documentation only — no code or plan was modified. diff --git a/.claude/knowledge/odoo-blueprint-inventory-v1.md b/.claude/knowledge/odoo-blueprint-inventory-v1.md new file mode 100644 index 00000000..6d427a2e --- /dev/null +++ b/.claude/knowledge/odoo-blueprint-inventory-v1.md @@ -0,0 +1,204 @@ +# odoo-blueprint-inventory-v1 — Full inventory of the 66 curated `OdooEntity` consts (D-ODOO-BP-1b corpus) + +> **READ BY:** odoo-blueprint authors, ARM-discovery proposer implementors, `style_recipe.rs` rule authors, `op_emitter.rs` consumers, the council `prior-art-savant`. +> **Status:** FINDING (corpus is on disk in main as of commit `c04adf10`; line numbers below are absolute against the lane files as of 2026-05-29). +> **Authored:** 2026-05-29 (session continuation, post PR #433 + PR #434 merges). +> **Why this exists:** session-survival index. The 66 entities are 11,563 LOC across 15 files; without this index, a fresh session must grep its way through `odoo_blueprint/l*.rs` to find anything. This doc is the index. + +--- + +## 0. Counting clarification + +**66 pub const `OdooEntity` declarations**, NOT 70. The earlier-cited "70" came from a `grep 'OdooEntity {'` literal that matched a handful of nested struct references inside `OdooStateMachine` or `OdooConstraint` bodies that themselves carry an `OdooEntity` reference. The canonical, codegen-consumed count is the **66 `pub const NAME: OdooEntity = OdooEntity {` declarations at module scope**. EXT-6's `COVERAGE.md` cites **53 curated entities** for its TIER-1 backing report — that count excludes the 13 L11-L15 Wave-3 additions that landed AFTER EXT-6 was authored on 2026-05-28. The numbers reconcile. + +Some `model_name` values appear in multiple lanes — e.g. `account.move.line` appears in L1 (the journal-entry-line in posting context) AND L2 (the reconciliation context); `account.account.tag` appears in L3 + L4 + L11 + L15 with different field/method subsets per L-doc facet. The lane is the disambiguator, not the model name. + +--- + +## 1. Per-lane summary + +| Lane | LOC | Consts | Tests | L-doc | Wave | +|---|---:|---:|---:|---|---| +| L1 | 512 | 3 | 0 (parent) | L1-K3-POST.md | Wave 1 (commit `f5702675`) | +| L2 | 577 | 3 | 3 | L2-K3-RECON.md | Wave 1 (commit `f5702675`) | +| L3 | 1,184 | 4 | 8 | L3-K7-TAX.md | Wave 1 (commit `f5702675`) | +| L4 | 645 | 6 | 8 | L4-K2-SKR-CHART.md / L4-K2-SKR-DE.md | Wave 1 (commit `f5702675`) | +| L5 | 1,136 | 5 | 8 | L5-K6-PAYMENT.md | Wave 1 (commit `f5702675`) | +| L6 | 697 | 4 | 7 | L6-K0-SALES.md / L6-K0-PURCHASE.md | Wave 2 (`d30186e5` + trims in `333a1ff2`) | +| L7 | 603 | 6 | 14 | L7-K0-STOCK.md | Wave 2 + Wave-3 trims | +| L8 | 580 | 6 | 5 | L8-K0-PRODUCT.md | Wave 2 + Wave-3 trims | +| L9 | 627 | 6 | 15 | L9-PARTNER-FISCALPOS.md | Wave 2 dedicated (`d30186e5`) — canonical FiscalPositionResolver lane | +| L10 | 760 | 5 | 4 | L10-K4-ANALYTIC.md | Wave 2 + Wave-3 trims | +| L11 | 826 | 4 | 6 | L11-CHART-LOCKDATE.md | Wave 3 (`333a1ff2`) | +| L12 | 948 | 5 | 15 | L12-MULTICOMPANY-CURRENCY.md | Wave 3 | +| L13 | 993 | 3 | 14 | L13-STOCK-VALUATION-PROCUREMENT.md | Wave 3 | +| L14 | 970 | 4 | 15 | L14-HR-BASE.md | Wave 3 | +| L15 | 505 | 2 | 8 | L15-TAX-REPARTITION.md | Wave 3 | +| **Σ** | **11,563** | **66** | **130** | — | — | + +L1's "0 tests" is correct: parent-module tests live in `odoo_blueprint::tests`, not in `l1::tests` — Wave 1 hadn't established the per-lane test convention yet. Wave 2 fixed it on L9; Wave 3 propagated. + +--- + +## 2. Per-entity index (alphabetical by const name, with cross-refs) + +Format: `const_name → model_name (kind) [lane:line] L-doc:lines — short description`. +- `ANALYTIC_ACCOUNT` → `account.analytic.account` (Model) [l10:120] L10-ANALYTIC.md:(82 119) — Leaf analytic cost-centre. Belongs to one analytic plan axis; \ +- `ANALYTIC_APPLICABILITY` → `account.analytic.applicability` (Model) [l10:256] L10-ANALYTIC.md:(423 453) — Policy rule: for a given (business_domain product_category \ +- `ANALYTIC_DISTRIBUTION_MODEL` → `account.analytic.distribution.model` (Model) [l10:541] L10-ANALYTIC.md:(368 420) — Distribution rule record: maps (partner product_category \ +- `ANALYTIC_LINE` → `account.analytic.line` (Model) [l10:357] L10-ANALYTIC.md:(456 483) — Analytic posting record created at journal-entry post time. \ +- `ANALYTIC_PLAN` → `account.analytic.plan` (Model) [l10:26] L10-ANALYTIC.md:(82 119) — Analytic plan hierarchy: one orthogonal cost-attribution axis (e.g. \ +- `ACCOUNT_ACCOUNT_TAG` → `account.account.tag` (Model) [l11:241] L11-COA-JOURNALS-LOCKDATES.md:(49 57) — Annotation tag scoped to applicability domain (accounts/taxes/products) \ +- `ACCOUNT_ACCOUNT` → `account.account` (Model) [l11:49] L11-COA-JOURNALS-LOCKDATES.md:(27 103) — Chart-of-accounts leaf record. `account_type` is a 19-value closed enum \ +- `ACCOUNT_JOURNAL` → `account.journal` (Model) [l11:345] L11-COA-JOURNALS-LOCKDATES.md:(53 103) — Journal record: type-classified posting book (sale/purchase/cash/bank/ \ +- `RES_COMPANY_LOCK_DATE` → `res.company` (Model) [l11:528] L11-COA-JOURNALS-LOCKDATES.md:(69 103) — Lock-date extension on res.company (L11 scope: fields R11-R19 only). \ +- `ACCOUNT_ACCOUNT_EXCHANGE` → `account.account` (Model) [l12:671] L12-MULTICOMPANY-CURRENCY.md:(79 81) — General ledger account in its FX exchange gain/loss role: \ +- `RES_COMPANY_MULTICOMPANY` → `res.company` (Model) [l12:439] L12-MULTICOMPANY-CURRENCY.md:(51 81) — Company / branch node in the multi-company tree (res.company _parent_store). \ +- `RES_CURRENCY_RATE` → `res.currency.rate` (Model) [l12:338] L12-MULTICOMPANY-CURRENCY.md:(31 49) — One dated FX exchange rate: foreign units per 1 base currency (technical `rate`). \ +- `RES_CURRENCY` → `res.currency` (Model) [l12:71] L12-MULTICOMPANY-CURRENCY.md:(25 82) — ISO 4217 currency: defines rounding precision (decimal_places derived from \ +- `RES_USERS_COMPANY_ACCESS` → `res.users` (Model) [l12:598] L12-MULTICOMPANY-CURRENCY.md:(54 56) — User model: company_id (active company) and allowed_company_ids \ +- `STOCK_LOT` → `stock.lot` (Model) [l13:602] L13-STOCK-VALUATION-PROCUREMENT.md:(71 75) — Lot/serial number master; uniqueness per (product_id company_id name) \ +- `STOCK_RULE` → `stock.rule` (Model) [l13:416] L13-STOCK-VALUATION-PROCUREMENT.md:(38 82) — Procurement rule mapping (dest route) → action (pull/push/transparent); \ +- `STOCK_WAREHOUSE_ORDERPOINT` → `stock.warehouse.orderpoint` (Model) [l13:186] L13-STOCK-VALUATION-PROCUREMENT.md:(55 79) — Min/max reorder rule for one product at one location; drives scheduler batch \ +- `HR_CONTRACT_TYPE` → `hr.contract.type` (Model) [l14:722] L14-HR-BASE.md:(68 81) — Employment contract type (e.g. CDI / CDD / interim); community stub with \ +- `HR_DEPARTMENT` → `hr.department` (Model) [l14:431] L14-HR-BASE.md:(41 78) — Organisational unit in the company tree; recursive parent hierarchy \ +- `HR_EMPLOYEE` → `hr.employee` (Model) [l14:62] L14-HR-BASE.md:(29 81) — Work-resource individual with versioned employment terms (hr.version chain); \ +- `HR_JOB` → `hr.job` (Model) [l14:588] L14-HR-BASE.md:(47 49) — Job role / position within a department; tracks active headcount \ +- `ACCOUNT_ACCOUNT_TAG_L15` → `account.account.tag` (Model) [l15:159] L15-TAX-REPARTITION.md:(529 545) — L15 ext: balance_negate + report_expression_id on account.account.tag. \ +- `ACCOUNT_TAX_L15` → `account.tax` (Model) [l15:321] L15-TAX-REPARTITION.md:(64 614) — L15 ext: CABA gate + rounding engine + totals. Core in L3. Adds \ +- `ACCOUNT_JOURNAL` → `account.journal` (Model) [l1:458] L1-K3-POST.md:(488 562) — Accounting journal; owns Belegnummer sequence format optional \ +- `ACCOUNT_MOVE_LINE` → `account.move.line` (Model) [l1:266] L1-K3-POST.md:(234 362) — Journal entry line; balance/debit/credit in company currency \ +- `ACCOUNT_MOVE` → `account.move` (Model) [l1:51] L1-K3-POST.md:(27 743) — Double-entry journal entry / invoice; draft→posted→cancel state machine \ +- `ACCOUNT_FULL_RECONCILE` → `account.full.reconcile` (Model) [l2:461] L2-K3-RECON.md:(530 567) — Completed settlement event — groups all partials and AMLs that together \ +- `ACCOUNT_MOVE_LINE` → `account.move.line` (Model) [l2:42] L2-K3-RECON.md:(27 466) — Journal entry line — the debit/credit leaf of a double-entry move; \ +- `ACCOUNT_PARTIAL_RECONCILE` → `account.partial.reconcile` (Model) [l2:252] L2-K3-RECON.md:(468 617) — Partial settlement event — one debit/credit AML pair matched for a \ +- `ACCOUNT_FISCAL_POSITION` → `account.fiscal.position` (Model) [l3:897] L3-K7-TAX.md:(661 800) — Tax regime mapping rule: translates taxes and GL accounts for a partner. \ +- `ACCOUNT_TAX_GROUP` → `account.tax.group` (Model) [l3:111] L3-K7-TAX.md:(624 658) — Groups taxes for display + closing-entry accounts (USt/VSt/advance); \ +- `ACCOUNT_TAX_REPARTITION_LINE` → `account.tax.repartition.line` (Model) [l3:626] L3-K7-TAX.md:(573 620) — Distribution rule mapping a tax computation result to a GL account and \ +- `ACCOUNT_TAX` → `account.tax` (Model) [l3:467] L3-K7-TAX.md:(805 850) — VAT / USt tax definition with computation type (percent/fixed/division/group) \ +- `ACCOUNT_ACCOUNT_DE` → `account.account` (Model) [l4:130] L4-K8K9-REPORTS-DATEV.md:(296 370) — General ledger account extended by l10n_de: blocks code changes \ +- `ACCOUNT_ACCOUNT_TAG` → `account.account.tag` (Model) [l4:40] L4-K8K9-REPORTS-DATEV.md:(42 295) — SKOS-style classification label applied to accounts or taxes; \ +- `ACCOUNT_JOURNAL_DE` → `account.journal` (Model) [l4:472] L4-K8K9-REPORTS-DATEV.md:(363 367) — Journal model extended by l10n_de: bank/cash liquidity accounts \ +- `ACCOUNT_TAX_DATEV` → `account.tax` (Model) [l4:212] L4-K8K9-REPORTS-DATEV.md:(368 451) — Tax record extended for DATEV export: carries a 4-character \ +- `PRODUCT_TEMPLATE_DE` → `product.template` (Model) [l4:290] L4-K8K9-REPORTS-DATEV.md:(376 422) — Product template extended by l10n_de: _get_product_accounts \ +- `RES_COMPANY_DE` → `res.company` (Model) [l4:363] L4-K8K9-REPORTS-DATEV.md:(330 367) — Company record augmented by l10n_de chart-template setup: \ +- `PAYMENT_TERM_LINE` → `account.payment.term.line` (Model) [l5:553] L5-PAY-TERMS-MATCH.md:(296 330) — One installment line within a payment term; computes due date via \ +- `PAYMENT_TERM` → `account.payment.term` (Model) [l5:392] L5-PAY-TERMS-MATCH.md:(193 341) — Structured payment obligation terms (installments Skonto/early-discount \ +- `PAYMENT` → `account.payment` (Model) [l5:87] L5-PAY-TERMS-MATCH.md:(32 191) — A posted payment event generating double-entry journal lines; \ +- `RECONCILE_MODEL_LINE` → `account.reconcile.model.line` (Model) [l5:895] L5-PAY-TERMS-MATCH.md:(456 468) — Write-off journal line template within a reconcile model; \ +- `RECONCILE_MODEL` → `account.reconcile.model` (Model) [l5:686] L5-PAY-TERMS-MATCH.md:(399 563) — Declarative rule for bank-statement-to-open-item matching \ +- `PURCHASE_ORDER_LINE` → `purchase.order.line` (Model) [l6:529] L6-SALE-PURCHASE.md:(611 643) — One line on a purchase order; qty_to_invoice feeds the order-level \ +- `PURCHASE_ORDER` → `purchase.order` (Model) [l6:410] L6-SALE-PURCHASE.md:(546 643) — Vendor purchase order (RFQ→PO); optional two-step approval via \ +- `SALE_ORDER_LINE` → `sale.order.line` (Model) [l6:216] L6-SALE-PURCHASE.md:(186 743) — One line on a sale order; tracks price_unit/discount/tax_ids and \ +- `SALE_ORDER` → `sale.order` (Model) [l6:75] L6-SALE-PURCHASE.md:(31 543) — Commercial sale quotation/order (Vorgang lifecycle: draft→sent→sale→cancel); \ +- `STOCK_LOCATION` → `stock.location` (Model) [l7:355] L7-STOCK.md:(155 207) — Node in the stock location hierarchy (physical or virtual); \ +- `STOCK_MOVE_LINE` → `stock.move.line` (Model) [l7:132] L7-STOCK.md:(77 108) — One lot/package/owner reservation or done-qty record within a stock move; \ +- `STOCK_MOVE` → `stock.move` (Model) [l7:56] L7-STOCK.md:(31 108) — One product movement between two stock locations; state machine \ +- `STOCK_PICKING` → `stock.picking` (Model) [l7:273] L7-STOCK.md:(419 500) — Group of stock moves for one logistics operation (receipt/delivery/internal); \ +- `STOCK_QUANT` → `stock.quant` (Model) [l7:173] L7-STOCK.md:(110 415) — Persistent stock record: qty of one product at one location with \ +- `STOCK_WAREHOUSE` → `stock.warehouse` (Model) [l7:421] L7-STOCK.md:(608 644) — Physical warehouse site with operational config (picking types routes \ +- `PRODUCT_CATEGORY` → `product.category` (Model) [l8:44] L8-PRODUCT-UOM-PRICELIST.md:(180 238) — Hierarchical product category; parent_path materialized closure used in \ +- `PRODUCT_PRICELIST_ITEM` → `product.pricelist.item` (Model) [l8:415] L8-PRODUCT-UOM-PRICELIST.md:(263 660) — Pricelist rule: applied_on scope min_quantity (product UoM) date validity. \ +- `PRODUCT_PRICELIST` → `product.pricelist` (Model) [l8:359] L8-PRODUCT-UOM-PRICELIST.md:(242 412) — Pricelist: currency + company + country-group scoping + ordered rule set. \ +- `PRODUCT_PRODUCT` → `product.product` (Model) [l8:282] L8-PRODUCT-UOM-PRICELIST.md:(62 115) — Product variant: _inherits product.template. lst_price = list_price + \ +- `PRODUCT_TEMPLATE` → `product.template` (Model) [l8:167] L8-PRODUCT-UOM-PRICELIST.md:(42 115) — Product template: canonical catalog record. Drives variant matrix via \ +- `UOM_UOM` → `uom.uom` (Model) [l8:103] L8-PRODUCT-UOM-PRICELIST.md:(119 177) — Unit of Measure: factor-based conversion. Reference unit factor=1.0. \ +- `ACCOUNT_FISCAL_POSITION` → `account.fiscal.position` (Model) [l9:38] L9-PARTNER-FISCALPOS.md:(207 413) — Named tax-and-account mapping applied to a partner; selected by \ +- `ACCOUNT_FISCAL_POS_ACCOUNT` → `account.fiscal.position.account` (Model) [l9:173] L9-PARTNER-FISCALPOS.md:(348 373) — One account-substitution rule inside a fiscal position: \ +- `ACCOUNT_PAYMENT_TERM_REF` → `account.payment.term` (Model) [l9:445] L9-PARTNER-FISCALPOS.md:(59 78) — Payment terms (L5 authoritative); projected in L9 only as target of \ +- `RES_COUNTRY_GROUP` → `res.country.group` (Model) [l9:403] L9-PARTNER-FISCALPOS.md:(263 277) — Named set of countries for fpos matching predicate 5: \ +- `RES_COUNTRY` → `res.country` (Model) [l9:363] L9-PARTNER-FISCALPOS.md:(207 291) — Country; 2-char ISO code used in _get_fiscal_position to compute \ +- `RES_PARTNER_ACCOUNTING` → `res.partner` (Model) [l9:229] L9-PARTNER-FISCALPOS.md:(32 513) — Partner extended by account module: AR/AP property accounts payment terms \ + +--- + +## 3. Per-lane semantic role + EXT-2 coverage + +| Lane | Semantic role | EXT-2 backing (`extracted/`) | Coverage (EXT-6) | +|---|---|---|---| +| L1 | Journal posting (`account.move` lifecycle) | `account.rs` | 100% | +| L2 | Reconciliation (partial/full settlement of AMLs) | `account.rs` (shared) | 100% | +| L3 | Tax: groups + rates + repartition + fiscal position | `account.rs` (shared) | 100% | +| L4 | German SKR03/SKR04 chart + DATEV-tagged taxes | `l10n_de.rs` + `l10n_de_chart.rs` (24,712 LOC SKR03/04 + UStVA Kz) | 100% | +| L5 | Payments + payment terms + reconcile-model auto-matching | `account_payment.rs` | 100% | +| L6 | Sales + Purchase order chain (header + lines) | `sale.rs` + `purchase.rs` | 100% | +| L7 | Stock movement (move/quant/picking/location/warehouse) | `stock.rs` (12,020 LOC) | 100% | +| L8 | Product catalogue + UoM + pricelist | `product.rs` + `uom.rs` | 100% | +| L9 | Fiscal-position resolver — canonical worked example | `account.rs` (shared) | 100% | +| L10 | Analytic accounting plan + line + distribution | `analytic.rs` | 100% | +| L11 | Account chart + journal + company lock-date hardening | `account.rs` (shared) | 90.6% (5 explicit TIER-2 deferrals workspace-wide) | +| L12 | Multi-company + multi-currency exchange | _no dedicated file; account/base shared_ | (Wave-3, post EXT-6) | +| L13 | Stock valuation layer + procurement (orderpoint/rule/lot) | `stock.rs` (shared) | (Wave-3, post EXT-6) | +| L14 | HR base (employee/department/job/contract-type) | _none_ — HR is TIER-2 deferral per `odoo-source-extraction-v1.md` | (deferred) | +| L15 | Tax-repartition extensions to L3 (CABA + balance_negate) | `account.rs` (shared) | (Wave-3 extension, post EXT-6) | + +**EXT-2 backing files (`extracted/`):** 99,209 LOC across 11 `.rs` files; coverage gate test in `extracted/coverage.rs`. Curated stays canonical on conflict per `pairing.rs::CURATED_EXTRACTED_PAIRS`. + +--- + +## 4. Field / method / state-machine density (where the work is) + +Approximate counts via `grep -cE '^ kind: Odoo<...>Kind::'` per lane: + +| Lane | Field-kind hits | Method-kind hits | Decorator-kind hits | State-machine present | +|---|---:|---:|---:|---:| +| L1 | 52 | 41 | 8 | (move state) | +| L2 | 21 | 20 | 4 | (recon state) | +| L3 | 0* | 0* | 0* | — | +| L4 | 21 | 3 | 0 | — | +| L5 | 55 | 26 | 11 | (payment state) | +| L6 | 0* | 0* | 0* | (sale state) | +| L7 | 11 | 0* | 0* | (picking state) | +| L8 | 0* | 0* | 6 | — | +| L9 | 0* | 0* | 0* | — | +| L10 | 36 | 16 | 2 | — | +| L11 | 28 | 16 | 4 | (lock state) | +| L12 | 29 | 15 | 2 | — | +| L13 | 44 | 18 | 3 | — | +| L14 | 39 | 12 | 6 | — | +| L15 | 0* | 0* | 0* | — | + +`*` = lane carries fields/methods but not with the exact `kind: OdooFieldKind::X` indentation pattern the grep expects (different formatting choices across Wave-1/2/3 agents — usually because the entry references a shared sub-const declared elsewhere in the file or inherits via reference). The 0* cells are **NOT empty entities**; verify via `wc -l l.rs` and the const-content blocks. + +State-machine presence audit needs a follow-up pass: `grep -E '^&OdooStateMachine \{|state_machine: Some\(&' l*.rs` came back 0 because of multi-line formatting, but L1 (move), L2 (recon), L5 (payment), L6 (sale order), L7 (picking), and L11 (lock_date) all carry state semantics per their L-doc role. The state machines may be inlined or referenced from a sibling const; this is a known gap to clean up in a follow-up. + +--- + +## 5. Wave provenance summary + +| Wave | Date | Commit | Agents | Lanes | Entities added | +|---|---|---|---:|---|---:| +| Scaffold | 2026-05-28 | `0c0ad4d3` | (mechanical) | l1..l15 stubs created | 0 (stubs) | +| Wave 1 | 2026-05-28 | `f5702675` | **5 Sonnet agents** (1 per lane) | L1, L2, L3, L4, L5 | 21 (= 3+3+4+6+5) | +| Wave 2 dedicated | 2026-05-28 | `d30186e5` | 1 Sonnet agent | L9 (FiscalPositionResolver canonical) | 6 | +| Wave 2 + Wave-3 trims | 2026-05-28 | `333a1ff2` | 5 Sonnet agents (Wave-3) + reviewer | L6, L7, L8, L10 (Wave-2 work folded in) + L11, L12, L13, L14, L15 | 39 (= 4+6+6+5 + 4+5+3+4+2) | +| EXT-3 follow-up | 2026-05-28 | `c04adf10` | Main thread | `OdooEntityKind` + `regulation_iri` back-fill across all 66 entities | 0 (additive) | + +**The "5 agents" of the question** = Wave 1 specifically — 5 Sonnet agents fanned out one-per-lane for L1-L5, each agent producing its lane's `OdooEntity` consts via mechanical L-doc-prose-to-typed-projection. The same fan-out pattern was used in Wave 3 (L11-L15) but with 5 agents working in a different lane band; the Wave-2 work (L6-L10) was done by ~3 agents with the canonical L9 carved out as its own commit. + +--- + +## 6. What's NOT in this corpus (and where it would land) + +| Missing | Why | Landing site if it ships | +|---|---|---| +| Cron-method coverage | No `OdooMethodKind::Cron` entries today; Odoo crons live in XML data, not Python AST | Extractor enrichment (`tools/odoo-blueprint-extractor/parsers/methods.py`) | +| Multi-company access rules | L12 covers res.users.company_access; full record-rule corpus not included | Future BP-1c hop (OGIT classifier extension) | +| Asset depreciation models (`account.asset.*`) | TIER-2 addon — explicitly deferred per `odoo-source-extraction-v1.md` | Stage 2 (TIER-2 addons) — EXT-2 follow-up | +| Manufacturing (`mrp.*`), POS (`pos.*`), Project (`project.*`) | TIER-2 addons | Stage 2 | +| Stage-2 dark atoms (Money/Quantity/ApplyRate/EmitAmount/Event/FiscalCtx) | Not lit because `return_kind` + `semantic_role` + `computed` fields are sparse in the curated set | Extractor enrichment OR ARM-discovery proposer (see `streaming-arm-nars-discovery-v1.md`) | + +--- + +## 7. Cross-refs + +- `style_recipe.rs` (PR #433) consumes these 66 entities + their `OdooMethod` slices to emit `OdooStyleRecipe` per method. +- `op_emitter.rs` (this PR #435 branch) groups `OdooStyleRecipe` corpus by `OdooMethodKind` bucket and emits compilable Rust dispatch. +- `extracted/` (D-ODOO-EXT-2 shipped 2026-05-28) provides 48/53 TIER-1 backing per `COVERAGE.md`. +- `tools/odoo-blueprint-extractor/` (D-ODOO-EXT-1 shipped) is the Python AST walker that produces the backing. +- `pairing.rs::CURATED_EXTRACTED_PAIRS` enforces canonical-on-conflict between this corpus and the EXT-2 output. +- ARM-discovery (this branch, PR #435 plan) is the **third proposer leg** that complements this corpus with runtime-data-derived candidates. +- See `odoo-extraction-strategies-v1.md` for the doctrine of three proposer legs. +- See `odoo-extraction-tools-v1.md` for the tool stacks behind each leg. + +End of inventory. diff --git a/.claude/knowledge/odoo-extraction-strategies-v1.md b/.claude/knowledge/odoo-extraction-strategies-v1.md new file mode 100644 index 00000000..74d57df5 --- /dev/null +++ b/.claude/knowledge/odoo-extraction-strategies-v1.md @@ -0,0 +1,210 @@ +# odoo-extraction-strategies-v1 — The three proposer legs into the Odoo SPO substrate + +> **READ BY:** anyone proposing a new extractor / discoverer / proposer for Odoo entities; the council `prior-art-savant`; `style_recipe.rs` rule authors deciding which `OdooConfidence` levels their rules can read from. +> **Status:** FINDING (the three legs exist or are spec'd; this doc names the doctrine). +> **Authored:** 2026-05-29. +> **Companion docs:** `odoo-blueprint-inventory-v1.md` (the 66-entity corpus), `odoo-extraction-tools-v1.md` (what backs each leg). + +--- + +## 0. The doctrine in one paragraph + +Odoo business logic enters the SPO substrate through **exactly three proposer legs**: (1) **Curated** — humans project L-doc prose into typed `OdooEntity` consts; (2) **Extracted** — Python AST walks `/home/user/odoo/addons/*` source and emits candidate consts; (3) **ArmDiscovered** — streaming pair-stats (and optionally Aerial+ neural-symbolic) mine runtime tabular data for co-correlations expressible as truth-carrying SPO triples. Each leg has its own throughput regime, confidence basin, and council-gate posture. The three legs converge on the **same typed `OdooEntity` SoA + same `SpoStore` substrate + same `TruthValue (f, c)` calibration** — they DIFFER in what they can see, not in what they produce. Downstream consumers (`derive_style_recipe`, `op_emitter`, the cognitive-shader-driver) read from the union; the council ratification gate is what lets candidates from a noisier leg become equally first-class in the codegen path. + +--- + +## 1. Leg 1 — Curated (D-ODOO-BP-1b) + +### What it sees + +Human-authored L-doc prose in `.claude/odoo/L*.md` — 15 L-docs covering Odoo's 15 business lanes (posting, reconciliation, tax, SKR chart, payment, sales/purchase, stock, product, partner-fiscalpos, analytic, lock-date, multi-company-currency, stock-valuation, HR, tax-repartition). Each L-doc carries line-ranged prose describing what an Odoo entity IS in business terms — its fields' meanings, its methods' intentions, its state machine's semantics, its regulatory anchors (UStG / HGB / GoBD / AO / EN 16931). + +### What it emits + +66 `pub const NAME: OdooEntity` declarations across `crates/lance-graph-ontology/src/odoo_blueprint/l{1..15}.rs`, totaling 11,563 LOC. Each const carries `confidence: OdooConfidence::Curated`, line-ranged `OdooProvenance::l_doc_lines` citation back to the L-doc, and (where applicable) `regulation_iri: &[...]` pointing into the OGIT-inherited regulation codebook. + +### Method + +Mechanical L-doc-prose → typed-const projection. One Sonnet agent per lane (5 agents in parallel for L1-L5 = Wave 1; 5 agents for L11-L15 = Wave 3; the Wave-2 L6-L10 band ran ~3 agents with L9 carved out for the canonical FiscalPositionResolver projection). Each agent's deliverable is *one lane's* `OdooEntity` consts. + +**Per-agent algorithm:** +1. Read the lane's L-doc (one of `.claude/odoo/L{1..15}-*.md`). +2. Read `odoo_blueprint/mod.rs` to bind the `OdooEntity` typed surface (fields, methods, decorators, state machine, constraints, provenance). +3. For each Odoo entity the L-doc names in prose: + - Map its prose fields to `OdooField { name, kind: OdooFieldKind::*, target, required, computed, depends, semantic_role: OdooSemanticRole::* }` — semantic_role is the human's read of the field's business meaning (Identity / Reference / Quantity / Date / Policy / Status / Money / Document / Address / Tax / Audit / Other). + - Map its prose methods to `OdooMethod { name, kind: OdooMethodKind::*, return_kind: OdooReturnKind::*, triggers: &[...] }`. + - Map its `@api.*` decorators to `OdooDecorator { kind, targets }`. + - Map its state machine (if present) to `OdooStateMachine { state_field, states, transitions }`. + - Map its `_sql_constraints` and `@api.constrains` clauses to `OdooConstraint`. + - Stamp `OdooProvenance { l_doc, l_doc_lines: (start, end), odoo_source: &[OdooSourceRef { path, line_range }], confidence: Curated, regulation_iri: &[...] }`. +4. Write the lane's `tests` module with a few smoke tests (typically: assert entities const exists, assert a specific field's kind, assert a specific method's return kind). + +### Throughput / confidence + +- **Throughput:** 3-6 entities per agent-session (~1 hour wall-clock at human-supervised Sonnet pace). 5 agents in parallel = ~30 entities per Wave. +- **Confidence:** HIGH. The L-doc is the human-filtered semantic surface; projection is mechanical; reviewer trims (Wave-2 → Wave-3 transition added these) catch the few edge cases. +- **Failure modes:** L-doc prose may itself be wrong or out of date relative to live Odoo source — `pairing.rs::CURATED_EXTRACTED_PAIRS` is the audit table that surfaces these conflicts. **Canonical rule: curated wins on conflict, but the conflict is logged for human ratification.** + +### Council posture + +`OdooConfidence::Curated` is the **highest-trust tier**; council ratification is NOT required for Curated entries. Per PR #433's council-bypass exemption, human-authored rulings ship without a council pass. (The §11 rulings of PR #434 were author-stated under the same exemption; the same applies here.) + +--- + +## 2. Leg 2 — Extracted (D-ODOO-EXT-2) + +### What it sees + +Python source in `/home/user/odoo/addons/{account, account_payment, l10n_de, product, stock, uom, base, analytic, purchase, sale, account_peppol, account_edi_ubl_cii}/` — the 12 TIER-1 addons covering German accounting flow + ORM roots + value chain + e-invoice. TIER-2 addons (POS, HR, website, payment providers, non-DE l10n, asset depreciation, MRP, project) are explicitly deferred. + +### What it emits + +11 `.rs` files in `crates/lance-graph-ontology/src/odoo_blueprint/extracted/{account, account_edi_ubl_cii, account_payment, account_peppol, analytic, base, l10n_de, l10n_de_chart, l10n_de_kennzahlen, pairing, product, purchase, sale, stock, uom}.rs`, totaling **99,209 LOC** — the bulk of which is `l10n_de_chart.rs` (24,712 LOC of SKR03/SKR04 chart-of-accounts data). Each emitted const carries `confidence: OdooConfidence::Extracted` and a `EXT_` symbol-prefix to avoid collision with the curated lane consts. + +### Method + +Stdlib-only Python 3 (`ast` module) — `tools/odoo-blueprint-extractor/`. **No tree-sitter, no Odoo runtime, no network.** Pure static analysis on the on-disk Python source. Per-addon walker: + +1. `os.walk` the addon's `models/` directory. +2. For each `.py` file, parse the AST via `ast.parse(source)`. +3. For each `class X(models.Model | models.TransientModel | models.AbstractModel)`: + - Bind the `_name`/`_inherit` attribute to the `model_name`. + - Bind the base class to `OdooEntityKind::{Model, Transient, Abstract}`. + - Walk class body for field assignments (`name = fields.Char(...)`) → `OdooField` entries. + - Walk class body for method definitions; classify by name prefix (`_compute_`, `_inverse_`, `_check_`, `_onchange_`, `action_`, `_cron_`) + decorators → `OdooMethod` entries. + - Walk class body for decorators on methods (`@api.depends`, `@api.constrains`, `@api.onchange`, `@api.model`, `@api.model_create_multi`) → `OdooDecorator` entries. + - Walk class body for state-field selections + transition methods → `OdooStateMachine` (best-effort; many transitions are implicit in `write({'state': ...})` calls and may need hand-trim). + - Bind `_sql_constraints` and `@api.constrains` methods to `OdooConstraint` entries. +4. Emit Rust source via the `emitters/` module: one `pub const EXT_: OdooEntity = OdooEntity {...}` per class. +5. Log any field-kind fallbacks (`fields.Image`, `fields.Properties`, etc. that aren't in `OdooFieldKind`) to `--audit /tmp/fallback.json` for human review. + +The CLI is `python -m odoo_blueprint_extractor --addons /home/user/odoo/addons --addon --out [--audit ]`. + +### Throughput / confidence + +- **Throughput:** ~10s entities per addon-run (≤1 sec per file; full TIER-1 sweep is ~minutes). +- **Confidence:** MED. AST sees what's *literally written*; misses runtime behavior (dynamic `_inherit`, registry-injected methods, computed-field side-channels). Stage-2 dark-atom gap is here: `return_kind` and `semantic_role` are conservatively inferred and often default to `Unit` / `Other` — see `odoo-blueprint-inventory-v1.md` §6. +- **Failure modes:** field-kind fallback rate is the canonical metric (gated `< 5%` per EXT-6). Methods with raise-but-no-classifier-match get `OdooMethodKind::Other`. The `OdooConfidence::Extracted` stamp tells consumers to expect this noise. + +### Council posture + +`OdooConfidence::Extracted` is **second tier**; ratification is NOT required for routine extracted entries, but conflicts with the curated set surface in `pairing.rs::CURATED_EXTRACTED_PAIRS` and are flagged for human ratification (NOT automatic council convening — `pairing.rs` is a manual review surface). Coverage gate (EXT-6, `extracted::coverage`) enforces 80% per-lane backing; below that, the lane fails CI. + +### Coverage today (per EXT-6 report 2026-05-28) + +- 48/53 curated entities have TIER-1 extracted backing (90.6% workspace-wide). +- L1-L10 all at 100% coverage; L11 at 90.6% with 5 explicit TIER-2 deferrals. +- L12-L15 (Wave-3 curated additions) are post-EXT-6 and need a fresh extractor pass to bring into the pairing table. +- 5 TIER-2 deferrals: 4 `hr.*` (HR-base) + 1 `stock.valuation.layer` (deferred to TIER-2 stock-account follow-up). + +--- + +## 3. Leg 3 — ArmDiscovered (this branch, `streaming-arm-nars-discovery-v1.md`) + +### What it sees + +Streaming runtime tabular data — parquet exports of `account.move`, `account.move.line`, `stock.move`, `sale.order`, etc. — at 20K–200K rows per window. **NOT static source; NOT human prose.** The actual transaction history of the running Odoo instance. + +### What it emits + +Candidate SPO triples `(subject, predicate, object, frequency, confidence)` produced by: + +1. **Stage A — Streaming pair-stats (deterministic trunk).** Per-window sufficient statistics over `(item_i, item_j)` pairs up to antecedent bound `a=2`. Memory bound `O(k²) ≈ 160 KB` at k=200 features. SIMD-amenable; ~2 s per 200K-row window on one core. Outputs `CandidateRule { antecedent, consequent, support, confidence, n }`. +2. **Stage A2 — Optional Aerial+ IPC fan-in.** For high-dimensional sparse feeds (k > 500), a separate Python process running the Aerial+ autoencoder (per Karabulut et al. 2025 §3.3, Algorithm 1) emits the same `CandidateRule` shape via NDJSON-over-Unix-socket. Behind the `arm-aerial` feature flag. +3. **Stage B — Translator.** Pure function `arm_to_nars(rule) -> TruthValue` maps ARM `confidence → NARS frequency f`, `(support × n) / (support × n + k) → NARS confidence c`. The output is a `CandidateTriple { s, p, o, truth, origin: ProvenanceTier::ArmDiscovered }`. +4. **Stage C — Hypothesis test.** Round-trip against `SpoStore`: revise the prior on match (NARS `revise(t_prior, candidate)`), commit a `Contradiction` edge on inversion match (per The Click), queue for ratification on novel. +5. **Stage D — Ratification.** The 5-savant epiphany-brainstorm-council vets novel candidates before they earn `ProvenanceTier::Ratified` and become consumable by `op_emitter`. + +### Method + +See `.claude/plans/streaming-arm-nars-discovery-v1.md` for the full 18-section spec. The crate `lance-graph-arm-discovery` is **queued, not yet implemented** (D-ARM-1 through D-ARM-12). + +### Throughput / confidence + +- **Throughput:** 20K-200K rows per window, continuous; theoretical 100K rows/window/sec sustainable on a 16-core machine for k=200 features (target per D-ARM-12 bench). +- **Confidence:** LOW until Stage D ratification (`ProvenanceTier::ArmDiscovered`); HIGH after (`ProvenanceTier::Ratified`). The Jirak-bounded threshold (`I-NOISE-FLOOR-JIRAK`) is the noise-floor gate at Stage A; without it, low-quality candidates would calcify into the substrate via NARS revision. With it, false-positive rate aligns with substrate's noise floor. +- **Failure modes:** weak-dependence violations (Stage A leaks below Jirak floor → calcification), council bottleneck (Stage D throughput is human-bounded), contradiction-commit semantic drift (Stage C requires `spo::truth::Contradiction` primitive to exist — verify at D-ARM-5 entry). + +### Council posture + +**MANDATORY ratification before codegen.** This is the only leg where Stage D ratification is non-bypassable. `op_emitter::bucket_corpus` filters candidates by `confidence ≥ Ratified` (D-ARM-10); ArmDiscovered candidates that haven't passed the council never reach the deterministic codegen path. **The council ratification gate IS the determinism firewall** — it's the only non-deterministic-to-deterministic transition in the pipeline. + +--- + +## 4. Convergence — how the three legs feed the same substrate + +``` + ┌── Leg 1 (Curated, BP-1b) ────► OdooEntity consts (66) ─────────┐ + │ l{1..15}.rs, 11,563 LOC, Sonnet-agent projection │ + │ Stamps: OdooConfidence::Curated, regulation_iri │ + │ ▼ + │ ┌───────────────────┐ + ├── Leg 2 (Extracted, EXT-2) ──► EXT_ consts (≈48) ──┤ Typed OdooEntity │ + │ extracted/.rs, 99,209 LOC, Python AST walker │ SoA │ + │ Stamps: OdooConfidence::Extracted, audit fallback log │ (the common │ + │ │ union) │ + │ │ │ + └── Leg 3 (ArmDiscovered, ARM-disc) ──► CandidateTriple ──►│ │ + parquet/stream, pair-stats + Aerial+ fan-in │ │ + Stamps: ProvenanceTier::ArmDiscovered └─────────┬─────────┘ + Must pass council (Stage D) → Ratified │ + ▼ + ┌───────────────┐ + │ SpoStore + │ + │ EdgeColumn │ + │ + mailbox SoA │ + └───────┬───────┘ + │ + ▼ + derive_style_recipe + + op_emitter + + codegen +``` + +The three legs are **not redundant** — they see different things: + +- **Curated** sees what *humans documented as semantically important*. High signal, low coverage. +- **Extracted** sees what *the source code literally states*. Medium signal, high coverage (TIER-1). +- **ArmDiscovered** sees what *runtime data confirms or contradicts*. Variable signal, runtime-dependent coverage. + +A complete picture requires all three. The `pairing.rs::CURATED_EXTRACTED_PAIRS` table is the audit surface between Legs 1 and 2; the council ratification queue is the audit surface between Leg 3 and Legs 1+2. + +--- + +## 5. Provenance ordering (for downstream consumers) + +The `OdooConfidence` (a.k.a. `ProvenanceTier`) enum carries a partial order that downstream consumers respect: + +```text +Curated > Extracted > Ratified > ArmDiscovered > Conjecture +``` + +- `Curated` and `Extracted` exist on disk today. +- `Ratified` is proposed by D-ARM-1 (new variant); applies to ArmDiscovered candidates that passed council. +- `ArmDiscovered` is proposed by D-ARM-1; applies to candidates emitted by Stage B but not yet ratified. +- `Conjecture` exists on disk today; applies to entries that are neither curated nor source-extracted nor data-discovered — placeholders awaiting one of the three. + +`op_emitter::bucket_corpus` reads `confidence ≥ Ratified` (cuts at the council gate); `derive_style_recipe` reads any confidence (the recipe is just an interpretation projection; truth flows through `OdooStyleRecipe::regulation_iris` only for the Curated tier today). + +--- + +## 6. What this doctrine forbids + +- **Auto-promotion of ArmDiscovered to Ratified.** The council gate is non-negotiable. Per The Click: novel candidates are the Epiphany branch, not the Commit branch. +- **Silent override of Curated by Extracted.** Conflicts surface through `pairing.rs::CURATED_EXTRACTED_PAIRS` for human ratification, never auto-resolved. +- **A fourth proposer leg without a ProvenanceTier slot.** New proposers (LLM-based, dual-IPC variants) get a new ProvenanceTier variant + a `discovery_origin` byte slot (proposed in PR #435 §7) — they do NOT silently inject into an existing tier. +- **Bypassing the SpoStore.** All three legs write through `SpoBuilder::build_edge` only. No parallel triple store, no shadow registry. Per `E-SOA-IS-THE-ONLY`. + +--- + +## 7. Cross-refs + +- `odoo-blueprint-inventory-v1.md` — the 66-entity corpus (Leg 1 output). +- `odoo-extraction-tools-v1.md` — tool stacks behind each leg. +- `.claude/plans/odoo-business-logic-blueprint-v1.md` — Leg 1's plan. +- `.claude/plans/odoo-source-extraction-v1.md` — Leg 2's plan. +- `.claude/plans/streaming-arm-nars-discovery-v1.md` — Leg 3's plan (this PR #435). +- `style_recipe.rs` (PR #433), `op_emitter.rs` (PR #435) — downstream codegen consumers. +- CLAUDE.md `E-SOA-IS-THE-ONLY`, `I-NOISE-FLOOR-JIRAK`, "The Click" — doctrinal anchors. + +End of strategies. diff --git a/.claude/knowledge/odoo-extraction-tools-v1.md b/.claude/knowledge/odoo-extraction-tools-v1.md new file mode 100644 index 00000000..56f37469 --- /dev/null +++ b/.claude/knowledge/odoo-extraction-tools-v1.md @@ -0,0 +1,252 @@ +# odoo-extraction-tools-v1 — Tool stacks behind the three proposer legs + +> **READ BY:** anyone running, extending, or porting an extractor; ARM-discovery implementors; session-survival readers who need to know where the tools live. +> **Status:** FINDING (tools 1 + 2 exist on disk; tool 3 is partially scaffolded in a sibling repo). +> **Authored:** 2026-05-29. +> **Companion docs:** `odoo-blueprint-inventory-v1.md` (the 66-entity corpus), `odoo-extraction-strategies-v1.md` (the three legs). + +--- + +## 1. Tool stack for Leg 1 (Curated) — humans + Sonnet agents + +### Inputs + +- `.claude/odoo/L{1..15}-*.md` — the 15 lane L-docs. Human-authored prose. +- `crates/lance-graph-ontology/src/odoo_blueprint/mod.rs` — the typed surface (`OdooEntity`, `OdooField`, `OdooMethod`, …). +- Existing lane files (read for stylistic conformance). + +### Tool + +**5-Sonnet-agent fan-out per Wave.** No code tool; the "tool" is the prompt + the Sonnet model. Agent cards in `.claude/agents/odoo-l-doc-projector.md` would formalize this if the pattern is reused (today it's done ad-hoc per Wave-spawn). + +### Outputs + +- `crates/lance-graph-ontology/src/odoo_blueprint/l{1..15}.rs` — 66 `pub const NAME: OdooEntity = OdooEntity {...}` declarations. +- 130 lane tests (per-lane smoke tests asserting field-kind correctness, method-name presence, etc.). + +### Run procedure + +1. User invokes the orchestrator with "spawn N agents for Wave-K, lanes Lx-Ly." +2. Main thread spawns N parallel `Agent(subagent_type=general-purpose, model=sonnet)` calls in one turn. +3. Each agent reads its L-doc + the typed surface + (optionally) an exemplar lane that already exists. +4. Each agent emits one file `l.rs` with the const + the tests module. +5. Main thread (Opus) reviews; trims agent verbosity; commits as one Wave-K commit. + +### Today's status + +Complete. 66 entities across 15 lanes; 11,563 LOC; 130 lane tests. Last touched by `c04adf10` (EXT-3 back-fill for `OdooEntityKind` + `regulation_iri`). + +--- + +## 2. Tool stack for Leg 2 (Extracted) — `tools/odoo-blueprint-extractor/` + +### Location + +`/home/user/lance-graph/tools/odoo-blueprint-extractor/` — a Python 3 package, stdlib-only. + +### Layout + +```text +tools/odoo-blueprint-extractor/ +├── README.md +├── pyproject.toml +├── odoo_blueprint_extractor/ +│ ├── __init__.py +│ ├── __main__.py # python -m entry point +│ ├── cli.py # 258 LOC — argv parsing + orchestration +│ ├── pairing.py # 382 LOC — curated/extracted pairing audit +│ ├── parsers/ # 950 LOC total — AST walkers +│ │ ├── classes.py # 209 LOC — class body → OdooEntity +│ │ ├── fields.py # 161 LOC — fields.X(...) → OdooField +│ │ ├── methods.py # 147 LOC — def _compute_… → OdooMethod +│ │ ├── state_machine.py # 157 LOC — state field + transitions +│ │ ├── constraints.py # 138 LOC — _sql_constraints + @api.constrains +│ │ ├── decorators.py # 73 LOC — @api.depends/constrains/onchange +│ │ └── regulation.py # 64 LOC — module __manifest__.py → reg IRIs +│ ├── data_extractors/ # CSV/XML for non-Python data +│ │ ├── csv_chart.py # SKR03/SKR04 CSV → 24,712 LOC of chart consts +│ │ ├── xml_kennzahl.py # UStVA Kz boxes from XML +│ │ └── gobd_company.py # GoBD audit-trail wiring from XML +│ ├── emitters/ # Rust source emitters +│ │ ├── module.py # per-addon module shell +│ │ └── rust.py # OdooEntity → Rust source text +│ └── audit/ +│ └── fallback_log.py # field-kind/method-kind fallback log → JSON +└── tests/ + └── test_smoke_uom.py # one-addon smoke test (uom) +``` + +### Inputs + +- `/home/user/odoo/addons//` — Odoo source tree. The 12 TIER-1 addons are: `account`, `account_payment`, `l10n_de`, `product`, `stock`, `uom`, `base`, `analytic`, `purchase`, `sale`, `account_peppol`, `account_edi_ubl_cii`. + +### Run procedure + +```bash +# One addon, stdout (development / inspection): +python -m odoo_blueprint_extractor \ + --addons /home/user/odoo/addons \ + --addon uom \ + --out - + +# All TIER-1 addons → extracted/ tree (production): +for addon in account account_payment l10n_de product stock uom base analytic purchase sale account_peppol account_edi_ubl_cii; do + python -m odoo_blueprint_extractor \ + --addons /home/user/odoo/addons \ + --addon "$addon" \ + --out crates/lance-graph-ontology/src/odoo_blueprint/extracted \ + --audit /tmp/fallback.json +done +``` + +### Outputs + +- `crates/lance-graph-ontology/src/odoo_blueprint/extracted/.rs` — 11 files, 99,209 LOC total. +- `--audit /tmp/fallback.json` — JSON log of field/method kinds that hit the `Other` fallback. + +### Today's status + +- Shipped (commits `2026-05-28` — see `extracted/COVERAGE.md`). +- 48/53 curated entities have TIER-1 backing (90.6% workspace-wide). +- 5 TIER-2 deferrals: 4 `hr.*` (HR-base lane) + 1 `stock.valuation.layer` (deferred to TIER-2 stock-account). +- Gate test in `extracted::coverage` enforces 80% per-lane backing. +- L12-L15 (Wave-3 curated additions) need a fresh extractor pass to bring into `CURATED_EXTRACTED_PAIRS` (post-EXT-6 gap). + +### Known gaps (Stage-2 enrichment targets) + +These would light the 6 dark D-Atoms (Money / Quantity / ApplyRate / EmitAmount / Event / FiscalCtx) currently not firing: + +| Gap | Where to fix | +|---|---| +| `return_kind` mostly defaults to `Unit` | `parsers/methods.py` — needs `ast.Return` walker to peek at return-expression type | +| `semantic_role` mostly defaults to `Other` | `parsers/fields.py` — needs lexical pattern matcher on field name + comment | +| `computed: Option<&'static str>` not populated | `parsers/fields.py` — already partial; needs depends-graph cross-link | +| `state_machine` rarely captured | `parsers/state_machine.py` — needs richer detection of `write({'state': ...})` patterns inside action methods | + +--- + +## 3. Tool stack for Leg 3 (ArmDiscovered) — `lance-graph-arm-discovery` (planned) + +### Location (when implemented) + +`/home/user/lance-graph/crates/lance-graph-arm-discovery/` — new crate. Specified in `.claude/plans/streaming-arm-nars-discovery-v1.md` (D-ARM-1 through D-ARM-12, ~2,400 LOC). + +### Status + +**Specified; not implemented.** 12 deliverables D-ARM-1..D-ARM-12 in STATUS_BOARD, all **Queued**. PR #435 (this branch) ships the plan + handover, not the crate. + +### Planned components + +| Component | LOC | Role | +|---|---:|---| +| `proposer::pair_stats` | 400 | Streaming pair-stats over Arrow RecordBatch (deterministic trunk) | +| `proposer::aerial_ipc` | 200 | NDJSON-over-Unix-socket client for Aerial+ subprocess (`arm-aerial` feature) | +| `translator` | 200 | `arm_to_nars(rule) -> TruthValue` + `CandidateTriple` + projector | +| `hypothesis` | 350 | SpoStore round-trip + NARS revision + contradiction commit | +| `queue` | 200 | `RatificationQueue` bounded ring buffer | +| `jirak` | 150 | Jirak-2016 weak-dependence threshold helpers | +| `feed` | 250 | `Feed` + `FeedProjector` config + Odoo `account.move` example | +| Tests + bench | 400 | End-to-end pipeline (D-ARM-12) | + +### Supporting external tool — Aerial+ reference + +- **Upstream:** https://github.com/DiTEC-project/aerial-rule-mining (paper authors' Python reference). +- **Workspace fork:** https://github.com/AdaWorldAPI/aerial-rule-mining (shared as the workspace's fork target). +- **Access:** **Both are outside the MCP allowlist as of 2026-05-29.** Implementation will be from paper Algorithm 1 (paper text in session record); IPC contract reads NDJSON, not Python API, so upstream-source access is not strictly required. +- **OQ-ARM-11 pending:** batch the "external-repo MCP allowlist class" question (Aerial+ + surreal_container's allowlist axis share this OQ class per cross-session review). + +### Run procedure (when implemented) + +```rust +use lance_graph_arm_discovery::{ + Feed, PairStatsProposer, HypothesisTest, RatificationQueue, +}; + +let feed = Feed::from_parquet("/data/odoo/account_move_2026.parquet") + .window_size(100_000) + .projector(OdooMovementProjector::new()); + +let mut proposer = PairStatsProposer::new(&feed); +let mut hypo = HypothesisTest::new(&spo_store); +let mut queue = RatificationQueue::new(1024); + +while let Some(window) = feed.next_window().await { + for rule in proposer.next_batch(window) { + let candidate = arm_to_nars(&rule); + match hypo.test(&candidate) { + HypothesisOutcome::Revised(emission) => mailbox.emit(emission), + HypothesisOutcome::Contradiction(pair) => spo_store.commit_contradiction(pair), + HypothesisOutcome::Novel => queue.push(candidate), + } + } +} +// queue.drain() → council for ratification (manual session trigger) +``` + +--- + +## 4. Cross-cutting tool — `ruff_spo_triplet` (cross-language SPO triple core) + +### Location + +- **Workspace path during prior session:** `/tmp/ruff-work/crates/ruff_spo_triplet/` — was scaffolded into a workspace fork of `AdaWorldAPI/ruff` during the SPO-triplet-extraction session. +- **Upstream:** `AdaWorldAPI/ruff` — sibling repo (within MCP allowlist for read; verify before write). +- **Docs:** `crates/ruff_spo_triplet/SPO_TRIPLET_EXTRACTION.md` — 252-LOC methodology + Rails→IR mapping guide. + +### Purpose + +**Language-agnostic SPO triple core.** Closed 7-predicate vocabulary; `ModelGraph` IR with `expand()` semantics; `to/from_ndjson()` round-trip. Lets a new source-language frontend (Python, Ruby/Rails, etc.) emit byte-identical triple shapes consumable by `lance_graph::graph::spo`. + +### Components + +- `ruff_spo_triplet` — the core crate (language-agnostic). +- `ruff_ruby_spo` — scaffolded Ruby/Rails frontend (`todo!()` stubs for parser; methodology doc explains how to fill). + +### Today's status + +- Scaffolded; partially shipped to `AdaWorldAPI/ruff` (commit history in that repo). +- The Odoo (Python) extraction in `tools/odoo-blueprint-extractor/` was the **prior pattern** that motivated the cross-language abstraction; the ruff-side crate is the formalized version. +- ARM-discovery's Stage B translator may emit through `ruff_spo_triplet`'s NDJSON shape for cross-language compatibility (not yet specified). + +--- + +## 5. Tool-to-leg matrix + +| Tool | Leg 1 (Curated) | Leg 2 (Extracted) | Leg 3 (ArmDiscovered) | +|---|---|---|---| +| Sonnet agents in fan-out | **trunk** | review only | n/a | +| `tools/odoo-blueprint-extractor/` Python | n/a | **trunk** | n/a | +| `lance-graph-arm-discovery` Rust (planned) | n/a | n/a | **trunk** | +| Aerial+ Python subprocess (planned) | n/a | n/a | optional fan-in | +| `ruff_spo_triplet` (Python+Rust) | n/a | could replace `emitters/rust.py` | could supply IPC NDJSON shape | +| `pairing.rs::CURATED_EXTRACTED_PAIRS` | audit surface | audit surface | (separate pairing surface for ratified vs ArmDiscovered TBD) | +| Council (`epiphany-brainstorm-council` PR #433) | bypassed | bypassed | **mandatory gate** | + +--- + +## 6. Where to find each tool if the session dies + +| Tool | Path | Recoverable from | +|---|---|---| +| Lane projector (Sonnet) | `(no file — it's the agent prompt pattern)` | `.claude/agents/BOOT.md` + `.claude/odoo/L*.md` | +| Extractor Python package | `tools/odoo-blueprint-extractor/` | In main as of `2026-05-28`; will be on disk if repo is cloned | +| Extractor invocation history | Wave commits `f5702675`, `d30186e5`, `333a1ff2`, `c04adf10` | `git log --all -- 'crates/lance-graph-ontology/src/odoo_blueprint/extracted/**'` | +| ARM-discovery crate (when impl'd) | `crates/lance-graph-arm-discovery/` (planned) | Spec at `.claude/plans/streaming-arm-nars-discovery-v1.md` | +| Aerial+ reference | `DiTEC-project/aerial-rule-mining` (allowlist required) OR paper Algorithm 1 reproduction | Paper text preserved in session record | +| ruff_spo_triplet | `AdaWorldAPI/ruff` repo (within MCP allowlist) | `git log --all` on that repo | +| L-docs | `.claude/odoo/L{1..15}-*.md` | In main; gitignored if any (verify) | + +--- + +## 7. Cross-refs + +- `odoo-blueprint-inventory-v1.md` — what the tools produce (Leg 1 output). +- `odoo-extraction-strategies-v1.md` — why each leg exists. +- `.claude/plans/odoo-business-logic-blueprint-v1.md` — Leg 1's plan. +- `.claude/plans/odoo-source-extraction-v1.md` — Leg 2's plan + EXT-6 coverage report. +- `.claude/plans/streaming-arm-nars-discovery-v1.md` — Leg 3's plan. +- `crates/lance-graph-ontology/src/odoo_blueprint/extracted/COVERAGE.md` — Leg 2's current 90.6% coverage. +- `crates/lance-graph-ontology/src/odoo_blueprint/extracted/pairing.rs` — Leg 1 ↔ Leg 2 audit surface. +- Paper: Karabulut, Groth, Degeler — *Neurosymbolic Association Rule Mining* (arxiv 2504.19354v1) — Leg 3's algorithmic anchor. + +End of tools doc. diff --git a/.claude/plans/odoo-classes-bitmask-render-v1.md b/.claude/plans/odoo-classes-bitmask-render-v1.md new file mode 100644 index 00000000..45968e6e --- /dev/null +++ b/.claude/plans/odoo-classes-bitmask-render-v1.md @@ -0,0 +1,513 @@ +# odoo-classes-bitmask-render-v1 — the bounded-weekend fix classes.md prescribes, with per-agent A2A split + +> **READ BY:** every agent spawned for any D-CLS-* row, the council if convened on this plan, the spec owner before unblocking pre-conditions. +> **Status:** PLAN (pre-council). Authored 2026-05-30 after the post-#439 rebase, the 4-savant council recalibration, and the on-disk inventory that found 4-way `DolceCategory` duplication + zero `class_id` / `presence_mask` / template engine. +> **Anchors:** `cognitive-risc-classes.md` v0.2 (the spec this plan implements verbatim), `cognitive-risc-core.md` v0.1 (invariants 1, 2, 3, 4, 9, 10), `wikidata-hhtl-load.md` (OWL→HHTL facet table), `faiss-homology-cam-pq.md` (facet u64 = SoA facet column, SIMD batch-AND). +> **Doctrine line, verbatim** (classes.md:56-57): *"The fix is bounded (a weekend, not a subsystem): discriminator + parent-pointer + parent-walking resolution against the existing cache. Full machinery (shape-compiler-to-grid, behavior/traits, SIMD kernels) is explicitly DEFERRED."* + +--- + +## 0. Non-goals (explicit deferrals — DO NOT scope-creep) + +- Full shape-compiler-to-grid. Out of scope (classes.md:57). +- SIMD kernels over the facet column. Out of scope. +- Behavior/trait composition (the recipe layer). Already partially exists (`recipe_kernels.rs` from #439); not extended here. +- Wikidata 115M-entity loader. That's a different plan (wikidata-hhtl-load.md). +- Chess bring-up as a behaviour test. The chess slice IS the N4 falsifier this plan honours by NOT freezing the SoA schema; chess implementation lives in `D-CHESS-BRINGUP-1` (queued, separate branch). +- Hot-path mailbox-SoA wiring of the new column. This plan adds `class_id` to the `OdooEntity` **const** (the class catalogue) — NOT to the runtime row. Runtime wiring is a follow-up plan once class IDs are stable. +- The `discovery_origin` byte. Different axis (provenance); held under OD-1/2/3 escalation. + +## 1. Hard constraints (violating any aborts the plan) + +- **C1** — WAL is OPEN. All work here is markdown + new contract types + new const data. NO byte-grammar freeze. +- **C2** — Bitmask is PRESENCE, NEVER semantics (classes.md:50). A bit means "field N is populated"; it does NOT mean "field N behaves differently here." Any code that branches on a mask bit's *meaning* (rather than its presence) violates this and must be rejected at review. +- **C3** — Shape inherits, behaviour composes (classes.md:37). `class_id` carries field/label/template inheritance. It does NOT carry method dispatch. Methods stay on `OdooMethod` recipes. +- **C4** — Universal column ISA is frozen; per-class grids are views (classes.md:38). Adding `class_id` to `OdooEntity` is adding a discriminator, not minting a new column type per shape-family. +- **C5** — Class taxonomy is DISCOVERED, not hand-assigned (classes.md:41-44). Shape-families come from Aerial+ on structural signature, not human selection. +- **C6** — All deliverables are additive. No rename of existing types. No deletion. The 4-way `DolceCategory` consolidation collapses by adding ONE canonical type + thin re-exports; legacy aliases stay for one release cycle. +- **C7** — Per CLAUDE.md mandatory board-hygiene rule: every PR landing a D-CLS-* row updates STATUS_BOARD + AGENT_LOG in the SAME commit. + +## 2. Spec-owner pre-conditions (blocking; user must answer before code) + +These are decisions only the spec owner / project lead can make. **No agent should start D-CLS-1+ until all four are answered.** + +| OD | Question | Default lean (from spec, for ratify/reject) | +|---|---|---| +| **OD-DOLCE-CANONICAL** | Which of the 4 `DolceCategory` definitions becomes canonical? The four divergences: `lance-graph-contract::cognition::entity` (no AbstractEntity? confirm at impl), `lance-graph-ontology::hydrators::dolce_odoo` (uses `AbstractEntity`), `lance-graph-arm-discovery::aerial::ontology` (uses `Abstract`), `lance-graph-callcenter::super_domain::DolceMarker` (adds `Unknown`). | **`lance-graph-contract::cognition::entity::DolceCategory`** — zero-dep, already in the cognition pipeline, naming aligns with DOLCE-Lite-Plus. Others become re-exports. The `Unknown` variant in callcenter is real-world necessary; add it to the canonical with explicit doc. | +| **OD-CLASSID-WIDTH** | `class_id: u8` (≤256 classes), `u16` (≤65,535), or `ShapeHash(u128)` (CAM-style)? | **`ClassId(u16)`** — the spec says ~40 shape-families today, but Odoo TIER-2 + Wikidata + chess push toward thousands. u16 (≤65,535) covers all known domains without overflowing a hot-path field. CAM-style u128 is overkill for the discriminator role; CAM hashing applies at the *content* layer, not the *class* layer. | +| **OD-CLASSID-VS-ENTITYKIND** | Does `class_id` *replace* the existing `OdooEntity.kind: OdooEntityKind` ({Model, Transient, Abstract})? Or coexist? | **Coexist.** `OdooEntityKind` is the ORM-base axis (storage shape: Model = real table, Transient = wizard scratch, Abstract = inherit-only). `ClassId` is the semantic shape-family axis (which `_compute_*` patterns + emits + depends). Orthogonal axes. Both stay. | +| **OD-TEMPLATE-ENGINE** | F3 from classes.md still open. Askama (compile-time typed) vs minijinja (runtime). | **Askama**, per classes.md:72 lean: "Since meaning is late-bound through OGIT but identity/shape is frozen, templates likely compile per-class (askama) with OGIT resolving the late-bound labels at render." | + +Until these four are answered, every agent below has `Status: Blocked-on-OD`. + +## 3. The pipeline (visual + textual) + +```text + ┌─ Wave 0 ───────────────────────────────────────────────────────────────┐ + │ Spec-owner ratifies OD-DOLCE-CANONICAL, OD-CLASSID-WIDTH, │ + │ OD-CLASSID-VS-ENTITYKIND, OD-TEMPLATE-ENGINE. │ + │ Output: updated .claude/board/ISSUES.md status flips Open→Resolved. │ + └─────────────────────────────────────┬──────────────────────────────────┘ + ▼ + ┌─ Wave 1 (3 parallel Sonnet agents) ────────────────────────────────────┐ + │ 1A Consolidate DolceCategory (contract crate) │ + │ 1B Structural-signature audit of 66 entities (read-only, table) │ + │ 1C Add askama to one consumer crate Cargo.toml (1-file edit) │ + └─────────────────────────────────────┬──────────────────────────────────┘ + ▼ + ┌─ Wave 2 (1 Opus agent — judgment) ─────────────────────────────────────┐ + │ 2A Aerial+ structural-hash → shape-family candidates │ + │ Output: ~10-15 named candidate classes for human review. │ + │ → spec owner reviews + names → CANONICAL_CLASS_TABLE static const │ + └─────────────────────────────────────┬──────────────────────────────────┘ + ▼ + ┌─ Wave 3 (5 parallel Sonnet agents) ────────────────────────────────────┐ + │ 3A ClassId newtype + tests (contract) │ + │ 3B class_id field on OdooEntity + 66 lanes (ontology) │ + │ 3C FieldPositionTable per class (ontology) │ + │ 3D FieldMask(u64) + per-class width audit (contract+ontology) │ + │ 3E ClassId → (DolceCategory, FieldPositionTable) (ontology) │ + └─────────────────────────────────────┬──────────────────────────────────┘ + ▼ + ┌─ Wave 4 (3 parallel Sonnet agents) ────────────────────────────────────┐ + │ 4A Per-class askama templates (~10-15 .html.j2) (new crate) │ + │ 4B render(entity_const, mask) function (new crate) │ + │ 4C Unit tests: mask round-trip + render-skip (new crate) │ + └─────────────────────────────────────┬──────────────────────────────────┘ + ▼ + ┌─ Wave 5 (1 Opus agent — integration synthesis) ────────────────────────┐ + │ 5A End-to-end: 66 entities × class templates → 66 render outputs. │ + │ Verify presence-discipline (C2): no bit branches on semantics. │ + │ Flag for chess falsifier (D-CHESS-BRINGUP-1, separate branch). │ + └────────────────────────────────────────────────────────────────────────┘ +``` + +Total: **9 deliverables (D-CLS-1..D-CLS-9), 10 agent runs across 5 waves, ~1,800 LOC across 1 new crate + 3 modified crates.** No code on the hot path; only the const catalogue + render path. + +--- + +## 4. Deliverables (D-CLS-1 through D-CLS-9 — meticulous) + +### D-CLS-1 — Canonical `DolceCategory` in `lance-graph-contract` + +**What.** Pick `lance-graph-contract::cognition::entity::DolceCategory` as canonical (per OD-DOLCE-CANONICAL). Add `Unknown` variant if not present. Add re-export aliases from the other 3 sites pointing at the canonical: +- `lance-graph-ontology::hydrators::dolce_odoo::DolceCategory` → `pub use lance_graph_contract::cognition::entity::DolceCategory;` + deprecate the local enum with `#[deprecated(note = "use lance_graph_contract::cognition::entity::DolceCategory")]` +- `lance-graph-arm-discovery::aerial::ontology::DolceCategory` → same pattern (but: arm-discovery is **zero-dep / excluded from workspace**, so the re-export uses a local newtype that mirrors the canonical and a TryFrom; do NOT add `lance-graph-contract` as a dep — that violates the zero-dep stance of #436). +- `lance-graph-callcenter::super_domain::DolceMarker` → keep `DolceMarker` as-is (it's the *unknown-tolerant* projection); add `From for DolceMarker` to the canonical. + +**Why.** classes.md known-debt: "stop hand-rolling the inheritance the cache was built to serve." Four parallel definitions IS the hand-rolling. C6 says additive — re-exports + deprecations, no rename. + +**Files.** +- `crates/lance-graph-contract/src/cognition/entity.rs` — add `Unknown` variant if absent; add doc-comment noting canonical status. +- `crates/lance-graph-ontology/src/hydrators/dolce_odoo.rs` — re-export + deprecation. +- `crates/lance-graph-arm-discovery/src/aerial/ontology.rs` — local newtype + TryFrom (cannot add contract dep). +- `crates/lance-graph-callcenter/src/super_domain.rs` — add `From for DolceMarker`. +- **TESTS:** `crates/lance-graph-contract/src/cognition/entity.rs` (existing test module) — assert all 4 DOLCE canonical variants round-trip; assert callcenter `From` mapping preserves Endurant/Perdurant/Quality/Abstract and maps `Unknown` to `DolceMarker::Unknown`. + +**Depends.** OD-DOLCE-CANONICAL ratified. +**Blocks.** D-CLS-2, D-CLS-5 (every downstream uses the canonical). +**LOC.** ~80 added, ~0 deleted (per C6 additive). +**Blast radius.** Contract crate (n8n-rs, crewai-rust consume it — additive only, no API break). +**Agent.** Wave 1A, Sonnet. + +--- + +### D-CLS-2 — Structural-signature audit of the 66 `OdooEntity` consts + +**What.** Read each of `crates/lance-graph-ontology/src/odoo_blueprint/l{1..15}.rs`. For each `pub const NAME: OdooEntity` produce a row: + +```text +(const_name, model_name, kind, field_count, method_kind_histogram, decorator_kind_histogram, has_state_machine, depends_signature_hash, emits_signature_hash, structural_hash) +``` + +where `structural_hash` = stable hash over `(sorted_field_kinds, sorted_method_kinds, sorted_decorator_kinds, has_state_machine_bool, depends_signature_hash, emits_signature_hash)`. Use BLAKE3-128 truncated to u64 for the structural hash; this is the input Aerial+ groups on in D-CLS-3. + +Output: `.claude/knowledge/odoo-66-structural-signatures.psv` — pipe-separated table, alphabetical by const_name. + +**Why.** classes.md:43 — "Group the 20k by structural signature (which fields, compute-method shape, depends_on/emits pattern) — computable, via group-by-on-structural-hash or Aerial+." This is the structural-hash precomputation step. Aerial+ (D-CLS-3) groups by it. + +**Files.** +- READ: all `crates/lance-graph-ontology/src/odoo_blueprint/l{1..15}.rs` (66 consts). +- WRITE: `.claude/knowledge/odoo-66-structural-signatures.psv` (NEW, ~70 lines). +- WRITE: `.claude/knowledge/odoo-66-structural-signatures-method.md` (NEW, ~80 LOC) — explains hash algorithm + reproducibility recipe. +- **TESTS:** none (read-only emission); but the method doc includes a manual reproduction command using `cargo test -p lance-graph-ontology --test signature_dump -- --nocapture` IF the agent decides to add a test-time emitter (optional, additive). + +**Depends.** None — read-only. +**Blocks.** D-CLS-3. +**LOC.** ~150 in the PSV + ~80 in the method doc. +**Blast radius.** None (knowledge docs only). +**Agent.** Wave 1B, Sonnet. + +--- + +### D-CLS-3 — Aerial+ → shape-family candidates (running existing #436 crate) + +**What.** Use `lance-graph-arm-discovery::aerial::AerialProposer` over the structural-signature table from D-CLS-2. Each row = one "transaction"; each (field_kind, method_kind, decorator_kind) bit = one "item". Mine rules at `max_antecedent = 1` and `max_antecedent = 2` with the Jirak-bound floor (D-ARM-7 when it lands; until then, use the conservative `support ≥ 3, confidence ≥ 0.85` default per ARM-plan §3 — be explicit this is a placeholder). + +Output: ranked list of structural-similarity clusters. Each cluster = candidate shape-family. Expected ~10-15 clusters from 66 entities per classes.md:42 ("~dozens of shape-families"). + +Emit: `.claude/knowledge/odoo-66-shape-family-candidates.md` with per-cluster: +- Cluster ID (provisional, e.g. `SF-CANDIDATE-001`) +- Member const names (typically 3-8 OdooEntity consts per cluster) +- Defining-signature digest (which field/method/decorator bits ALL members share) +- One-line semantic guess for the spec owner to ratify or rename + +**Why.** classes.md:43-44 — "The shapes sharing a signature ARE a class; name the groups, don't hand-assign them." Aerial+ is the named tool for this in the spec. + +**Files.** +- READ: `.claude/knowledge/odoo-66-structural-signatures.psv` (from D-CLS-2). +- READ: `crates/lance-graph-arm-discovery/src/aerial/` (existing #436 crate, no modification). +- WRITE: `crates/lance-graph-arm-discovery/examples/odoo_66_class_discovery.rs` (NEW example binary, ~150 LOC) — loads the PSV, runs AerialProposer, emits clusters as markdown. +- WRITE: `.claude/knowledge/odoo-66-shape-family-candidates.md` (NEW, ~200 LOC). +- **TESTS:** the example is itself a test — Sonnet runs `cargo run --example odoo_66_class_discovery` and pastes output into the markdown. + +**Depends.** D-CLS-2. +**Blocks.** Wave 3 (spec owner must NAME the clusters before D-CLS-3a output becomes the CANONICAL_CLASS_TABLE). +**LOC.** ~350. +**Blast radius.** New example in `lance-graph-arm-discovery`; standalone, excluded from workspace, no consumer impact. +**Agent.** Wave 2A, **Opus** (running Aerial+ is mechanical; INTERPRETING the clusters into shape-family names requires judgment + cross-reference to the 66 entities' L-doc prose — accumulation). + +**SPEC-OWNER GATE.** After D-CLS-3, the spec owner reviews the ~10-15 candidate clusters and either ratifies the auto-generated names or renames them. The ratified names become the `CANONICAL_CLASS_TABLE` (D-CLS-9). Until ratified, Wave 3 cannot start. + +--- + +### D-CLS-4 — Add askama to the new render crate + +**What.** Create a new workspace crate `lance-graph-ontology-render` (excluded from default workspace, like bgz17 — so the heavy lance/datafusion deps don't pull it). Add `askama = "0.12"` (or current). Skeleton crate with `src/lib.rs` + `templates/` directory + `Cargo.toml`. NO templates yet (D-CLS-7). + +**Why.** OD-TEMPLATE-ENGINE = askama per spec lean. F3 in classes.md:72. Standalone crate keeps the template engine out of the contract crate (which is zero-dep) and out of `lance-graph-ontology` (which already has heavy deps; adding askama would bloat compile time for every consumer). + +**Files.** +- `crates/lance-graph-ontology-render/Cargo.toml` (NEW, ~25 LOC). +- `crates/lance-graph-ontology-render/src/lib.rs` (NEW, ~20 LOC — skeleton with one smoke test). +- `crates/lance-graph-ontology-render/templates/.gitkeep` (NEW). +- `Cargo.toml` (workspace root) — add to `exclude = [...]` list (NOT to members, per the standalone pattern). +- **TESTS:** one smoke test in `lib.rs`: assert askama hello-world compiles. + +**Depends.** OD-TEMPLATE-ENGINE ratified. +**Blocks.** D-CLS-7, D-CLS-8. +**LOC.** ~70. +**Blast radius.** None (new excluded crate). +**Agent.** Wave 1C, Sonnet. + +--- + +### D-CLS-5 — `ClassId(u16)` newtype in `lance-graph-contract` + +**What.** Add `pub struct ClassId(pub u16);` to `lance-graph-contract::cognition::entity` (next to `OgitUriRef`). Implement `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Hash`, `Default` (= 0 = "unclassified"). Add `pub const UNCLASSIFIED: ClassId = ClassId(0);` so `Default::default()` is meaningful. Add a doc-comment quoting classes.md:63 N1 verbatim. + +**Why.** Cognitive-RISC N1 (classes.md:63). The single hook the entire triangle hangs off. Spec is emphatic: "Add it even before full inheritance exists — it's the hook." + +**Files.** +- `crates/lance-graph-contract/src/cognition/entity.rs` — add `ClassId` struct + `UNCLASSIFIED` const + doc. +- **TESTS:** `crates/lance-graph-contract/src/cognition/entity.rs` (existing test module): assert `ClassId::UNCLASSIFIED == ClassId(0)`; assert `Default::default() == ClassId::UNCLASSIFIED`; assert `ClassId(u16::MAX)` constructs. + +**Depends.** OD-CLASSID-WIDTH ratified. +**Blocks.** D-CLS-6, D-CLS-9. +**LOC.** ~40. +**Blast radius.** Contract crate (consumers see new symbol, no API change). +**Agent.** Wave 3A, Sonnet. + +--- + +### D-CLS-6 — Add `class_id: ClassId` field to `OdooEntity` + back-fill all 66 consts + +**What.** Add a `pub class_id: ClassId` field to `OdooEntity` struct at `crates/lance-graph-ontology/src/odoo_blueprint/mod.rs:115`. Default initialization to `ClassId::UNCLASSIFIED` so the 66 existing consts compile UNCHANGED initially (additive per C6). + +THEN: walk all 66 consts, set `class_id` to the ratified `CANONICAL_CLASS_TABLE` value (from the spec owner's review of D-CLS-3 output). Each lane file gets the same surgical edit per its 2-6 consts. + +**Why.** The hook lands on the entity catalogue. `OdooEntity` is the const-data class catalogue; this gives every catalogue entry its class discriminator. Per C4: this is adding a discriminator field, NOT minting a new column type per family — same struct, new field, all entities use it. + +**Files.** +- `crates/lance-graph-ontology/src/odoo_blueprint/mod.rs` — `OdooEntity` struct gets `class_id` field; constructor docs updated. +- `crates/lance-graph-ontology/src/odoo_blueprint/l1.rs` through `l15.rs` — each of 66 const literals gets `class_id: SF_,` (where `SF_` references the CANONICAL_CLASS_TABLE shape-family). +- `crates/lance-graph-ontology/src/odoo_blueprint/CLASS_TABLE.rs` (NEW, ~80 LOC) — `pub const SF_: ClassId = ClassId(N);` for each shape-family + a `static SHAPE_FAMILIES: &[(ClassId, &str)]` index. +- **TESTS:** `crates/lance-graph-ontology/src/odoo_blueprint/mod.rs` test module — assert ALL 66 consts have a non-default `class_id`; assert every class_id used appears in `SHAPE_FAMILIES`; assert no two shape-families share a `ClassId`. + +**Depends.** D-CLS-5, D-CLS-3 (spec-owner ratification of the cluster names). +**Blocks.** D-CLS-7, D-CLS-8, D-CLS-9. +**LOC.** ~30 in mod.rs + ~80 in CLASS_TABLE + ~150 across 15 lane files (the `class_id: SF_*` line per const). +**Blast radius.** `lance-graph-ontology` only. No consumer crate sees a breaking change (new field, default initialization where unset). +**Agent.** Wave 3B, Sonnet (mechanical edit across 15 lane files + 1 new table file; clear input, clear output). + +--- + +### D-CLS-7 — Per-class field-position table + `FieldMask(u64)` + +**What.** For each shape-family in `CANONICAL_CLASS_TABLE`, define a `FieldPositionTable` — an append-only ordered list of `OdooField` names that members of this class can carry. The position in the list IS the bit position in the presence mask. New fields APPEND; retired fields are kept with a `Retired` marker (per C3-style discipline + classes.md:65 N3). + +```rust +pub struct FieldPositionTable { + pub class: ClassId, + pub positions: &'static [FieldPosition], +} + +pub struct FieldPosition { + pub name: &'static str, // field name as on OdooField + pub status: FieldPositionStatus, // Active | Retired + pub since_version: u16, // append-only audit +} + +pub enum FieldPositionStatus { Active, Retired } + +pub struct FieldMask(pub u64); // bit N is set iff position N is populated +``` + +For each shape-family (the ~10-15 from CANONICAL_CLASS_TABLE), generate the position table by union of `field.name` across all `OdooEntity` consts assigned to that ClassId. + +Audit: per-class field count ≤ 64. If any class exceeds, FLAG to spec owner (decision: split the class, OR widen to `FieldMask128(u128)`, OR move some fields to a sub-class). DO NOT auto-resolve. + +**Why.** Classes.md:65 N3 — stable bitmask bit-positions per class, append-only. Mask width per spec: "dozens of bits" (line 51) — u64 is generous. Audit catches the union-disease early. + +**Files.** +- `crates/lance-graph-contract/src/cognition/field_mask.rs` (NEW, ~80 LOC) — `FieldMask`, `FieldPosition`, `FieldPositionStatus`, `FieldPositionTable` types + their tests (mask AND/OR/coverage helpers; reject set-on-Retired). +- `crates/lance-graph-contract/src/cognition/mod.rs` — `pub mod field_mask;`. +- `crates/lance-graph-ontology/src/odoo_blueprint/CLASS_TABLE.rs` — extend with `pub const SF__POSITIONS: &'static [FieldPosition] = &[...];` per class + `pub const SF__TABLE: FieldPositionTable = FieldPositionTable { class: SF_, positions: SF__POSITIONS };`. +- `crates/lance-graph-ontology/src/odoo_blueprint/class_audit.rs` (NEW, ~120 LOC) — emits the audit report at test-time; assertion test that all class field counts ≤ 64 (FAIL with explicit message if not, naming the offending class + field count). +- **TESTS:** `crates/lance-graph-contract/src/cognition/field_mask.rs` test module (mask round-trip; Retired-position reject; OR/AND); `crates/lance-graph-ontology/src/odoo_blueprint/class_audit.rs` test module (per-class width gate; per-class field-name uniqueness). + +**Depends.** D-CLS-5 (ClassId), D-CLS-6 (CLASS_TABLE). +**Blocks.** D-CLS-8 (render needs mask + position table). +**LOC.** ~80 + ~120 + ~50 in CLASS_TABLE extensions ≈ 250. +**Blast radius.** Contract crate (additive new type); ontology crate (new const data + new audit test). +**Agent.** Wave 3C + 3D split — 3C does the contract-side types + tests, 3D does the ontology-side per-class tables + audit. Both Sonnet, both mechanical (input is the CLASS_TABLE from D-CLS-6 + the OdooField data from the existing 66 consts). + +--- + +### D-CLS-8 — `render(entity_const, mask) -> String` + per-class askama templates + +**What.** In the new `lance-graph-ontology-render` crate: + +1. Per shape-family in `CANONICAL_CLASS_TABLE`, write ONE askama template `templates/.html.j2` (or `.txt.j2` — see note). The template references only the fields in that class's `FieldPositionTable`; each field is wrapped in `{% if mask.has(POS_N) %} ... {% endif %}` (presence-only gate). + +2. A render function: +```rust +pub fn render(entity: &OdooEntity, mask: FieldMask) -> Result +``` +that dispatches on `entity.class_id` to the right askama template and renders with the entity's data + the mask. + +3. **CRITICAL — C2 enforcement at render time.** The render function refuses to read a field's *value* unless the corresponding bit in the mask is set. Even if the field has a default. This makes presence the actual gate, not just a hint. + +Note on template extension: per F3 spec is silent on output format. Default to `.txt.j2` — the templates are *projections of the entity into a presentation format*, not necessarily HTML. HTML templates can be added per-class on top of the txt baseline. Templates are tiny (one per class, dozens of lines each). + +**Why.** classes.md:46-51 — "Render = class template, fields gated by bitmask, off-bits skipped." This is the render path. + +**Files.** +- `crates/lance-graph-ontology-render/src/lib.rs` (extend from D-CLS-4 skeleton) — add `render()`, `RenderErr`, dispatch logic. +- `crates/lance-graph-ontology-render/templates/.txt.j2` × N (where N = #classes from D-CLS-3) — typically 10-15 templates, ~30 LOC each ≈ 300-450 LOC total. +- `crates/lance-graph-ontology-render/src/templates.rs` (NEW, ~80 LOC) — askama derive structs (one per template, one per ClassId), each carrying the slice of `OdooField` borrows + the mask. +- **TESTS:** per-class smoke test (render returns Ok for a known good entity + mask; refuses to access a field when the bit is unset; renders the same output for the same (entity, mask) pair bit-for-bit identically). + +**Depends.** D-CLS-4 (crate skeleton), D-CLS-6 (CLASS_TABLE), D-CLS-7 (FieldMask + per-class tables). +**Blocks.** D-CLS-9 (integration test). +**LOC.** ~80 + ~80 + ~350 (templates) ≈ 510. Templates dominate. +**Blast radius.** New crate; no consumer impact (consumers opt-in by depending on it). +**Agent.** Wave 4A + 4B split — 4A authors the ~10-15 templates (mechanical, one per shape-family); 4B writes the dispatch + render + template-derive structs. Wave 4C writes the per-class unit tests. All Sonnet. + +--- + +### D-CLS-9 — Integration test: 66 entities × ratified classes × render + +**What.** Single end-to-end test: +1. Iterate the 66 OdooEntity consts. +2. For each, look up its `class_id` → `FieldPositionTable`. +3. Compute the presence mask by walking the entity's `fields` slice and setting bit N iff `field.name == positions[N].name`. +4. Call `render(entity, mask)`. +5. Assert Ok + non-empty. +6. Snapshot the output (insta or hand-coded golden files) for each entity. + +Then a **second test** asserting C2 (presence-not-semantics discipline): build a mutant mask that flips a bit OFF, render, assert the field's content does NOT appear in the output. Then flip the bit ON without populating the field — assert the render fails (or emits a clearly-empty value, depending on the canonical decision). + +Then a **third (advisory) audit**: emit a markdown report listing per-shape-family the average mask density (fraction of positions populated across members) — this is the metric wikidata-hhtl-load.md:84 calls "THE optimization metric". Land it as a CI-visible artifact, not a gate. + +**Why.** Wave-5 closure. Verifies the bounded-weekend objective: every OdooEntity can be classified, masked, and rendered through its class template; presence discipline holds. + +**Files.** +- `crates/lance-graph-ontology-render/tests/integration_66_entities.rs` (NEW, ~250 LOC). +- `crates/lance-graph-ontology-render/tests/snapshots/` (NEW directory; 66 golden snapshot files, one per entity). +- `crates/lance-graph-ontology-render/src/audit.rs` (NEW, ~80 LOC) — mask-density metric + markdown reporter. +- `.claude/knowledge/odoo-66-mask-density-report.md` (NEW, generated by the audit test, ~80 LOC) — per-class density numbers + interpretation per wikidata-hhtl-load.md:84. +- **CI HOOK:** `cargo test -p lance-graph-ontology-render` is the gate; if any of the 66 entities fails to render, the test fails. + +**Depends.** D-CLS-1 through D-CLS-8 all green. +**Blocks.** Nothing (this is the closure). +**LOC.** ~250 + ~80 + 66 snapshots × ~30 LOC each ≈ 2,310. Bulk is snapshots. +**Blast radius.** Render crate only. +**Agent.** Wave 5A, **Opus** (integration synthesis + the C2-discipline mutant-test design needs judgment about what "clearly-empty" means; this is not mechanical). + +--- + +## 5. Per-agent split (the A2A wave map) + +| Wave | Agent | Model | D-CLS | Deliverable | LOC | Parallel? | +|---:|---|---|---|---|---:|---| +| 1 | **1A** | Sonnet | D-CLS-1 | DolceCategory consolidation | 80 | Yes | +| 1 | **1B** | Sonnet | D-CLS-2 | Structural-signature audit of 66 | 230 | Yes | +| 1 | **1C** | Sonnet | D-CLS-4 | Render crate skeleton + askama dep | 70 | Yes | +| 2 | **2A** | **Opus** | D-CLS-3 | Aerial+ structural-hash → shape-family candidates | 350 | No (sequential after Wave 1) | +| — | (spec owner) | — | — | Ratifies cluster names → CANONICAL_CLASS_TABLE | — | Blocking gate | +| 3 | **3A** | Sonnet | D-CLS-5 | ClassId(u16) newtype in contract | 40 | Yes | +| 3 | **3B** | Sonnet | D-CLS-6 | class_id field + back-fill 66 consts | 260 | Yes | +| 3 | **3C** | Sonnet | D-CLS-7 (contract side) | FieldMask + FieldPositionTable types | 80 | Yes | +| 3 | **3D** | Sonnet | D-CLS-7 (ontology side) | Per-class position tables + audit | 170 | Yes (after 3A+3B start, can run concurrently from same baton) | +| 3 | **3E** | Sonnet | D-CLS-7 (lookup glue) | ClassId → (DolceCategory, FieldPositionTable) helper | 50 | Yes | +| 4 | **4A** | Sonnet | D-CLS-8 (templates) | ~10-15 askama templates per shape-family | 350 | Yes | +| 4 | **4B** | Sonnet | D-CLS-8 (dispatch) | render() + template-derive structs | 160 | Yes | +| 4 | **4C** | Sonnet | D-CLS-8 (tests) | Per-class unit tests | 80 | Yes | +| 5 | **5A** | **Opus** | D-CLS-9 | Integration test + mask-density audit | 2,310 | No (final synthesis) | + +**Totals:** 11 agent runs (10 working agents + 1 spec-owner gate). 2 Opus, 9 Sonnet. ~4,230 LOC across 1 new crate + 3 modified crates. Bulk LOC is generated content (66 snapshot files in D-CLS-9 + 15 askama templates in D-CLS-8). + +### Inter-wave dependency DAG (the baton chain) + +```text + [OD pre-conditions ratified] + │ + ┌──────────────────────┼──────────────────────┐ + ▼ ▼ ▼ + 1A (DOLCE) 1B (signatures) 1C (render-crate) + │ │ │ + └──────────┬───────────┴──────────────────────┘ + ▼ + 2A (Aerial+ on signatures) + │ + ▼ + [spec owner names ~10-15 clusters → CANONICAL_CLASS_TABLE] + │ + ┌──────────┼──────────┬──────────┬──────────┐ + ▼ ▼ ▼ ▼ ▼ + 3A 3B 3C 3D 3E + ClassId class_id FieldMask pos tables lookup glue + │ │ │ │ │ + └──────────┴──────────┴──────────┴──────────┘ + │ + ┌──────────────────────┼──────────────────────┐ + ▼ ▼ ▼ + 4A (templates) 4B (render) 4C (tests) + │ │ │ + └──────────┬───────────┴──────────────────────┘ + ▼ + 5A (integration + audit) + │ + ▼ + [READY FOR REVIEW / MERGE] +``` + +### A2A coordination protocol (Layer-2 blackboard) + +Per CLAUDE.md `Layer-2 — Session A2A`, `.claude/board/AGENT_LOG.md` is the blackboard. Every agent's prompt MUST include this preamble: + +```text +You are agent of multi-agent plan `odoo-classes-bitmask-render-v1`. +MANDATORY READS (in order, before any output): +1. .claude/board/AGENT_LOG.md (top 50 lines — what just shipped) +2. .claude/plans/odoo-classes-bitmask-render-v1.md § +3. .claude/specs/cognitive-risc-classes.md (§"Jinja = classes + presence bitmask") +4. [your D-CLS dependency outputs — the specific files prior agents wrote] + +Your D-CLS: +Your files (exact paths): [from §4] +Your tests gate: [from §4] +HARD CONSTRAINTS: C1-C7 from plan §1. Honour all 7 or your output is rejected. + +AFTER committing your work: +1. Prepend an AGENT_LOG entry in this format: + ## [ / Sonnet|Opus] D-CLS-X — + **Branch:** | **Files:** + **Cargo:** + **Outcome:** DONE/BLOCKED + one-paragraph what + what's next +2. Do NOT modify any other D-CLS-X row's files (cross-lane discipline). +3. If you discover a hard constraint violation in your input, BLOCK and surface + it to AGENT_LOG instead of attempting a fix. +``` + +### Per-wave gate (cannot start next wave until prior wave passes) + +- **After Wave 1:** all three agents committed; `cargo check -p lance-graph-contract -p lance-graph-ontology` green; D-CLS-2 PSV present in knowledge dir. +- **After Wave 2:** spec owner has reviewed `.claude/knowledge/odoo-66-shape-family-candidates.md` and added a `## RATIFIED:` section naming the ~10-15 shape-families. AGENT_LOG entry from spec owner (manual; not an agent task). +- **After Wave 3:** all 5 agents committed; `cargo test -p lance-graph-contract` green (incl. ClassId, FieldMask tests); `cargo test -p lance-graph-ontology` green (incl. class-audit); per-class field-width audit reports zero violations. +- **After Wave 4:** all 3 agents committed; `cargo test -p lance-graph-ontology-render` green (per-class smoke tests). +- **After Wave 5:** integration test green; mask-density audit emitted to `.claude/knowledge/`; PR ready for review. + +--- + +## 6. File-level ownership matrix (no two agents touch the same file) + +| File | Agent | Operation | +|---|---|---| +| `crates/lance-graph-contract/src/cognition/entity.rs` | 1A + 3A | 1A adds `DolceCategory::Unknown` (if absent); **3A** adds `ClassId(u16)` (different region of same file) | +| `crates/lance-graph-ontology/src/hydrators/dolce_odoo.rs` | 1A | re-export + deprecation | +| `crates/lance-graph-arm-discovery/src/aerial/ontology.rs` | 1A | local newtype + TryFrom | +| `crates/lance-graph-callcenter/src/super_domain.rs` | 1A | `From` | +| `.claude/knowledge/odoo-66-structural-signatures.psv` | 1B | NEW | +| `.claude/knowledge/odoo-66-structural-signatures-method.md` | 1B | NEW | +| `crates/lance-graph-ontology-render/Cargo.toml` | 1C | NEW | +| `crates/lance-graph-ontology-render/src/lib.rs` | 1C (skeleton) + 4B (extend) | sequenced | +| `crates/lance-graph-ontology-render/templates/.gitkeep` | 1C | NEW | +| `Cargo.toml` (workspace root) | 1C | edit `exclude` list | +| `crates/lance-graph-arm-discovery/examples/odoo_66_class_discovery.rs` | 2A | NEW | +| `.claude/knowledge/odoo-66-shape-family-candidates.md` | 2A | NEW | +| `crates/lance-graph-ontology/src/odoo_blueprint/mod.rs` | 3B | add `class_id` field | +| `crates/lance-graph-ontology/src/odoo_blueprint/l{1..15}.rs` | 3B | back-fill 66 consts | +| `crates/lance-graph-ontology/src/odoo_blueprint/CLASS_TABLE.rs` | 3B (skeleton) + 3D (extend) | sequenced | +| `crates/lance-graph-contract/src/cognition/field_mask.rs` | 3C | NEW | +| `crates/lance-graph-contract/src/cognition/mod.rs` | 3C | add `pub mod field_mask;` | +| `crates/lance-graph-ontology/src/odoo_blueprint/class_audit.rs` | 3D | NEW | +| `crates/lance-graph-ontology/src/odoo_blueprint/class_lookup.rs` | 3E | NEW (~50 LOC) | +| `crates/lance-graph-ontology-render/templates/.txt.j2` × ~12 | 4A | NEW | +| `crates/lance-graph-ontology-render/src/templates.rs` | 4B | NEW | +| `crates/lance-graph-ontology-render/src/lib.rs` (extend) | 4B | dispatch + render + RenderErr | +| `crates/lance-graph-ontology-render/tests/per_class_smoke_.rs` × ~12 | 4C | NEW | +| `crates/lance-graph-ontology-render/tests/integration_66_entities.rs` | 5A | NEW | +| `crates/lance-graph-ontology-render/tests/snapshots/*.snap` × 66 | 5A | NEW (generated) | +| `crates/lance-graph-ontology-render/src/audit.rs` | 5A | NEW | +| `.claude/knowledge/odoo-66-mask-density-report.md` | 5A | NEW (test-generated) | +| `.claude/board/AGENT_LOG.md` | every agent | PREPEND only | +| `.claude/board/STATUS_BOARD.md` | every agent at PR-merge | row status flip Queued→In-progress→Shipped | + +**Two files (`entity.rs`, `lib.rs`, `CLASS_TABLE.rs`) have multi-agent edits in different waves** — sequenced, never parallel on the same file. Per-line conflicts impossible by construction (1A's region is `DolceCategory`; 3A's region is `ClassId` — different parts of same file). + +--- + +## 7. Risk register + +| Risk | Probability | Impact | Mitigation | +|---|---|---|---| +| Wave-2 Aerial+ returns >20 clusters → too fragmented to manage | Med | Med | Spec owner can merge clusters; the structural-hash is repeatable, so re-runs with different `max_antecedent` parameters explore the cluster-coarseness axis | +| Wave-2 returns <5 clusters → too coarse, distinct entities collide | Low | Med | Same axis: re-run with stricter `min_confidence` to force differentiation | +| A class's field count exceeds 64 → FieldMask(u64) overflow | Med | High | D-CLS-7 audit catches this AT TEST TIME and fails the build; explicit decision required (split class, widen to u128, or sub-classes) | +| The spec owner doesn't ratify the Wave-2 cluster names | Block | Block | Wave 3 blocked indefinitely; no auto-fallback. Plan explicitly names this as a manual gate (not "automatically continue with provisional names") | +| `askama` template engine choice rejected (OD-TEMPLATE-ENGINE flipped to minijinja or a third option) | Low | High | Re-do D-CLS-4 + D-CLS-8 dispatch + templates. Everything else (D-CLS-1, 2, 3, 5, 6, 7, 9) is engine-agnostic and unaffected | +| Aerial+ structural-hash is noisy on small N=66 input | Med | Low | Spec doc warned this at #438: "not measurable on 10" (wikidata-hhtl-load.md:85). N=66 may be borderline. Fall back to deterministic group-by on structural-hash if Aerial+ is unstable | +| D-CLS-1 deprecation triggers downstream warnings in n8n-rs / crewai-rust | Med | Low | Deprecations are warnings, not errors; downstream has one release cycle to migrate. Document the migration path in the deprecation message itself | +| Cross-file edits (entity.rs, lib.rs, CLASS_TABLE.rs) collide between 1A+3A or 1C+4B | Low | Med | Sequencing per the DAG eliminates this; agents in the same wave touch DIFFERENT files | +| C2 violation lands undetected (a bit is read as semantics) | Med | High | D-CLS-9's second test (mutant mask) is the canonical detector. Make the test mandatory for green CI | +| Workspace `cargo check` regresses due to D-CLS-4's new excluded crate not being checked in CI | Low | Low | Add `cargo test --manifest-path crates/lance-graph-ontology-render/Cargo.toml` to CI matrix (standalone crate pattern per bgz17/deepnsm) | +| The 4-way DOLCE consolidation breaks arm-discovery's zero-dep stance | Low | High | D-CLS-1 explicitly does NOT add `lance-graph-contract` as an arm-discovery dep; uses a local newtype + TryFrom. Sonnet must follow this precisely | + +--- + +## 8. What this plan does NOT decide (escalations remain) + +- OD-1/2/3 (`discovery_origin` byte width + tier set + Conjecture/Derived) — still SPEC-OWNER from yesterday. This plan does not touch `discovery_origin`. +- F1/F2/F4 from classes.md v0.2 — F1 settled, F2 default-federate, F4 universal column ISA design — F4 is the load-bearing unsolved piece *for the SoA*, but this plan only adds `class_id` to the const catalogue, not to the SoA row. F4 is downstream. +- The chess bring-up (D-CHESS-BRINGUP-1, queued). Per R1+R2 from yesterday's council it deserves its own branch + freeze-decision authority. This plan does not implement chess; it just doesn't *prevent* chess (per C1 — no WAL freeze). +- The hot-path mailbox-SoA `class_id` + `field_mask` columns. Different plan. This plan touches only the const catalogue + render path. + +--- + +## 9. Cross-refs + +- `cognitive-risc-classes.md` v0.2 §"Jinja = classes + presence bitmask" + §"NON-DEFERRABLE freeze-time moves" — the spec. +- `cognitive-risc-core.md` v0.1 §"discovery_origin" — parallel byte-grammar work, NOT in this plan's scope. +- `wikidata-hhtl-load.md` §"Facets: OWL/DOLCE as the template" — the OWL→facet mapping table that informs D-CLS-3's signature design. +- `faiss-homology-cam-pq.md` §"Why CAM_PQ is cheaper than float PQ" — confirms `facet u64 IS the SoA facet column → SIMD batch-AND`; informs the FieldMask u64 choice in D-CLS-7. +- `.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md` — the reconciliation work that surfaced the 4-way DOLCE duplication this plan fixes in D-CLS-1. +- `.claude/plans/post-438-integration-options-v1.md` §1 Option G — chess bring-up (the N4 falsifier this plan honours by not freezing the SoA schema). +- `.claude/board/EPIPHANIES.md` `E-DISCOVERY-ORIGIN-HOME-IS-ARIGRAPH-BRIDGE` — the structurally adjacent insight; this plan's `FieldMask`/`class_id` IS the same shape but at the catalogue layer. +- `.claude/board/EPIPHANIES.md` `E-TEMPLATE-IS-CHECKLIST-IS-DATOMS` + `E-RELIABILITY-IS-CHECKLIST-COVERAGE` (parallel session, #439) — the `ThoughtMask` shape this plan generalises to `FieldMask`. +- `crates/lance-graph-arm-discovery/src/aerial/` (#436) — the existing crate D-CLS-3 calls; no modification. +- `crates/lance-graph-contract/src/recipe_kernels.rs:137` (#439) — the existing `ThoughtMask(u8)` this plan's `FieldMask(u64)` is the structural sibling of. +- 4 `DolceCategory` definitions catalogued in the prior session's bitmask-state inventory; D-CLS-1 fixes the duplication. + +End of plan. diff --git a/.claude/plans/post-438-integration-options-v1.md b/.claude/plans/post-438-integration-options-v1.md new file mode 100644 index 00000000..39850005 --- /dev/null +++ b/.claude/plans/post-438-integration-options-v1.md @@ -0,0 +1,146 @@ +# post-438-integration-options-v1 — what to do next (council recalibration input) + +> **READ BY:** council reviewers, integration-lead, the next session that lands on this branch. +> **Status:** OPTIONS LIST (pre-council). Composed 2026-05-30 after rebase onto post-#438 main. +> **Purpose:** the user asked for an integration plan AS A LIST OF POSSIBILITIES so the council can recalibrate via brutally honest review before autonomous auto-resolve. This is that list — not a single path, not a recommendation that bypasses review. + +--- + +## 0. State snapshot (verified on-disk this session) + +- **Branch:** `claude/activate-lance-graph-att-k2pHI` HEAD `8d75294b`, rebased onto `origin/main` HEAD `4b00d049`. +- **My 4 commits on this branch (all docs, no code):** survival dossier (66-entity inventory + strategies + tools), reconciliation doc (discovery_origin + Jirak), Jirak §7 verification + EPIPHANIES board prepend, CLAUDE.md dependency pin fix. +- **Merged since I started:** #436 (Aerial+ Rust crate, +2,693 LOC standalone), #437 (kanban + soa_view in contract, +4 files), #438 (DOLCE projector). #439 open (kanban Phase 2: lifecycle DAG + ExecTarget). +- **What is verified UNCHANGED in code post-rebase:** + - `mod.rs:450 OdooConfidence = {Curated, Extracted, Conjecture}` — same 3 variants + - 66 `pub const OdooEntity` declarations across 15 lanes — same count + - `ProvenanceTier` / `discovery_origin` — still in ZERO `.rs` files +- **What is NEW on this branch via the rebase:** the 4 canonical `cognitive-risc-*` specs now live at `.claude/specs/` (they came in via main commits `d1635dbe`/`93ac0463`/`a16d0f41`/`45276eb3`). +- **Stale points in my own committed docs (NOT yet fixed):** + - Reconciliation §8 says specs live only on `origin/claude/cognitive-risc-core-9PMW8` — WRONG since the rebase. They are on main and on this branch. + - Survival dossier "Concrete next moves" §7 ("ARM-discovery Wave 1 = D-ARM-1 + D-ARM-2") — STALE. Wave 1 went D-ARM-13/14 (Aerial+ port + DOLCE projector) per #436/#438. + +--- + +## 1. The 8 options (for council to rank) + +Each option lists: **What** · **Why** · **Cost** · **Risk** · **Reversibility**. + +### Option A — Retire the branch as-is + +- **What:** Do nothing further. PR #435 is already merged; the 4 doc commits are on origin. Close out. +- **Why:** Sometimes the right move is to ship the body of work and walk away. Anything else risks scope creep and another round of "you fixed something I didn't ask for." +- **Cost:** Zero. +- **Risk:** Stale docs stay stale; the reconciliation §8 + dossier §7 wrongness rots in the knowledge base. Future sessions read it as ground truth. +- **Reversibility:** Trivial — a follow-up session can fix the rot later, but it'll cost a re-read of the canonical specs. + +### Option B — Fix the 2 stale citations in my own docs, push, retire branch + +- **What:** One commit: correct reconciliation §8 (specs are on main, list paths under `.claude/specs/`) and dossier §7 (Wave 1 went D-ARM-13/14, not D-ARM-1/2; restate the actual queue). +- **Why:** Cheap, honest, fixes lies-on-disk that I committed before the rebase. +- **Cost:** ~30 min, 1 commit, no code touched. +- **Risk:** None. +- **Reversibility:** Trivial. + +### Option C — Lift ProvenanceTier + DiscoveryOrigin into `lance-graph-contract` (close OD-1/2/3) + +- **What:** Implement the council-blessed canonical-set decision: add `lance_graph_contract::provenance::{ProvenanceTier, DiscoveryOrigin}` with the canonical `{Curated, Extracted, ArmDiscovered, Ratified}` per core spec + widen proposer-id to 6 bits (or move whole byte to u16) per core spec recommendation. Add a `Conjecture` aux variant or treat `Derived` as a separate orthogonal field per the wikidata spec's "reasoning store is orthogonal" stance. Wire the Aerial crate's `CandidateRule` to it. +- **Why:** #436 PR note explicitly flags this as the missing piece ("no lance-graph-contract carriers were added yet — `rule`/`translator` are the local seam until D-ARM-1/2 land the shared `CandidateRule`/`Proposer`/`ProvenanceTier` types"). My reconciliation doc identified this as the highest-leverage decision. The core spec is now on-branch so the citation is direct. +- **Cost:** 1 new contract module (~100 LOC), ~5 unit tests, wire the Aerial crate (1 small change in `lance-graph-arm-discovery`), update mod.rs:450 OdooConfidence to either alias or wrap the new ProvenanceTier (decision: which way the migration runs). Maybe 2-3 hours. +- **Risk:** Touches the contract crate (cross-repo blast radius — n8n-rs and crewai-rust depend on it). Decision OD-2 (Conjecture vs Derived as orthogonal axis) is non-trivial and load-bearing. +- **Reversibility:** Contract additions are additive; reversal is a follow-up PR. The byte-layout choice is harder to reverse once the WAL hardens, but the WAL is NOT YET hardened (verified). + +### Option D — Help land #439 + +- **What:** Pull #439 into context. Review CodeRabbit comments if any, see whether the lifecycle DAG / ExecTarget design needs another savant pass. If clean, push toward merge. If not, raise the concerns. +- **Why:** It's the open scaffold for the Rubicon kanban machinery the rest of the architecture rests on. Held-up PRs are technical debt that compounds. +- **Cost:** Variable. If clean: ~30 min review pass. If issues: hours. +- **Risk:** Low. Review effort is bounded. +- **Reversibility:** N/A — review is read-only until I push code. + +### Option E — D-ARM-1 + D-ARM-2 only (subset of C, no widening) + +- **What:** Just lift `ProvenanceTier` + `Proposer` + `CandidateRule` into `lance-graph-contract` with the existing committed-plan value-set (2-bit tier, 3-bit proposer-id per #436's local types or per my plan §7.2). Do NOT make the widening decision; do NOT touch OD-2. Just unblock D-ARM-3+ which were waiting on the carriers. +- **Why:** Smallest unblocker; defers the contested decisions to a separate council convening. +- **Cost:** Smaller than C, ~1 hour. +- **Risk:** Bakes in the over-subscribed widths my reconciliation doc flagged as wrong. The reviewer who reads the §7 corrections will (correctly) ask why D-ARM-1 landed at the known-wrong width. +- **Reversibility:** Painful once shipped — the whole point of the reconciliation doc is that widening *after* the byte hardens is expensive. + +### Option F — Wikidata loader (the big integration the wikidata-hhtl-load spec describes) + +- **What:** Begin implementing the 115M-entity HHTL/CAM loader. 4 reduction levers + 2-pass streaming. The spec says ~38GB landing target. +- **Why:** Wikidata is the second domain the cognitive-risc N4 freeze test demands ("don't freeze SoA schema until ≥2 genuinely different domains have run through it"). Right now it's "Odoo's schema cosplaying as universal." +- **Cost:** Substantial. Multi-PR. Needs splat top-k codebook from jc (per #438), needs the Wikidata dump, needs basin sharding. +- **Risk:** Largest scope. Many open measurements (mask density, dedup rate, P279 fan-out — all explicitly flagged "untested" in the spec). +- **Reversibility:** Each PR can be reverted but the early architectural choices set the trajectory. + +### Option G — Chess bring-up test + +- **What:** Encode chess openings/methods/verbs as OWL/ttl, run proposers over it, see if GM-flavored legal candidates fall out of the same pipeline. Stockfish for ground truth. +- **Why:** The cognitive-risc-core spec calls this **the** falsifiable slice. It's tiny and decisive — does the universal SoA actually serve a second domain, or does chess need a column Odoo's SoA didn't anticipate? +- **Cost:** Bounded — OWL/ttl encoding is small, proposers exist (or scaffolded in #436), Stockfish is a subprocess. +- **Risk:** It might FALSIFY a load-bearing claim. That is actually the point of the test, but emotionally distinct from "another build-out PR." +- **Reversibility:** A failed test is a finding, not a regression. + +### Option H — Cleanup target/ (3.3G of 3.5G repo) + +- **What:** `cargo clean` in the workspace and the standalone crates. +- **Why:** Pure disk hygiene. The user just asked about disk space. +- **Cost:** Trivial. +- **Risk:** Next build is from scratch (slow). +- **Reversibility:** Trivial. + +--- + +## 2. Combinations the council should consider + +| Combo | Rationale | +|---|---| +| B + H | Smallest safe move: fix the rot, free 3.3G, walk away. | +| B + C | Fix the rot, then ship the byte the reconciliation doc was about — close the loop in one branch. | +| B + D + H | Fix rot, push #439 toward merge, free disk. Mostly review-only work. | +| B + C + D | Whole-branch closure: fix rot, ship the contract carrier, push #439. | +| B + C + G | Fix rot, ship the contract carrier, then run the chess bring-up to falsify or confirm the choices that just hardened. | +| C alone | The risky single-bet: change the byte grammar without first correcting the docs that name the wrong tier sets. Council should reject if so. | + +--- + +## 3. My pre-council bias (state it explicitly for the council to attack) + +I'm leaning **B + C** with **OD-2 = Derived as a separate orthogonal "reasoning provenance" field, NOT a tier value** (per wikidata spec's "orthogonal=beside not mixed in" stance), tier set = the canonical core-spec stable-4 `{Curated, Extracted, ArmDiscovered, Ratified}`, proposer-id = **6 bits (64 slots)** keeping the byte at u8 (cheaper, fits, matches the spec's stated alternative). + +**Why I'm biased here:** it's the path that takes my own analysis seriously. That is exactly the kind of bias the council exists to attack — "of course you want to ship YOUR analysis." + +**What I might be missing (where the council should poke hardest):** +- Is the canonical-stable-4 the RIGHT set, or is `Conjecture` (which is in code today) load-bearing in a way I haven't grokked? +- Is widening the byte right now actually safe given #437 just added `KanbanMove` (which is `Copy` and ≤16B and has a `const _` size assertion) — would the `discovery_origin` byte go INSIDE `KanbanMove` or alongside, and does either fit the assertion? +- Have I confused "what the spec says" with "what the team has converged on"? Specs are aspirational; main is reality. +- Is the chess bring-up (G) actually more urgent than the contract carrier (C)? The spec is emphatic that schema falsification is cheap on a board and expensive at domain 3-4. + +--- + +## 4. Council brief (the prompt the reviewers receive) + +The four reviewers are spawned in parallel with this prompt verbatim: + +> Read this options doc (`.claude/plans/post-438-integration-options-v1.md`) + my reconciliation doc (`.claude/knowledge/discovery-origin-provenance-reconciliation-v1.md`) + the four canonical specs (`.claude/specs/cognitive-risc-*.md`, `.claude/specs/faiss-homology-cam-pq.md`, `.claude/specs/wikidata-hhtl-load.md`). Also read PR #436/#437/#438 PR bodies via `mcp__github__pull_request_read`. +> +> Rank the 8 options + 6 combinations. **State the one you would execute if you had the keys.** Be brutally honest — call out anything where the author's stated bias (§3) is wrong, where they're cherry-picking the spec, where the easy path is the wrong path. If you would execute something NOT on the list, propose it. +> +> Hard constraints: 1) do not propose work that needs the WAL to harden; the WAL is OPEN. 2) do not propose work that requires the canonical specs to be reconciled with themselves first (e.g. the core/wikidata `Derived` vs `ArmDiscovered/Ratified` split) — name it as a blocker if you think it is one. 3) you may propose retiring the branch (Option A) if you think the right move is "stop doing things." +> +> Output: one section with your verdict, one with what you'd execute first this session, one with what you'd flag as the biggest unstated risk in the options as written. Under 400 words total. + +--- + +## 5. After the council — auto-resolve protocol + +1. Wait for all four reviewers to return. +2. If 3 of 4 agree on a single option or combo → execute it. +3. If 2/2 split → tiebreak by: highest reversibility, lowest cross-repo blast radius, smallest cost. Pick. +4. If 4 different verdicts → take the union of "fix the obvious stale citations" (Option B) and stop. Surface the 4 dissenting verdicts to the user. +5. If any reviewer rejects everything → that reviewer's reasoning is treated as a veto on the contested options; pick the union of the non-vetoed set. + +Execute. Commit. Push. Report. + +End of options doc. diff --git a/CLAUDE.md b/CLAUDE.md index 65e4547c..6a2b3aac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -940,14 +940,16 @@ cd crates/lance-graph-python && maturin develop ## Key Dependencies ```toml -arrow = "57" -datafusion = "51" -lance = "2" -lance-linalg = "2" +# Verified against Cargo.lock 2026-05-30. The lance family moves in lockstep at =6.0.0. +arrow = "58" +datafusion = "53" +lance = "=6.0.0" # exact-pinned: lancedb 0.29.0 transitively requires lance =6.0.0 +lance-linalg = "=6.0.0" +lancedb = "=0.29.0" ndarray = { path = "../../../ndarray" } # AdaWorldAPI fork, default, optional fallback nom = "7.1" snafu = "0.8" -deltalake = "0.30" # optional +deltalake = "0.32" # optional ``` ---