From 65056bf9c6f41bc2fa662ccd13f9a6c534da3e6f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 04:26:21 +0000 Subject: [PATCH 1/3] od-ontology: knowledge-transfer ruff #76 region grammar to Odoo transcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Carry ruff PR #76's region-grammar plane (Predicate::{DockedAt,TabOrder, OpensPopup} -> the six-region frame {top_bar,left_nav,center,right_panel, bottom_bar,popup}) into the odoo-rs transcode. #76 built it for C# WinForms (DockStyle/TabIndex/ContextMenuStrip); the furnace playbook §8.1 says Odoo XML arch tags map to regions "exactly like DockStyle" — this makes that concrete, grounded in the vendored real account views. - docs/knowledge/ODOO-REGION-GRAMMAR.md: the transfer. Odoo arch element -> region map (header->top_bar, searchpanel->left_nav, sheet/list/kanban/ notebook->center, chatter->right_panel, footer->bottom_bar, act_window target=new / dropdown -> popup); tab_order = arch DOM position (reuses odoo_views.rs's position-ordered scan); opens_popup's two Odoo spellings; the render equation (value half = view_mask WideFieldMask IS the per-screen local_mask); the three-edit recipe (Edit 1 vocab DONE on ruff main; Edits 2 harvester arm + 3 digest section = named [H]); anti-patterns. - data/nav/odoo_regions.conf: the sanctioned convention config (furnace output #2) — the arch-token -> region rows, consumed by ruff's own region= directive. Data, not hand-rolled Rust. - tests/region_grammar.rs: proof-by-execution — the conf parses through ruff_spo_triplet::parse (the real region= directive), the full token->region map is pinned (drift fuse), every region is one of the six canonical, and the six-region frame is fully reachable from Odoo arch. 3/3 green. No predicate mint (region tokens are layout, not concepts — two-axis refusal, playbook §3). Reuses the existing Klickweg wiring; no parallel structure. --- crates/od-ontology/tests/region_grammar.rs | 115 +++++++++++ data/nav/odoo_regions.conf | 37 ++++ docs/knowledge/ODOO-REGION-GRAMMAR.md | 214 +++++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 crates/od-ontology/tests/region_grammar.rs create mode 100644 data/nav/odoo_regions.conf create mode 100644 docs/knowledge/ODOO-REGION-GRAMMAR.md diff --git a/crates/od-ontology/tests/region_grammar.rs b/crates/od-ontology/tests/region_grammar.rs new file mode 100644 index 0000000..1f19669 --- /dev/null +++ b/crates/od-ontology/tests/region_grammar.rs @@ -0,0 +1,115 @@ +//! **Region-grammar config pin** — proof-by-execution for the knowledge +//! transfer of ruff PR #76's region grammar into the odoo-rs transcode. +//! +//! `docs/knowledge/ODOO-REGION-GRAMMAR.md` carries #76's six-region pattern +//! (`docked_at`/`tab_order`/`opens_popup` → `{top_bar, left_nav, center, +//! right_panel, bottom_bar, popup}`) onto Odoo's XML view arch. The mapping +//! lives as data — `data/nav/odoo_regions.conf` — consumed by ruff's own +//! `region=` directive (`ruff_spo_triplet::parse`). This test proves the +//! config PARSES through that real directive and produces the pinned +//! arch-token → region map, and that every region is one of the six +//! canonical names (a malformed row can never silently mis-dock). +//! +//! This is the config half of the three-edit recipe (Edit 1 vocab = DONE on +//! ruff main; this pins the convention config). Edits 2 (the ruff Odoo +//! `docked_at` harvester arm) + 3 (the `[regions]` digest section) are the +//! remaining [H] work named in the doc — a full arch→region harvest test +//! lands with the arm. + +use ruff_spo_triplet::parse; + +/// The six canonical regions (`exam_config.rs:36-37`, verbatim on ruff main). +const CANONICAL_REGIONS: &[&str] = &[ + "top_bar", + "left_nav", + "center", + "right_panel", + "bottom_bar", + "popup", +]; + +/// The committed Odoo arch-token → region convention config. +const ODOO_REGIONS_CONF: &str = include_str!("../../../data/nav/odoo_regions.conf"); + +/// The full pinned map (drift fuse — a hand-edit to the conf that changes a +/// mapping or adds/drops a token fails here loudly). +const EXPECTED: &[(&str, &str)] = &[ + // top_bar — top action/filter strip + ("header", "top_bar"), + ("search", "top_bar"), + ("filter", "top_bar"), + // left_nav — left category rail + ("searchpanel", "left_nav"), + // center — main body / primary data view + ("sheet", "center"), + ("form", "center"), + ("group", "center"), + ("field", "center"), + ("separator", "center"), + ("list", "center"), + ("tree", "center"), + ("kanban", "center"), + ("notebook", "center"), + ("page", "center"), + // right_panel — chatter / activity + ("chatter", "right_panel"), + // bottom_bar — wizard dialog action bar + ("footer", "bottom_bar"), + // popup — dropdown / cog / act_window target="new" + ("action_menu", "popup"), +]; + +#[test] +fn odoo_regions_conf_parses_through_the_ruff_region_directive() { + let cfg = parse(ODOO_REGIONS_CONF); + + // Every pinned (token -> region) pair is present, exactly once, verbatim. + for (tok, region) in EXPECTED { + let hits: Vec<&String> = cfg + .regions + .iter() + .filter(|(t, _)| t == tok) + .map(|(_, r)| r) + .collect(); + assert_eq!( + hits.len(), + 1, + "dock token {tok:?} must map exactly once (found {})", + hits.len() + ); + assert_eq!(hits[0], region, "dock token {tok:?} maps to the wrong region"); + } + + // No extra rows leaked in (comments/blanks dropped, malformed rows dropped + // by the `:`-split — so the parsed count is exactly the pinned count). + assert_eq!( + cfg.regions.len(), + EXPECTED.len(), + "odoo_regions.conf has {} region rows; pin expects {}", + cfg.regions.len(), + EXPECTED.len() + ); +} + +#[test] +fn every_mapped_region_is_one_of_the_six_canonical() { + let cfg = parse(ODOO_REGIONS_CONF); + for (tok, region) in &cfg.regions { + assert!( + CANONICAL_REGIONS.contains(®ion.as_str()), + "token {tok:?} docks to non-canonical region {region:?} (allowed: {CANONICAL_REGIONS:?})" + ); + } +} + +#[test] +fn all_six_canonical_regions_are_covered_by_at_least_one_token() { + let cfg = parse(ODOO_REGIONS_CONF); + for region in CANONICAL_REGIONS { + assert!( + cfg.regions.iter().any(|(_, r)| r == region), + "no Odoo arch token maps to canonical region {region:?} — the six-region \ + frame must be fully reachable from Odoo arch" + ); + } +} diff --git a/data/nav/odoo_regions.conf b/data/nav/odoo_regions.conf new file mode 100644 index 0000000..0e59020 --- /dev/null +++ b/data/nav/odoo_regions.conf @@ -0,0 +1,37 @@ +# Odoo arch-token -> six-region map (furnace convention config, sanctioned +# output #2). Knowledge transfer of ruff PR #76's region grammar; see +# docs/knowledge/ODOO-REGION-GRAMMAR.md for the grounding and the three-edit +# recipe. Consumed by ruff exam_config's `region=` directive +# (ruff_spo_triplet/src/exam_config.rs). Format: region=:. +# The six canonical regions: top_bar / left_nav / center / right_panel / +# bottom_bar / popup. Region names are free strings FROM config — edit here, +# never in Rust. A row with no `:` is dropped (malformed can't mis-dock). + +# top_bar — the top action/filter strip (DockStyle.Top analogue) +region=header:top_bar +region=search:top_bar +region=filter:top_bar + +# left_nav — the left category/filter rail (DockStyle.Left) +region=searchpanel:left_nav + +# center — the main record body / primary data view (DockStyle.Fill) +region=sheet:center +region=form:center +region=group:center +region=field:center +region=separator:center +region=list:center +region=tree:center +region=kanban:center +region=notebook:center +region=page:center + +# right_panel — the messaging/activity side panel (DockStyle.Right) +region=chatter:right_panel + +# bottom_bar — wizard dialog action bar (DockStyle.Bottom) +region=footer:bottom_bar + +# popup — dropdown / cog menu / act_window target="new" wizard +region=action_menu:popup diff --git a/docs/knowledge/ODOO-REGION-GRAMMAR.md b/docs/knowledge/ODOO-REGION-GRAMMAR.md new file mode 100644 index 0000000..3f289c3 --- /dev/null +++ b/docs/knowledge/ODOO-REGION-GRAMMAR.md @@ -0,0 +1,214 @@ +# Odoo region grammar — the render half of the structure oracle + +> **Knowledge transfer of ruff PR #76** (region-grammar plane, merged to +> `ruff` `main` @ `a0eb4988` / tip `48f2374`) **into the odoo-rs transcode.** +> PR #76 built the pattern for C# WinForms (`DockStyle`/`TabIndex`/ +> `ContextMenuStrip`); the furnace playbook §8.1 says *"Odoo XML arch tags +> (`form`/`tree`/`kanban`) map to regions exactly like `DockStyle`."* This doc +> makes that mapping concrete and grounded, reusing the **same closed-vocab +> predicates** — no new predicate, no hand-rolled layout. +> +> **READ BY:** any odoo-rs session touching view rendering, the Klickweg +> render skin, or the six-region frame. Companion to +> `docs/knowledge/RUFF-SPO-SURFACE.md` (the predicate census) and the +> `klickweg_parity.rs` structure-oracle tests. +> **Cross-ref:** ruff `.claude/knowledge/consumer-transcode-furnace-playbook.md` +> §6 (the render side + render equation) and §8.1 (odoo portability row); +> ruff `crates/ruff_spo_triplet/src/{exam_config,nav_digest}.rs` (the +> `region=` directive + `[regions]` digest section). +> **Status:** KNOWLEDGE-TRANSFER — the vocab exists on ruff main (proof +> below); the Odoo harvester arm + odoo-rs digest section are the two +> remaining *three-edit-recipe* edits (§4), scoped but not yet built. + +______________________________________________________________________ + +## 0. The pattern in one sentence (what #76 established) + +**Every legacy screen is a declarative layout in disguise; harvest *where a +control docks* as a fact, then re-render it into ONE universal six-region +frame — never emulate the widget tree.** Three closed-vocab predicates carry +it, and a `region=` config (data, not Rust) names the mapping: + +``` +{ top_bar, left_nav, center, right_panel, bottom_bar, popup } +``` + +**Proof-of-read (ruff main `48f2374`):** +- `crates/ruff_spo_triplet/src/exam_config.rs:36-37` — the six canonical + region names, verbatim: `top_bar / left_nav / right_panel / bottom_bar / + center / popup`. +- `crates/ruff_spo_triplet/src/triple.rs:1159` — + `fn predicate_count_locked_at_76()` (was 73; #76 added the three below). +- `crates/ruff_spo_triplet/src/nav_digest.rs:165,172,179` — the digest folds + `docked_at` → region, `tab_order` → intra-region order, `opens_popup` → + `→popup` suffix; unmapped dock tokens land in `unmapped:` (never + dropped). + +The convention (playbook §6): `predicate → (region, order, interaction)`: + +| Predicate (wire) | `Predicate` variant | Carries | WinForms source (#76) | **Odoo source (this doc)** | +|---|---|---|---|---| +| `docked_at` | `DockedAt` | **region** | `Dock = DockStyle.X` | **XML arch element** (see §2) | +| `tab_order` | `TabOrder` | **order within region** | `TabIndex` literal | **DOM position** in the arch | +| `opens_popup` | `OpensPopup` | **popup interaction** | `ContextMenuStrip = m` | **`act_window target="new"`** / dropdown (see §3) | + +______________________________________________________________________ + +## 1. Why odoo-rs already has half of this + +The Klickweg arc (`HANDOVER-2026-07-08-polyglot-transpiler.md` §2) closed the +**structure oracle** — but only its *value/field-set* half: + +- `ruff_python_spo::odoo_views.rs` harvests **which fields** a view projects + (`` inside `arch`) → `WideFieldMask` (the "does the Rust + show the same fields?" question). +- `crates/od-ontology/src/view_mask.rs` is the local hop-aware refinement. + +That answers *what is on the screen*. Region grammar answers the orthogonal +*where each thing sits* — the **render** half of the same structure oracle +(playbook §2 "structure parity" + §6 "the render side"). Same harvest source +(the view `arch`), same closed vocab, disjoint question. Together they are the +diverse-redundant structure witness; neither replaces the value oracle +(PostgreSQL rows). + +______________________________________________________________________ + +## 2. The Odoo-arch → six-region map (grounded in the vendored `account` views) + +Dock tokens are the **Odoo arch element name** (or a `class=`-qualified +variant); the region is assigned by config (§5), never hardcoded in Rust. +Grounded in the real arch tags present in `data/nav/*.xml` +(`account_account_views.xml` + `account_analytic_line_views.xml`): `form`, +`sheet`, `group`, `field`, `notebook`, `page`, `list`, `kanban`, `search`, +`filter`, `searchpanel`, `button`, `separator`, `div`. + +| Odoo arch element | dock token | → region | Rationale | +|---|---|---|---| +| `
` (form action bar + ``) | `header` | **top_bar** | top workflow-button + status strip — the `DockStyle.Top` analogue | +| `` root, `` | `search` | **top_bar** | the filter/search bar above a list/kanban | +| `` | `searchpanel` | **left_nav** | Odoo's left category-filter rail (`DockStyle.Left`) | +| ``, `
` body, ``, ``, `` | `sheet` | **center** | the main record body (`DockStyle.Fill`) | +| ``/``, `` | `list` / `kanban` | **center** | the primary data view | +| `` / `` | `notebook` | **center** | tabbed sub-sections *within* center (order = page index) | +| chatter (`
`, `` in 17+) | `chatter` | **right_panel** | messaging/activity side panel (`DockStyle.Right`) | +| `