diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 178686d..93061e3 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -159,6 +159,19 @@ _[Original REJECTED verdict retained below, append-only, as the cautionary recor **GATE (mint fence):** no verb-codebook row is allocated until the kind/verb A/B falsifier is green; **naming a stratum is never mint authorization** (E-VENDOR-DELTA doctrine: spec it, don't fake it). If a route classifier is ever built, it is built in **ruff** on newly-harvested discriminant facts (spec-to-ruff wishlist: HTTP verb / writes / return-shape), phase-named per OGAR-AS-IR — classifier = front-end analysis pass, verb rows = symbol-table entries, recipes = lowering passes — never op-side. **Cross-ref:** `docs/DISCOVERY-MAP.md` D-ROUTE-KIND-VERB-STRATA (twin); E-ONE-MASK-THREE-PORTS (verb rows argued-for, mint parked); E-AR-DIRECT-SDK (DO-arm landing zone: ActionHandler + unified adapters — placement unchanged by this entry); E-OGAR-CONVERGENCE-SHAPE open seam #2 (ActionDef↔UnifiedStep); op-nexgen `.claude/handovers/2026-07-05-CAPSTONE-ar-shape-convergence.md` C5 + `2026-07-05-ogar-v3-consumer-migration-plan.md` §1/§6; op-nexgen `crates/ruff_python_dto_check/README.md` (the parked sqlx delta). +## 2026-07-06 — E-BODY-TRIAGE-ODOO-CONTROL-1 — F17 Odoo control leg measured: 94.9% order-free recoverable + +**Status:** FINDING (measured; drift-fused in the odoo-rs probe) + +**The measurement** (2026-07-06, `odoo-rs crates/od-ontology/tests/body_triage_probe.rs`, default build, slice_2 corpus, run verified green; the headline numbers are pinned as drift-fuses inside the probe): 393 lifecycle hooks triaged into verb-classes — guard-pure 44, compute-pure 298, self-feedback 30, write+raise 2, read-only 15, no-facts 4. Headline over the behavioural arm (guards + computes: 357 hooks, 354 resolved): **PASS 336 (94.9%)** — accidentally imperative, order-free recoverable — vs **FAIL 18 (5.1%)**, the order-dependent tail; 3 unresolved (no facts captured) excluded. + +**The tail decomposes** into self-feedback (read-modify-write inside one hook) + write+raise (partial-write escape order). **Conservative-bound caveat:** the self-feedback bucket includes `@api.depends` extractor artifacts, so the true order-dependent tail is ≤ the measured 5.1%. Context: onchange 15/17 resolved FAIL — cooperative loops are the genuinely order-dependent shape, as predicted. Cross-hook order is NOT counted here; recompute-DAG Kahn-orderability is re-asserted inside the probe. + +**Method:** static order-signature from harvested facts — writes = inverted `emitted_by` (Odoo's declarative `compute=` target; the F17 ledger note "Python frontend leaves writes/calls empty" is exactly why the declarative target is used), reads = `reads_field`, raise = `raises`. + +**What it means for F17:** the Odoo CONTROL leg is measured and behaves as a control should — 94.9% recoverable is consistent with "already declarative". D‑ACCIDENTAL‑IMPERATIVE stays **[H]** until the Rails TEST leg (`before_*`/`after_*` via `ruff_ruby_spo` writes/calls) runs — that is the remaining [H] gate. + +**Cross-ref:** INTEGRATION-MAP F17 row (amended in place with the Odoo control-leg RUN annotation, same session), F15/F16 Odoo RUNs (recipe collapse / chaining collapse — the sibling probes on the same corpus). --- diff --git a/docs/INTEGRATION-MAP.md b/docs/INTEGRATION-MAP.md index 2aa5e43..586ae98 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_*` | 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`) | +| 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 | (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 —