diff --git a/docs/DISCOVERY-MAP.md b/docs/DISCOVERY-MAP.md index ee6dd8b..24fdb96 100644 --- a/docs/DISCOVERY-MAP.md +++ b/docs/DISCOVERY-MAP.md @@ -225,7 +225,7 @@ two halves of a cell. ADR‑026 names the cascade that ties them. | D‑RECIPE‑BITMASK | OGAR = Open Graph **Active Record**: the canonical recipe IS the AR lifecycle protocol; a class stores it once + a per‑class override **bitmask** (set=override, clear=inherited default, fall‑through per the zero‑fallback ladder) + the genuine deltas → thins the "impossible 15%" behavioural leftover toward ~7% for best‑shaped (AR‑canonical) consumers. Guards: zero per‑class payload (recipe IS the spec); computes: shape + `Depends.paths` delta. Guards: bitmask=register not VSA (`I‑VSA‑IDENTITIES` T0); redundant = content‑hash‑equal‑to‑default (lossless‑DO §1); slots RESERVE‑DON'T‑RECLAIM (`I‑LEGACY‑API‑FEATURE‑GATED`). Odoo (Python, UPPER bound) RUN: guard arm 47→1 full‑collapse, compute arm 101 distinct of 141 resolved, **45.7% collapse / 54.3% leftover** — REFUTES "Odoo→7%", CONFIRMS the Rails‑AR scoping | H | EPIPHANY | E‑RECIPE‑BITMASK · F15 · `odoo-rs tests/recipe_redundancy_probe.rs` | D‑VOCAB, D‑HIRO‑DO, D‑ACTION | | D‑RECIPE‑BITMASK‑CHAIN | the **inheritance axis** of the recipe‑bitmask: a derived ClassView is built by chaining its base `LazyLock` constants + its own delta (`classid → ClassView` compositional; chain = MRO; #533 `resolve_overrides` = order). Fixes two things — "out‑of‑slice" dissolves (the base is a registry constant, not a slice dep) and "redundant = content‑hash‑equal" becomes **referential identity** (the inherited part IS the shared cached constant, pointer‑identical, no copy to drift). Orthogonal to the 3×4 carving (value/registry‑side vs address/centroid‑side) → **3×4 stands, no re‑carve**. Chain‑order falsifier = F1; acyclic (DAG, topo order). MEASURED (full manifest, 388 classes / 166 edges / 3328 methods): naive 4215 vs chained 3328 → **21.0% collapse / 22.7% behavioural**; STACKS with the within‑class 54.3%; lower bound (shallow mixin harvest) | G (measured) / H (LazyLock impl) | EPIPHANY | E‑RECIPE‑BITMASK‑CHAIN · F16 · `odoo-rs tests/recipe_chaining_collapse.rs` | D‑RECIPE‑BITMASK, D‑VOCAB, F1 | | D‑FUNCTION‑CATALOG | a consumer function = `(verb, criteria)`: the verbs (filter=query engine · project=compute/recompute DAG · map=deterministic lookup · reduce=semirings) are SHIPPED/`[G]`; the "catalog" is of CRITERIA (selection-condition + params, grounded on a domain ontology), EXTRACTED not authored, keyed by canonical concept id (E‑RECIPE‑LABEL‑DTO). DTO-guided landing zone = a criteria DTO selecting+parameterizing a shipped verb; lands on `lance-graph-contract::action` (actions_for/OgarResolver) + `ogar-render-askama` artifact_kinds + `ogar-vocab`. **map ≠ CAM‑PQ** (Kontenerkennung = relational rule + precedent; Test‑0 register, not ANN); **CRUD = generic lifecycle + extracted criteria**, not hand-rolled. Gate: extracted criterion must round‑trip | H | EPIPHANY | E‑FUNCTION‑CATALOG · F17 | D‑VOCAB, D‑ACTIONHANDLER‑RESOLVER, D‑RECIPE‑BITMASK | -| D‑ACCIDENTAL‑IMPERATIVE | the hand-rolled residue = **accidentally-imperative** (AR verbs on AR targets, no declarative home — recoverable; Odoo `@api.depends` vs Rails `before_save{lines.sum}` proves it is source-*expressiveness* not logic complexity) ∪ **essentially-foreign** (the only true escape). The body pass **TRIAGES** to `(target classid, verb-class, order-signature)` — NOT decompile; residue becomes legible; lands at a coarse catalog tier + point-to-body (lossless-DO §1). "random orders" = the **round-trip-order-free** recover/preserve gate. Three tiers: clean `(verb,criteria)` · coarse `(target,verb-class)` · foreign | H | EPIPHANY | E‑ACCIDENTAL‑IMPERATIVE · F17 · ruff writes/calls capture SHIPPED 2026‑06‑30 (AdaWorldAPI/ruff#38: `Function.writes`/`calls` + `writes_field`/`calls` predicates + `ruff_ruby_spo` body walker) → F17 RUNNABLE, body‑triage probe is the next deliverable; ratio still unmeasured | D‑FUNCTION‑CATALOG, D‑HIRO‑DO | +| D‑ACCIDENTAL‑IMPERATIVE | the hand-rolled residue = **accidentally-imperative** (AR verbs on AR targets, no declarative home — recoverable; Odoo `@api.depends` vs Rails `before_save{lines.sum}` proves it is source-*expressiveness* not logic complexity) ∪ **essentially-foreign** (the only true escape). The body pass **TRIAGES** to `(target classid, verb-class, order-signature)` — NOT decompile; residue becomes legible; lands at a coarse catalog tier + point-to-body (lossless-DO §1). "random orders" = the **round-trip-order-free** recover/preserve gate. Three tiers: clean `(verb,criteria)` · coarse `(target,verb-class)` · foreign | G | EPIPHANY | E‑ACCIDENTAL‑IMPERATIVE · F17 · ruff writes/calls capture SHIPPED 2026‑06‑30 (AdaWorldAPI/ruff#38: `Function.writes`/`calls` + `writes_field`/`calls` predicates + `ruff_ruby_spo` body walker) → F17 RUNNABLE, body‑triage probe is the next deliverable; ratio still unmeasured. **REGRADED `[H]→[G]` 2026‑07‑06 (F17 both legs measured):** Odoo control leg 94.9% order‑free recoverable (odoo‑rs `body_triage_probe`); **Rails TEST leg run** (openproject‑nexgen‑rs `crates/ruff_openproject/tests/body_triage_probe.rs`, Redmine corpus, drift‑fused): 114 lifecycle hooks, behavioural arm 62 — coarse triage **93.5% PASS**, recipe‑codebook refinement (ruff #45 `fuzzy‑recipe‑codebook` + J1 `writes_if_blank`, ruff #47) → **Cascade 46 · Compute 12 · Default 2 · Normalize 1 · WriteRaise 0 · Compensate 1 = 98.4% recipe‑recoverable, exactly 1 essentially‑imperative core**. Both legs land on the predicted shape: the residue is source‑expressiveness, not logic complexity; the essential tail is single‑digit | D‑FUNCTION‑CATALOG, D‑HIRO‑DO | ### 2.9 Domain instances (universality witnesses) diff --git a/docs/INTEGRATION-MAP.md b/docs/INTEGRATION-MAP.md index 586ae98..69a127c 100644 --- a/docs/INTEGRATION-MAP.md +++ b/docs/INTEGRATION-MAP.md @@ -395,7 +395,7 @@ R2 — not D.) | F14 | **Wide-model render** | render `account.move` through `ClassView` with full presence (**>64 is the load-bearing bound; 109 declarations counted** in account_move.py, Odoo 17) | **gated: cannot RUN until Phase B** wires the field-enum into `RegistryClassView`; once runnable, fails by construction (FieldMask u64, L0b) until presence exceeds u64 — **Track X7 is the named path** | | F15 | **AR-recipe collapse** (`PROBE-OGAR-AR-RECIPE-COLLAPSE`) | measure how much of a consumer's lifted behavioural arm folds to the shared ActiveRecord-lifecycle recipe + per-class override bitmask vs genuine per-class leftover. **Odoo upper-bound RUN** (`odoo-rs tests/recipe_redundancy_probe.rs`, default build, slice_2): guard arm 47→1 full-collapse, compute arm 101 distinct of 141 resolved, **45.7% collapse / 54.3% leftover**; clean run is Rails/OpenProject where `ruff_ruby_spo` captures `callbacks` as first-class `Model` data | D‑RECIPE‑BITMASK `[H]→[G]` when the Rails-AR clean run lands near the ~7% leftover target; the Odoo bound already refutes the strong "Odoo→7%" reading | | F16 | **Constructor-chaining collapse** (`PROBE-OGAR-CHAINING-COLLAPSE`) | the inheritance axis: naive flatten (own + inherited copies) vs chained (stored once at the base `LazyLock` constant) over a consumer's `inherits_from` DAG. **Odoo RUN** (`odoo-rs tests/recipe_chaining_collapse.rs`, full manifest, 388 classes / 166 edges / 3328 methods): naive 4215 vs chained 3328 → **21.0% collapse / 22.7% behavioural**; top base `mail_activity_mixin` (324 copies). Chain-order correctness = F1; acyclic DAG | D‑RECIPE‑BITMASK‑CHAIN `[H]→[G]` (measured); confirms chaining resolves F15's "out-of-slice" upper bound — the real leftover sits below it; orthogonal to F15, stacks. Rails leg (concerns captured) should exceed Odoo's collapse | -| F17 | **Body-triage / accidentally-imperative ratio** (`PROBE-OGAR-BODY-TRIAGE`) | run the body pass over real hooks → `(target classid, verb-class, order-signature)` per hook → **round-trip-order-free** each coarse group (does the order-free `(verb,criteria)` reproduce the source output?). PASS-rate = the accidentally-imperative (recoverable) fraction; FAIL-rate = the order-dependent / essentially-foreign (preserve/escape) tail. Control = Odoo `_compute_*` (already declarative); test = Rails `before_*`/`after_*`. **Odoo control-leg RUN** (2026-07-06, `odoo-rs tests/body_triage_probe.rs`, default build, slice_2, run verified green, numbers pinned as drift-fuses in the probe): 393 lifecycle hooks; verb-classes guard-pure 44 / compute-pure 298 / self-feedback 30 / write+raise 2 / read-only 15 / no-facts 4; headline (behavioural arm = guards + computes: 357 hooks, 354 resolved): **PASS 336 (94.9%)** accidentally imperative, order-free recoverable / **FAIL 18 (5.1%)** order-dependent tail — decomposing into self-feedback (read-modify-write inside one hook; conservative, includes `@api.depends` extractor artifacts, so true tail ≤ measured) + write+raise (partial-write escape order); 3 unresolved (no facts captured) excluded; context: onchange 15/17 resolved FAIL — cooperative loops are the genuinely order-dependent shape, as predicted; cross-hook order NOT counted (recompute-DAG Kahn-orderability re-asserted inside the probe); method = static order-signature from harvested facts — writes = inverted `emitted_by` (Odoo's declarative `compute=` target; the ledger note below, "Python frontend leaves them empty", is exactly why the declarative target is used), reads = `reads_field`, raise = `raises` | D‑ACCIDENTAL‑IMPERATIVE / D‑FUNCTION‑CATALOG `[H]→[G]`. ~~**Gated on a ruff extension:** capture writes/calls per function (today: reads/raises/traverses only) so "calls update on X" is extractable~~ **Prerequisite SHIPPED 2026-06-30** (AdaWorldAPI/ruff @ `claude/odoo-rs-transcode-lf8ya5`, commit `dd70588`): `ruff_spo_triplet::Function` gains `writes` (self.= setters → `writes_field`, Authoritative) + `calls` (lifecycle-mutator dispatches as "receiver.method" → `calls`, Inferred); the `ruff_ruby_spo` body walker populates both from the Rails AST (closed `AR_MUTATORS` set). "calls update on X" is now extractable → **F17 is RUNNABLE** (probe still to author/run). Python frontend leaves them empty (Odoo write target already declarative via `Field::emitted_by`). **Odoo CONTROL leg MEASURED 2026-07-06** (94.9% order-free recoverable — consistent with "already declarative", the control behaving as a control should); D‑ACCIDENTAL‑IMPERATIVE stays `[H]` until the Rails TEST leg (`before_*`/`after_*` via `ruff_ruby_spo` writes/calls) runs | +| F17 | **Body-triage / accidentally-imperative ratio** (`PROBE-OGAR-BODY-TRIAGE`) | run the body pass over real hooks → `(target classid, verb-class, order-signature)` per hook → **round-trip-order-free** each coarse group (does the order-free `(verb,criteria)` reproduce the source output?). PASS-rate = the accidentally-imperative (recoverable) fraction; FAIL-rate = the order-dependent / essentially-foreign (preserve/escape) tail. Control = Odoo `_compute_*` (already declarative); test = Rails `before_*`/`after_*`. **Odoo control-leg RUN** (2026-07-06, `odoo-rs tests/body_triage_probe.rs`, default build, slice_2, run verified green, numbers pinned as drift-fuses in the probe): 393 lifecycle hooks; verb-classes guard-pure 44 / compute-pure 298 / self-feedback 30 / write+raise 2 / read-only 15 / no-facts 4; headline (behavioural arm = guards + computes: 357 hooks, 354 resolved): **PASS 336 (94.9%)** accidentally imperative, order-free recoverable / **FAIL 18 (5.1%)** order-dependent tail — decomposing into self-feedback (read-modify-write inside one hook; conservative, includes `@api.depends` extractor artifacts, so true tail ≤ measured) + write+raise (partial-write escape order); 3 unresolved (no facts captured) excluded; context: onchange 15/17 resolved FAIL — cooperative loops are the genuinely order-dependent shape, as predicted; cross-hook order NOT counted (recompute-DAG Kahn-orderability re-asserted inside the probe); method = static order-signature from harvested facts — writes = inverted `emitted_by` (Odoo's declarative `compute=` target; the ledger note below, "Python frontend leaves them empty", is exactly why the declarative target is used), reads = `reads_field`, raise = `raises` | D‑ACCIDENTAL‑IMPERATIVE / D‑FUNCTION‑CATALOG `[H]→[G]`. ~~**Gated on a ruff extension:** capture writes/calls per function (today: reads/raises/traverses only) so "calls update on X" is extractable~~ **Prerequisite SHIPPED 2026-06-30** (AdaWorldAPI/ruff @ `claude/odoo-rs-transcode-lf8ya5`, commit `dd70588`): `ruff_spo_triplet::Function` gains `writes` (self.= setters → `writes_field`, Authoritative) + `calls` (lifecycle-mutator dispatches as "receiver.method" → `calls`, Inferred); the `ruff_ruby_spo` body walker populates both from the Rails AST (closed `AR_MUTATORS` set). "calls update on X" is now extractable → **F17 is RUNNABLE** (probe still to author/run). Python frontend leaves them empty (Odoo write target already declarative via `Field::emitted_by`). **Odoo CONTROL leg MEASURED 2026-07-06** (94.9% order-free recoverable — consistent with "already declarative", the control behaving as a control should); D‑ACCIDENTAL‑IMPERATIVE stays `[H]` until the Rails TEST leg (`before_*`/`after_*` via `ruff_ruby_spo` writes/calls) runs. **Rails TEST leg MEASURED 2026-07-06** (openproject-nexgen-rs `crates/ruff_openproject/tests/body_triage_probe.rs`, Redmine corpus incl. `Model::helpers` private-visibility split + one-Callback-per-symbol fixes, numbers pinned as drift-fuses): 114 lifecycle hooks — coarse verb-classes blocks 8 / no-facts 43 / self-feedback 3 / write+raise 1 / guard-pure 0 / compute-pure 58 / read-only 1; behavioural arm 62; coarse **PASS 93.5%**; the FAIL tail was then rolled through the recipe codebook (ruff #45; J1 `writes_if_blank` ruff #47 splits Default from Normalize) → **Cascade 46 / Compute 12 / Default 2 / Normalize 1 / WriteRaise 0 / Compensate 1 = 98.4% recipe-recoverable, exactly 1 essential imperative core**. Both legs measured → **D‑ACCIDENTAL‑IMPERATIVE REGRADED `[H]→[G]`** (dated note on the DISCOVERY-MAP row) | (F3 already carries the D‑QUANTGATE pre/post-quantization contrast. F10–F13 restore the DISCOVERY-MAP §4.2 jc×hpc floor that v1.0 omitted —