diff --git a/PROGRESS.md b/PROGRESS.md index 3a7d064e3..8a88d2a92 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -3,9 +3,7 @@ > **Rule:** PROGRESS.md is a thin status file. No specs here — specs live in `docs/` and `prompts/`. Keep this file under 80 lines. ## Current State - **Gate:** `./scripts/run_RosettaStones.sh` — S190 fleet: 116/157 PASS, 4 ALL GREEN (BR,MO,RL,WI). 21 buildings. 9-gate system. - | PFX | EL | GATES | Notes | |-----|----|-------|-------| | BR | 33 | 9/9 | ALL GREEN | @@ -18,86 +16,65 @@ **Pipeline:** 11 stages. 77 verbs. 7403 products (ERP.db). 4-DB architecture. -## OPEN — real work, not yet done (check these before starting a new session) +⏸ **PUSH PAUSE in effect (2026-07-11, until lifted)** — commit locally, verify on localhost, do +not push/open a PR unless the user explicitly lifts it for that session. `CLAUDE.md` §⏸ PUSH PAUSE. -### 🔀 CURRENTLY JUGGLED (user runs these concurrently across terminals, shuts down/resumes cold — read this -### list FIRST, don't ask the user to restate status, it's kept current here) -- `prompts/RESUME_HR_BIM_ASSET.md` §2026-07-06c — camera-POV done (#674 `77f41b9`). Open: A/B/C bugs + E decision. -- `prompts/RESUME_WORLD_HISTORY_DEDUP_RESTORE.md` §2026-07-06 — Viewer Pt2 + Modeller Ph1-2 done, open: G6, Ph3, Pt1 parked. -- `prompts/PILL_DRAWER_REORGANIZATION.md` §2026-07-06 — done (#673 `953d1e4`). Open: first-touch flicker. +## OPEN — to be assigned to sessions (user dispatches from this list, check before starting cold) +- `prompts/RESUME_HR_BIM_ASSET.md` §2026-07-06c — A/B/C bugs + E decision. +- `prompts/RESUME_WORLD_HISTORY_DEDUP_RESTORE.md` §2026-07-06 — G6, Ph3, Pt1 parked. +- `prompts/PILL_DRAWER_REORGANIZATION.md` — first-touch flicker. - `prompts/OPEN_BUTTON_IFC_BCF_MERGE.md` — not started. -- **DiscWalk containment (Bug A) — RESOLVED+VERIFIED 2026-07-12 (5-axis proof, DX+SC; Terminal honestly - unmeasurable, data gap). Bug B merged. D3/D4/D4b fixed. Room-taxonomy Lane A (R-MERGE+R-REJECT) also - SHIPPED both mirrors, 6/6 parity verified; R-DOOR-SCORE tried+cleanly reverted (broke a real witness). - Read `[[project_discwalk_containment_utmost]]` memory for detail — do NOT re-attempt the `_hostAxis` - cardinal-swap patch (disproven) or R-DOOR-SCORE's hallwayness formula (also disproven) unbuilt.** - §TE-ARC-DATUM DONE+MERGED both repos 2026-07-11 (bim-compiler `b202eb44b` PR #40; bim-ootb PR #726). Remaining: - `prompts/DISC_WALKER_BRANCH_CLOSEOUT.md` — 3 stale PRs (#722/#724/#725) need re-verify + the undiagnosed - guide-screenshot camera bug. §LIVEWIRE CLOSED 2026-07-11 — W-SCHED-MINE 7/7 + W-DX-WALKBACK-RSGT 14/14, 0 unpushed. -- **Room Intelligence lane — canonical status: `prompts/ROOM_INTELLIGENCE_SCOREBOARD.md`.** - User's verdict 2026-07-11: building taxonomy is **good enough, no more perfection work there.** - 15 features shipped/verified. Weakest links unchanged: door-access signal (4/10), classifier - sample size (5/10, Duplex+SampleHouse only). Open proposals not dispatched (user's call): - fixture-in-room recognition, graph-joint-inference, external RoomGraph/SAGC-A68 datasets, - OmniClass/Uniclass mapping. Refresh the scoreboard doc, don't re-derive in prose here. -- ⏸ **PUSH PAUSE in effect (2026-07-11, until lifted)** — commits locally, verifies on localhost, - does not push/open a PR. See `CLAUDE.md` §⏸ PUSH PAUSE + `prompts/MANAGER.md`. -- **`prompts/SPARSE_WALL_ROOM_INFERENCE.md`** — Phase 0 (data-health guard) DONE+witnessed, merged - into bim-compiler `fable/meshdb-livewire`. Phase 1 (grid+door+slab+envelope fusion for - sparse-wall federated buildings, e.g. HHS) scoped as a ready-to-pick-up 4-step follow-up, not - built — genuinely substantial new engineering, deserves its own session. -- **Guide blocked on ONE thing, named and spec'd, not fire-fought:** `docs/ModellerGuide.md` (29 - real screenshots, otherwise complete) can't get a Building Parts entry yet — user's own live - review: LOD is fine now, but Modeller glass/window material isn't see-through like the Viewer's. - Spec: `prompts/MODELLER_RENDER_MATERIAL_PARITY.md` (also folds in a named quirk: Outliner panel's - collapse control isn't discoverable). Companion spec, Viewer side (evidence gap, not a code - gap): `prompts/VIEWER_FIND_PANEL_PARTS_VERIFICATION.md` — needs one real driven-from-fresh-load - screenshot, 3 known dead-ends already named so it isn't re-attempted blind. -- **`prompts/MANAGER.md` hardened this session** — anti-ad-hoc-debugging conduct rule (stop after - a 2nd failed quick-check, write a spec + dispatch instead of trial-and-erroring in-turn). Read it - before picking up either spec above. -- **From the 2026-07-10 marathon, still unmerged, not superseded:** `fix/grid-tilt-guard`, - `fix/dw-rot-units`, `fable/dwprobe-dedup`, `fix/terminal-oracle-source` (bim-ootb, all verified+ - pushed, no PR). Full detail: `project_disc_walker_grid_guard_marathon_2026-07-10.md` (memory). - -### Other open work (lower/no current juggling priority) -- **HBA IoT "wow" batch** (bim-ootb PR #659 shipped item 4b) — items 1/2/0 (CCTV double-click capture, camera-POV - fly-to ⛔ needs human-declared facing vector, mobile card-stack redesign) not yet built. Full detail + phased - build order: `prompts/RESUME_HBA_MOBILE_CARD_STACK.md` (bim-ootb) + memory `project_hba_iot_lod400_lane.md`. -- **Held, not yet built (user's own call — prove smallest piece first):** Modeller prefab design dialogue — - DAG-guided lasso, escalating selection, macro-capture — `prompts/PREFAB_LASSO_MACRO_LIBRARY_DIALOGUE.md`. -- UBBL room-size demo gate — SHIPPED+MERGED 2026-07-11, see `ROOM_INTELLIGENCE_SCOREBOARD.md` row 3. -- **Kernel op-log T4+T5** (unify 3 kernel copies) — BROWSER-GATED, needs W-ONE-KERNEL building-load smoke. - Deferred: `commitGroup` id-race retry. Spec: `prompts/KERNEL_HARDENING_BATCH1_SPEC.md §STATUS`. -- **Modeller onboarding** — Hospital/Clinic/LTU/HHS_Office as Modeller residents + migrate SH/DX/SC into the - canonical `IFC/` folder. Spec: `prompts/ARC_GEO_FETCH_SPEC.md §NEXT` item 2. -- ⛔ BLOCKED (user call): are `migration/DV_*_rules.sql` mined-rule files EXEMPT from append-only, or enforce? - Full triage: `prompts/CODEBASE_QUALITY_AUDIT_2026-07-02.md §TRIAGE` (§1 refactors, §3 shallow specs also open). -- Modeller unassigned polish: item 9 PBR textures; SSAO (needs EffectComposer vendored). -- ARC occupancy density drift (99%→92-95%, `W-DW-DENSITY-TE` D3) surfaced by PR #638 below — real, unexplained, - low-priority, not urgent. Memory: `project_arc_meshreadpixels_branch_unmerged.md`. +- DiscWalk: `prompts/Modeller/DISC_Walker/RESUME_DISC_WALKER_ENVELOPE_BOUND.md` §STOREY-UNKNOWN + (source of truth) + `DISC_WALKER_BRANCH_CLOSEOUT.md` (3 stale PRs #722/#724/#725 + guide- + screenshot camera bug). Do NOT re-attempt `_hostAxis` swap or R-DOOR-SCORE (both disproven). +- `prompts/SPARSE_WALL_ROOM_INFERENCE.md` Phase 1 — sparse-wall fusion (HHS), 4-step follow-up. +- `prompts/Modeller/DISC_Walker/XRAY_FIXTURE_CLASSIFICATION_FIX.md` — SampleCastle walls + misclassified as glowing fixtures, root-caused, POC-gated. +- `prompts/FUNCTIONAL_SPACE_MGMT_NEXT_SESSION.md` — HHS's 2 remaining islands (storey='Unknown'). +- `prompts/ROOM_LENS_VISUAL_HIGHLIGHT_SPEC.md` §25 — large-group Find-panel filter-cheap opt; + §14 — Hospital's real per-tab-switch number never captured (263MB DB wouldn't stream in sandbox). +- `prompts/PHOTOREAL_STILL_RENDER.md` — Time Machine high-quality movie export, explicit + next-session ask, not started. +- HBA IoT items 1/2/0 (CCTV double-click, camera-POV fly-to ⛔ needs facing vector, mobile + card-stack) — `prompts/RESUME_HBA_MOBILE_CARD_STACK.md` (bim-ootb). +- Held (prove smallest piece first): Modeller prefab dialogue — `prompts/PREFAB_LASSO_MACRO_LIBRARY_DIALOGUE.md`. +- Kernel op-log T4+T5 (unify 3 kernel copies) — BROWSER-GATED. `prompts/KERNEL_HARDENING_BATCH1_SPEC.md §STATUS`. +- Modeller onboarding — Hospital/Clinic/LTU/HHS_Office + SH/DX/SC into `IFC/`. `prompts/ARC_GEO_FETCH_SPEC.md §NEXT` item 2. +- ⛔ BLOCKED: `migration/DV_*_rules.sql` EXEMPT from append-only, or enforce? `prompts/CODEBASE_QUALITY_AUDIT_2026-07-02.md §TRIAGE`. +- Modeller polish: PBR textures (item 9); SSAO (needs EffectComposer). ARC occupancy drift (99%→92-95%, + `W-DW-DENSITY-TE` D3) unexplained, low-priority — `project_arc_meshreadpixels_branch_unmerged.md`. ## Archive — DONE/shipped (one-line pointers; detail in cards + memory topic files) -- **✅ Building Parts Taxonomy** (STAIRWAY/LIFT_SHAFT/PLANT_ROOM, Find panel + Outliner + disc-walk - room-type-aware placement) — merged bim-ootb `main` local (not pushed). Detail: `prompts/BUILDING_PARTS_TAXONOMY.md`. -- **✅ MANAGER housekeeping 2026-07-11:** 6 stale branches/worktrees pruned, 1 orphan landed (`c254cb271`), - ERP PR #8 re-verified (real conflict, correctly left open). -- **✅ pending merge only:** `SCALE_AND_UX_SWEEP.md` (bim-ootb PR #665), `OFFLINE_GITHUB_RELEASE_BUNDLE.md` - (`lane/offline-gateway-leak-fix`) — both independently re-verified, just need the human merge click. -- **2026-07-05 arc** (landing Save/Open, grid, Teams E2E, HBA mobile, UBBL recon) — ALL MERGED - (bim-ootb #654-664), watchdog-verified. Detail: `prompts/archive/FRONTEND_LANE_MASTER.md`. -- Pre-2026-07-05: `prompts/archive/PROGRESS_DONE_ARCHIVE_pre_2026-07-05.md` / - `_pre_2026-06-14.md`. Viewer S-series/DAGCompiler: MEMORY.md "Project — Shipped". +- ✅ B-3 0-seed posting oracles (2026-07-17, Fable 5) — 6 G-seed classes `maxDiff=0c` vs real compiled + posters (OSGi harness, scratch clone); ledger 43→49; local only (PUSH PAUSE). `prompts/FABLE5_B3_POSTING_ORACLE.md`. +- ✅ 2026-07-10 marathon branches (2026-07-17 verify) — `fix/grid-tilt-guard`, `fix/dw-rot-units`, + `fable/dwprobe-dedup`, `fix/terminal-oracle-source` all confirmed fully superseded (exact fix + content already verbatim on main via other commits) and pruned, local+origin, bim-ootb. +- ✅ FLY_TOUR + walker translation-invariance cure chain (2026-07-16/17) — PRs #812,#815,#832-#835 + merged+Pages-deployed; §WALKER-PHASE-SENSITIVITY resolved. `prompts/Viewer/FLY_TOUR_CORRIDOR_GRAPH.md`. +- ✅ Photoreal staffage + BimWhale ground fix (2026-07-17) — local only (PUSH PAUSE). `prompts/PHOTOREAL_STILL_RENDER.md`. +- ✅ LTU krn-persist readonly-ops fix, LIVE (PR #808). `prompts/MOBILE_PERF.md`. +- ✅ Room Lens §14/§26 (2026-07-15/16) — x-crossing, bbox threshold, door markers, §26 texture- + serialization hang fixed LIVE (PR #811). `prompts/ROOM_LENS_VISUAL_HIGHLIGHT_SPEC.md`. +- ✅ Room Lens taxonomy + island-bridge (2026-07-15) — Clinic 71.8%→95.7%, HHS 49.4%→85.2% + connected (PR #794/#795). +- ✅ DiscWalk containment (2026-07-12/13) — Bug A/B, D3/D4/D4b, storey='Unknown' substrate() fix. + §TE-ARC-DATUM + §LIVEWIRE closed. +- ✅ Modeller glass parity + guide-quality pass (2026-07-13) — PR #735. +- ✅ Building Parts Taxonomy — `prompts/BUILDING_PARTS_TAXONOMY.md`. +- ✅ MANAGER housekeeping (2026-07-11/13) — stale branches/worktrees pruned. +- ✅ pending merge only: `SCALE_AND_UX_SWEEP.md` (#665), `OFFLINE_GITHUB_RELEASE_BUNDLE.md` — human merge click. +- 2026-07-05 arc (Save/Open, grid, Teams E2E, HBA mobile, UBBL) — ALL MERGED (#654-664). +- Pre-2026-07-05: `prompts/archive/PROGRESS_DONE_ARCHIVE_pre_2026-07-05.md` / `_pre_2026-06-14.md`. + Viewer S-series/DAGCompiler: MEMORY.md "Project — Shipped". ## OCI Deployment - - Live: `bim-ootb-live` (SYSNOVA landing + viewer + single DBs). Always upload here. - Single DB per building: `buildings/{Name}_extracted.db` (metadata + geometry + bbox). - `deploy/sandbox/` stale (last ~S225) — not used for deploy. `deploy/dev/` is canonical. - Deploy SOP: `deploy/OCI_UPLOAD.md` ## Reference - - Docs site: https://red1oon.github.io/BIMCompiler/ - Academic paper: `docs/SPATIAL_COMPILATION_PAPER.md` - OCI setup: `internal/OCI_SETUP.md` diff --git a/build/erp/oracle/post_b3_fixture.json b/build/erp/oracle/post_b3_fixture.json new file mode 100644 index 000000000..11c85a8db --- /dev/null +++ b/build/erp/oracle/post_b3_fixture.json @@ -0,0 +1,6424 @@ +{ + "db": "idempiere_b3", + "tables": { + "fact_acct": { + "cols": [ + "fact_acct_id", + "ad_client_id", + "ad_org_id", + "c_acctschema_id", + "account_id", + "c_period_id", + "ad_table_id", + "record_id", + "line_id", + "gl_category_id", + "c_tax_id", + "postingtype", + "c_currency_id", + "amtsourcedr", + "amtsourcecr", + "amtacctdr", + "amtacctcr", + "qty", + "m_product_id", + "c_bpartner_id", + "description" + ], + "rows": [ + [ + "1000300", + "11", + "11", + "101", + "563", + "200174", + "53137", + "1000000", + "", + "108", + "", + "A", + "100", + "12000.00", + "0.00", + "12000.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000301", + "11", + "11", + "101", + "431", + "200174", + "53137", + "1000000", + "", + "108", + "", + "A", + "100", + "0.00", + "12000.00", + "0.00", + "12000.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000302", + "11", + "11", + "200000", + "563", + "200174", + "53137", + "1000000", + "", + "108", + "", + "A", + "100", + "12000.00", + "0.00", + "10200.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000303", + "11", + "11", + "200000", + "431", + "200174", + "53137", + "1000000", + "", + "108", + "", + "A", + "100", + "0.00", + "12000.00", + "0.00", + "10200.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000304", + "11", + "11", + "101", + "501", + "200174", + "53121", + "1000000", + "1000000", + "108", + "", + "A", + "100", + "555.56", + "0.00", + "555.56", + "0.00", + "0.00", + "", + "", + "1000000 #0 (Asset Depreciation Amount)" + ], + [ + "1000305", + "11", + "11", + "101", + "574", + "200174", + "53121", + "1000000", + "1000000", + "108", + "", + "A", + "100", + "0.00", + "555.56", + "0.00", + "555.56", + "0.00", + "", + "", + "1000000 #0 (Asset Depreciation Amount)" + ], + [ + "1000306", + "11", + "11", + "101", + "563", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "100", + "1000.00", + "0.00", + "1000.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000307", + "11", + "11", + "101", + "714", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "100", + "0.00", + "1000.00", + "0.00", + "1000.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000308", + "11", + "11", + "101", + "714", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "100", + "100.00", + "0.00", + "100.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000309", + "11", + "11", + "101", + "574", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "100", + "0.00", + "100.00", + "0.00", + "100.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000310", + "11", + "11", + "200000", + "563", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "102", + "1000.00", + "0.00", + "1000.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000311", + "11", + "11", + "200000", + "714", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "102", + "0.00", + "1000.00", + "0.00", + "1000.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000312", + "11", + "11", + "200000", + "714", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "102", + "100.00", + "0.00", + "100.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000313", + "11", + "11", + "200000", + "574", + "200174", + "53275", + "1000000", + "", + "108", + "", + "A", + "102", + "0.00", + "100.00", + "0.00", + "100.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000314", + "11", + "11", + "101", + "563", + "200174", + "53137", + "1000001", + "", + "108", + "", + "A", + "100", + "6000.00", + "0.00", + "6000.00", + "0.00", + "0.00", + "", + "", + "1000001" + ], + [ + "1000315", + "11", + "11", + "101", + "431", + "200174", + "53137", + "1000001", + "", + "108", + "", + "A", + "100", + "0.00", + "6000.00", + "0.00", + "6000.00", + "0.00", + "", + "", + "1000001" + ], + [ + "1000316", + "11", + "11", + "200000", + "563", + "200174", + "53137", + "1000001", + "", + "108", + "", + "A", + "100", + "6000.00", + "0.00", + "5100.00", + "0.00", + "0.00", + "", + "", + "1000001" + ], + [ + "1000317", + "11", + "11", + "200000", + "431", + "200174", + "53137", + "1000001", + "", + "108", + "", + "A", + "100", + "0.00", + "6000.00", + "0.00", + "5100.00", + "0.00", + "", + "", + "1000001" + ], + [ + "1000318", + "11", + "11", + "101", + "556", + "200174", + "53128", + "1000000", + "", + "108", + "", + "A", + "100", + "6000.00", + "0.00", + "6000.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000319", + "11", + "11", + "101", + "563", + "200174", + "53128", + "1000000", + "", + "108", + "", + "A", + "100", + "0.00", + "6000.00", + "0.00", + "6000.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000320", + "11", + "11", + "200000", + "556", + "200174", + "53128", + "1000000", + "", + "108", + "", + "A", + "102", + "6000.00", + "0.00", + "6000.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000321", + "11", + "11", + "200000", + "563", + "200174", + "53128", + "1000000", + "", + "108", + "", + "A", + "102", + "0.00", + "6000.00", + "0.00", + "6000.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000322", + "11", + "11", + "101", + "563", + "200174", + "53137", + "1000003", + "", + "108", + "", + "A", + "100", + "9000.00", + "0.00", + "9000.00", + "0.00", + "0.00", + "", + "", + "1000003" + ], + [ + "1000323", + "11", + "11", + "101", + "431", + "200174", + "53137", + "1000003", + "", + "108", + "", + "A", + "100", + "0.00", + "9000.00", + "0.00", + "9000.00", + "0.00", + "", + "", + "1000003" + ], + [ + "1000324", + "11", + "11", + "200000", + "563", + "200174", + "53137", + "1000003", + "", + "108", + "", + "A", + "100", + "9000.00", + "0.00", + "7650.00", + "0.00", + "0.00", + "", + "", + "1000003" + ], + [ + "1000325", + "11", + "11", + "200000", + "431", + "200174", + "53137", + "1000003", + "", + "108", + "", + "A", + "100", + "0.00", + "9000.00", + "0.00", + "7650.00", + "0.00", + "", + "", + "1000003" + ], + [ + "1000326", + "11", + "11", + "101", + "563", + "200174", + "53127", + "1000000", + "", + "108", + "", + "A", + "100", + "0.00", + "9000.00", + "0.00", + "9000.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000327", + "11", + "11", + "101", + "726", + "200174", + "53127", + "1000000", + "", + "108", + "", + "A", + "100", + "9000.00", + "0.00", + "9000.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000328", + "11", + "11", + "200000", + "563", + "200174", + "53127", + "1000000", + "", + "108", + "", + "A", + "102", + "0.00", + "7650.00", + "0.00", + "7650.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000329", + "11", + "11", + "200000", + "726", + "200174", + "53127", + "1000000", + "", + "108", + "", + "A", + "102", + "7650.00", + "0.00", + "7650.00", + "0.00", + "0.00", + "", + "", + "1000000" + ], + [ + "1000330", + "11", + "11", + "101", + "568", + "200174", + "623", + "1000000", + "1000000", + "108", + "", + "A", + "100", + "2.70", + "0.00", + "2.70", + "0.00", + "1.00", + "137", + "", + "Landscape #10 #10" + ], + [ + "1000331", + "11", + "11", + "101", + "742", + "200174", + "623", + "1000000", + "1000000", + "108", + "", + "A", + "100", + "0.00", + "2.70", + "0.00", + "2.70", + "-1.00", + "137", + "", + "Landscape #10 #10" + ], + [ + "1000332", + "11", + "11", + "200000", + "568", + "200174", + "623", + "1000000", + "1000000", + "108", + "", + "A", + "102", + "2.30", + "0.00", + "2.30", + "0.00", + "1.00", + "137", + "", + "Landscape #10 #10" + ], + [ + "1000333", + "11", + "11", + "200000", + "742", + "200174", + "623", + "1000000", + "1000000", + "108", + "", + "A", + "102", + "0.00", + "2.30", + "0.00", + "2.30", + "-1.00", + "137", + "", + "Landscape #10 #10" + ] + ] + }, + "a_asset_addition": { + "cols": [ + "a_asset_addition_id", + "a_asset_id", + "dateacct", + "a_sourcetype", + "a_capvsexp", + "assetsourceamt", + "assetvalueamt", + "c_charge_id", + "m_product_id", + "c_invoice_id", + "c_invoiceline_id", + "c_project_id", + "postingtype", + "docstatus", + "posted", + "ad_org_id", + "ad_client_id", + "c_currency_id" + ], + "rows": [ + [ + "1000000", + "1000000", + "2026-07-17", + "MAN", + "Cap", + "12000.00", + "12000.00", + "0", + "0", + "0", + "0", + "0", + "A", + "CO", + "Y", + "11", + "11", + "100" + ], + [ + "1000001", + "1000001", + "2026-07-17", + "MAN", + "Cap", + "6000.00", + "6000.00", + "0", + "0", + "0", + "0", + "0", + "A", + "CO", + "Y", + "11", + "11", + "100" + ], + [ + "1000002", + "1000001", + "2026-07-17", + "MAN", + "Exp", + "500.00", + "500.00", + "0", + "0", + "0", + "0", + "0", + "A", + "CO", + "Y", + "11", + "11", + "100" + ], + [ + "1000003", + "1000002", + "2026-07-17", + "MAN", + "Cap", + "9000.00", + "9000.00", + "0", + "0", + "0", + "0", + "0", + "A", + "CO", + "Y", + "11", + "11", + "100" + ] + ] + }, + "a_asset_disposed": { + "cols": [ + "a_asset_disposed_id", + "a_asset_id", + "dateacct", + "a_disposed_method", + "a_disposal_amt", + "a_accumulated_depr_delta", + "expense", + "postingtype", + "docstatus", + "posted", + "ad_org_id" + ], + "rows": [ + [ + "1000000", + "1000002", + "2026-07-17", + "S", + "9000.00", + "0.00", + "9000.00", + "A", + "CO", + "Y", + "11" + ] + ] + }, + "a_asset_reval": { + "cols": [ + "a_asset_reval_id", + "a_asset_id", + "dateacct", + "a_asset_cost", + "a_accumulated_depr", + "a_asset_cost_change", + "a_change_acumulated_depr", + "postingtype", + "docstatus", + "posted", + "ad_org_id" + ], + "rows": [ + [ + "1000000", + "1000000", + "2026-07-31", + "12000.00", + "555.56", + "13000.00", + "655.56", + "A", + "CO", + "Y", + "11" + ] + ] + }, + "a_asset_transfer": { + "cols": [ + "a_asset_transfer_id", + "a_asset_id", + "c_acctschema_id", + "dateacct", + "a_asset_acct", + "a_asset_new_acct", + "a_accumdepreciation_acct", + "a_accumdepreciation_new_acct", + "a_depreciation_acct", + "a_depreciation_new_acct", + "postingtype", + "docstatus", + "posted", + "ad_org_id" + ], + "rows": [ + [ + "1000000", + "1000001", + "101", + "2026-07-18", + "200013", + "200010", + "200004", + "200011", + "200001", + "200001", + "A", + "CO", + "Y", + "11" + ] + ] + }, + "a_depreciation_entry": { + "cols": [ + "a_depreciation_entry_id", + "c_acctschema_id", + "dateacct", + "postingtype", + "a_entry_type", + "docstatus", + "posted", + "ad_org_id", + "c_doctype_id" + ], + "rows": [ + [ + "1000000", + "101", + "2026-07-31", + "A", + "DEP", + "CO", + "Y", + "11", + "1000000" + ] + ] + }, + "a_depreciation_exp": { + "cols": [ + "a_depreciation_exp_id", + "a_depreciation_entry_id", + "a_asset_id", + "c_acctschema_id", + "dr_account_id", + "cr_account_id", + "expense", + "dateacct", + "processed", + "a_period", + "postingtype", + "a_entry_type", + "ad_org_id" + ], + "rows": [ + [ + "1000000", + "1000000", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2026-07-31", + "Y", + "1", + "A", + "DEP", + "11" + ], + [ + "1000001", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2026-08-31", + "N", + "2", + "A", + "DEP", + "11" + ], + [ + "1000002", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2026-09-30", + "N", + "3", + "A", + "DEP", + "11" + ], + [ + "1000003", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2026-10-31", + "N", + "4", + "A", + "DEP", + "11" + ], + [ + "1000004", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2026-11-30", + "N", + "5", + "A", + "DEP", + "11" + ], + [ + "1000005", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2026-12-31", + "N", + "6", + "A", + "DEP", + "11" + ], + [ + "1000006", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2027-01-31", + "N", + "7", + "A", + "DEP", + "11" + ], + [ + "1000007", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2027-02-28", + "N", + "8", + "A", + "DEP", + "11" + ], + [ + "1000008", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2027-03-31", + "N", + "9", + "A", + "DEP", + "11" + ], + [ + "1000009", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2027-04-30", + "N", + "10", + "A", + "DEP", + "11" + ], + [ + "1000010", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2027-05-31", + "N", + "11", + "A", + "DEP", + "11" + ], + [ + "1000011", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2027-06-30", + "N", + "12", + "A", + "DEP", + "11" + ], + [ + "1000012", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2027-07-31", + "N", + "13", + "A", + "DEP", + "11" + ], + [ + "1000013", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2027-08-31", + "N", + "14", + "A", + "DEP", + "11" + ], + [ + "1000014", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2027-09-30", + "N", + "15", + "A", + "DEP", + "11" + ], + [ + "1000015", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2027-10-31", + "N", + "16", + "A", + "DEP", + "11" + ], + [ + "1000016", + "0", + "1000000", + "101", + "200001", + "200004", + "555.56", + "2027-11-30", + "N", + "17", + "A", + "DEP", + "11" + ], + [ + "1000017", + "0", + "1000000", + "101", + "200001", + "200004", + "555.55", + "2027-12-31", + "N", + "18", + "A", + "DEP", + "11" + ], + [ + "1000018", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2026-07-31", + "N", + "1", + "A", + "DEP", + "11" + ], + [ + "1000019", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2026-08-31", + "N", + "2", + "A", + "DEP", + "11" + ], + [ + "1000020", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2026-09-30", + "N", + "3", + "A", + "DEP", + "11" + ], + [ + "1000021", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2026-10-31", + "N", + "4", + "A", + "DEP", + "11" + ], + [ + "1000022", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2026-11-30", + "N", + "5", + "A", + "DEP", + "11" + ], + [ + "1000023", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2026-12-31", + "N", + "6", + "A", + "DEP", + "11" + ], + [ + "1000024", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-01-31", + "N", + "7", + "A", + "DEP", + "11" + ], + [ + "1000025", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-02-28", + "N", + "8", + "A", + "DEP", + "11" + ], + [ + "1000026", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-03-31", + "N", + "9", + "A", + "DEP", + "11" + ], + [ + "1000027", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-04-30", + "N", + "10", + "A", + "DEP", + "11" + ], + [ + "1000028", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.23", + "2027-05-31", + "N", + "11", + "A", + "DEP", + "11" + ], + [ + "1000029", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-06-30", + "N", + "12", + "A", + "DEP", + "11" + ], + [ + "1000030", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.23", + "2027-07-31", + "N", + "13", + "A", + "DEP", + "11" + ], + [ + "1000031", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-08-31", + "N", + "14", + "A", + "DEP", + "11" + ], + [ + "1000032", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.23", + "2027-09-30", + "N", + "15", + "A", + "DEP", + "11" + ], + [ + "1000033", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-10-31", + "N", + "16", + "A", + "DEP", + "11" + ], + [ + "1000034", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.23", + "2027-11-30", + "N", + "17", + "A", + "DEP", + "11" + ], + [ + "1000035", + "0", + "1000000", + "200000", + "200001", + "200004", + "472.22", + "2027-12-31", + "N", + "18", + "A", + "DEP", + "11" + ], + [ + "1000072", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2026-07-31", + "N", + "1", + "A", + "DEP", + "11" + ], + [ + "1000073", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2026-08-31", + "N", + "2", + "A", + "DEP", + "11" + ], + [ + "1000074", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2026-09-30", + "N", + "3", + "A", + "DEP", + "11" + ], + [ + "1000075", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2026-10-31", + "N", + "4", + "A", + "DEP", + "11" + ], + [ + "1000076", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2026-11-30", + "N", + "5", + "A", + "DEP", + "11" + ], + [ + "1000077", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2026-12-31", + "N", + "6", + "A", + "DEP", + "11" + ], + [ + "1000078", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-01-31", + "N", + "7", + "A", + "DEP", + "11" + ], + [ + "1000079", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-02-28", + "N", + "8", + "A", + "DEP", + "11" + ], + [ + "1000080", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-03-31", + "N", + "9", + "A", + "DEP", + "11" + ], + [ + "1000081", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-04-30", + "N", + "10", + "A", + "DEP", + "11" + ], + [ + "1000082", + "0", + "1000001", + "101", + "200001", + "200004", + "222.23", + "2027-05-31", + "N", + "11", + "A", + "DEP", + "11" + ], + [ + "1000083", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-06-30", + "N", + "12", + "A", + "DEP", + "11" + ], + [ + "1000084", + "0", + "1000001", + "101", + "200001", + "200004", + "222.23", + "2027-07-31", + "N", + "13", + "A", + "DEP", + "11" + ], + [ + "1000085", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-08-31", + "N", + "14", + "A", + "DEP", + "11" + ], + [ + "1000086", + "0", + "1000001", + "101", + "200001", + "200004", + "222.23", + "2027-09-30", + "N", + "15", + "A", + "DEP", + "11" + ], + [ + "1000087", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-10-31", + "N", + "16", + "A", + "DEP", + "11" + ], + [ + "1000088", + "0", + "1000001", + "101", + "200001", + "200004", + "222.23", + "2027-11-30", + "N", + "17", + "A", + "DEP", + "11" + ], + [ + "1000089", + "0", + "1000001", + "101", + "200001", + "200004", + "222.22", + "2027-12-31", + "N", + "18", + "A", + "DEP", + "11" + ], + [ + "1000090", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2026-07-31", + "N", + "1", + "A", + "DEP", + "11" + ], + [ + "1000091", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2026-08-31", + "N", + "2", + "A", + "DEP", + "11" + ], + [ + "1000092", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2026-09-30", + "N", + "3", + "A", + "DEP", + "11" + ], + [ + "1000093", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2026-10-31", + "N", + "4", + "A", + "DEP", + "11" + ], + [ + "1000094", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2026-11-30", + "N", + "5", + "A", + "DEP", + "11" + ], + [ + "1000095", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2026-12-31", + "N", + "6", + "A", + "DEP", + "11" + ], + [ + "1000096", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-01-31", + "N", + "7", + "A", + "DEP", + "11" + ], + [ + "1000097", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-02-28", + "N", + "8", + "A", + "DEP", + "11" + ], + [ + "1000098", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-03-31", + "N", + "9", + "A", + "DEP", + "11" + ], + [ + "1000099", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-04-30", + "N", + "10", + "A", + "DEP", + "11" + ], + [ + "1000100", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-05-31", + "N", + "11", + "A", + "DEP", + "11" + ], + [ + "1000101", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-06-30", + "N", + "12", + "A", + "DEP", + "11" + ], + [ + "1000102", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-07-31", + "N", + "13", + "A", + "DEP", + "11" + ], + [ + "1000103", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-08-31", + "N", + "14", + "A", + "DEP", + "11" + ], + [ + "1000104", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-09-30", + "N", + "15", + "A", + "DEP", + "11" + ], + [ + "1000105", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.88", + "2027-10-31", + "N", + "16", + "A", + "DEP", + "11" + ], + [ + "1000106", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.89", + "2027-11-30", + "N", + "17", + "A", + "DEP", + "11" + ], + [ + "1000107", + "0", + "1000001", + "200000", + "200001", + "200004", + "188.88", + "2027-12-31", + "N", + "18", + "A", + "DEP", + "11" + ] + ] + }, + "c_projectissue": { + "cols": [ + "c_projectissue_id", + "c_project_id", + "m_product_id", + "m_locator_id", + "movementqty", + "movementdate", + "m_inoutline_id", + "s_timeexpenseline_id", + "posted", + "processed", + "ad_org_id", + "ad_client_id" + ], + "rows": [ + [ + "1000000", + "101", + "137", + "101", + "1.00", + "2026-07-17", + "0", + "0", + "Y", + "Y", + "11", + "11" + ] + ] + }, + "a_asset_acct": { + "cols": [ + "a_asset_acct_id", + "a_asset_id", + "c_acctschema_id", + "postingtype", + "validfrom", + "a_asset_acct", + "a_accumdepreciation_acct", + "a_depreciation_acct", + "a_disposal_loss_acct", + "a_disposal_revenue_acct", + "a_reval_cost_offset_acct" + ], + "rows": [ + [ + "1000000", + "1000000", + "101", + "A", + "1970-01-01", + "200013", + "200004", + "200001", + "200006", + "200005", + "200005" + ], + [ + "1000001", + "1000000", + "200000", + "A", + "1970-01-01", + "200013", + "200004", + "200001", + "200006", + "200005", + "200005" + ], + [ + "1000002", + "1000001", + "101", + "A", + "1970-01-01", + "200013", + "200004", + "200001", + "200006", + "200005", + "0" + ], + [ + "1000003", + "1000001", + "200000", + "A", + "1970-01-01", + "200013", + "200004", + "200001", + "200006", + "200005", + "0" + ], + [ + "1000004", + "1000001", + "101", + "A", + "2026-07-18", + "200010", + "200011", + "200001", + "200006", + "200005", + "0" + ], + [ + "1000005", + "1000002", + "101", + "A", + "1970-01-01", + "200013", + "200004", + "200001", + "200006", + "200005", + "0" + ], + [ + "1000006", + "1000002", + "200000", + "A", + "1970-01-01", + "200013", + "200004", + "200001", + "200006", + "200005", + "0" + ] + ] + }, + "a_asset_change": { + "cols": [ + "a_asset_change_id", + "a_asset_id", + "changetype", + "c_acctschema_id", + "postingtype", + "assetvalueamt", + "assetbookvalueamt", + "assetaccumdepreciationamt" + ], + "rows": [ + [ + "1000004", + "1000002", + "DIS", + "101", + "A", + "9000.00", + "9000.00", + "0.00" + ], + [ + "1000005", + "1000002", + "DIS", + "200000", + "A", + "7650.00", + "7650.00", + "0.00" + ] + ] + }, + "a_depreciation_workfile": { + "cols": [ + "a_depreciation_workfile_id", + "a_asset_id", + "c_acctschema_id", + "postingtype", + "a_asset_cost", + "a_accumulated_depr" + ], + "rows": [ + [ + "1000000", + "1000000", + "101", + "A", + "13000.00", + "655.56" + ], + [ + "1000001", + "1000000", + "200000", + "A", + "10200.00", + "0.00" + ], + [ + "1000002", + "1000001", + "101", + "A", + "6000.00", + "0.00" + ], + [ + "1000003", + "1000001", + "200000", + "A", + "5100.00", + "0.00" + ], + [ + "1000004", + "1000002", + "101", + "A", + "0.00", + "0.00" + ], + [ + "1000005", + "1000002", + "200000", + "A", + "0.00", + "0.00" + ] + ] + }, + "a_asset": { + "cols": [ + "a_asset_id", + "name", + "a_asset_group_id", + "m_product_id" + ], + "rows": [ + [ + "1000000", + "b3_asset_a", + "50007", + "200001" + ], + [ + "1000001", + "b3_asset_b", + "50007", + "200001" + ], + [ + "1000002", + "b3_asset_c", + "50007", + "200001" + ], + [ + "1100001", + "Delivery Truck #1", + "50003", + "200001" + ], + [ + "1100002", + "Office HVAC Unit #1", + "50003", + "200002" + ], + [ + "1100003", + "Forklift #1", + "50003", + "200001" + ] + ] + }, + "c_project": { + "cols": [ + "c_project_id", + "projectcategory" + ], + "rows": [ + [ + "100", + "N" + ], + [ + "101", + "N" + ] + ] + }, + "c_project_acct": { + "cols": [ + "c_project_id", + "c_acctschema_id", + "pj_asset_acct", + "pj_wip_acct" + ], + "rows": [ + [ + "100", + "101", + "250", + "250" + ], + [ + "100", + "200000", + "200062", + "200063" + ], + [ + "101", + "101", + "250", + "251" + ], + [ + "101", + "200000", + "200062", + "200063" + ] + ] + }, + "c_validcombination": { + "cols": [ + "c_validcombination_id", + "account_id" + ], + "rows": [ + [ + "219", + "698" + ], + [ + "220", + "699" + ], + [ + "221", + "724" + ], + [ + "222", + "631" + ], + [ + "223", + "771" + ], + [ + "224", + "600" + ], + [ + "225", + "741" + ], + [ + "226", + "776" + ], + [ + "227", + "439" + ], + [ + "228", + "442" + ], + [ + "229", + "758" + ], + [ + "230", + "431" + ], + [ + "231", + "742" + ], + [ + "232", + "445" + ], + [ + "233", + "430" + ], + [ + "234", + "518" + ], + [ + "235", + "750" + ], + [ + "237", + "749" + ], + [ + "238", + "588" + ], + [ + "239", + "532" + ], + [ + "240", + "427" + ], + [ + "241", + "448" + ], + [ + "242", + "765" + ], + [ + "243", + "712" + ], + [ + "244", + "722" + ], + [ + "245", + "767" + ], + [ + "246", + "723" + ], + [ + "247", + "598" + ], + [ + "248", + "533" + ], + [ + "249", + "652" + ], + [ + "250", + "567" + ], + [ + "251", + "568" + ], + [ + "252", + "760" + ], + [ + "253", + "597" + ], + [ + "254", + "535" + ], + [ + "255", + "596" + ], + [ + "256", + "534" + ], + [ + "257", + "509" + ], + [ + "258", + "508" + ], + [ + "259", + "661" + ], + [ + "260", + "705" + ], + [ + "261", + "717" + ], + [ + "262", + "510" + ], + [ + "263", + "711" + ], + [ + "264", + "769" + ], + [ + "265", + "710" + ], + [ + "266", + "721" + ], + [ + "267", + "702" + ], + [ + "268", + "703" + ], + [ + "269", + "423" + ], + [ + "270", + "521" + ], + [ + "271", + "424" + ], + [ + "272", + "587" + ], + [ + "273", + "515" + ], + [ + "274", + "690" + ], + [ + "275", + "700" + ], + [ + "276", + "701" + ], + [ + "277", + "508" + ], + [ + "282", + "444" + ], + [ + "283", + "447" + ], + [ + "284", + "426" + ], + [ + "285", + "441" + ], + [ + "286", + "511" + ], + [ + "287", + "589" + ], + [ + "288", + "516" + ], + [ + "290", + "419" + ], + [ + "291", + "635" + ], + [ + "292", + "515" + ], + [ + "293", + "516" + ], + [ + "294", + "700" + ], + [ + "295", + "690" + ], + [ + "296", + "701" + ], + [ + "297", + "778" + ], + [ + "298", + "781" + ], + [ + "299", + "780" + ], + [ + "300", + "736" + ], + [ + "50000", + "50000" + ], + [ + "50001", + "50005" + ], + [ + "50002", + "50006" + ], + [ + "50003", + "50002" + ], + [ + "50004", + "50001" + ], + [ + "50005", + "50003" + ], + [ + "50006", + "50004" + ], + [ + "50007", + "50007" + ], + [ + "50008", + "50009" + ], + [ + "50009", + "50010" + ], + [ + "50010", + "50011" + ], + [ + "50011", + "50014" + ], + [ + "50012", + "50016" + ], + [ + "50013", + "50017" + ], + [ + "200000", + "200000" + ], + [ + "200001", + "501" + ], + [ + "200002", + "742" + ], + [ + "200003", + "574" + ], + [ + "200004", + "574" + ], + [ + "200005", + "714" + ], + [ + "200006", + "726" + ], + [ + "200007", + "646" + ], + [ + "200008", + "558" + ], + [ + "200009", + "726" + ], + [ + "200010", + "556" + ], + [ + "200011", + "569" + ], + [ + "200012", + "558" + ], + [ + "200013", + "563" + ], + [ + "200014", + "564" + ], + [ + "200015", + "575" + ], + [ + "200016", + "647" + ], + [ + "200017", + "434" + ], + [ + "200018", + "600" + ], + [ + "200019", + "698" + ], + [ + "200020", + "724" + ], + [ + "200021", + "635" + ], + [ + "200022", + "736" + ], + [ + "200023", + "50000" + ], + [ + "200024", + "741" + ], + [ + "200025", + "712" + ], + [ + "200026", + "722" + ], + [ + "200027", + "767" + ], + [ + "200028", + "723" + ], + [ + "200029", + "587" + ], + [ + "200030", + "423" + ], + [ + "200031", + "427" + ], + [ + "200032", + "448" + ], + [ + "200033", + "765" + ], + [ + "200034", + "518" + ], + [ + "200035", + "749" + ], + [ + "200036", + "750" + ], + [ + "200037", + "532" + ], + [ + "200038", + "742" + ], + [ + "200039", + "431" + ], + [ + "200040", + "781" + ], + [ + "200041", + "780" + ], + [ + "200042", + "430" + ], + [ + "200043", + "758" + ], + [ + "200044", + "445" + ], + [ + "200045", + "444" + ], + [ + "200046", + "447" + ], + [ + "200047", + "426" + ], + [ + "200048", + "50003" + ], + [ + "200049", + "50017" + ], + [ + "200050", + "200000" + ], + [ + "200051", + "439" + ], + [ + "200052", + "508" + ], + [ + "200053", + "509" + ], + [ + "200054", + "589" + ], + [ + "200055", + "511" + ], + [ + "200056", + "717" + ], + [ + "200057", + "705" + ], + [ + "200058", + "596" + ], + [ + "200059", + "534" + ], + [ + "200060", + "760" + ], + [ + "200061", + "702" + ], + [ + "200062", + "567" + ], + [ + "200063", + "568" + ], + [ + "200064", + "508" + ], + [ + "200065", + "516" + ], + [ + "200066", + "419" + ], + [ + "1000000", + "596" + ] + ] + }, + "m_product": { + "cols": [ + "m_product_id", + "producttype", + "m_product_category_id", + "name" + ], + "rows": [ + [ + "122", + "S", + "105", + "Standard" + ], + [ + "123", + "I", + "106", + "Oak Tree" + ], + [ + "124", + "I", + "106", + "Elm Tree" + ], + [ + "125", + "I", + "105", + "Grass Seed Container" + ], + [ + "126", + "S", + "105", + "Planting Service" + ], + [ + "127", + "I", + "107", + "Rose Bush" + ], + [ + "128", + "I", + "107", + "Azalea Bush" + ], + [ + "129", + "I", + "107", + "Holly Bush" + ], + [ + "130", + "I", + "106", + "Plum Tree" + ], + [ + "131", + "E", + "105", + "Travel cost" + ], + [ + "132", + "R", + "105", + "Mary Consultant" + ], + [ + "133", + "I", + "110", + "Patio Chair" + ], + [ + "134", + "I", + "110", + "Patio Table" + ], + [ + "135", + "I", + "110", + "Patio Sun Screen" + ], + [ + "136", + "I", + "109", + "Fertilizer #50" + ], + [ + "137", + "I", + "109", + "Mulch 10#" + ], + [ + "138", + "I", + "108", + "Hoe 4 ft" + ], + [ + "139", + "I", + "108", + "Rake Bamboo" + ], + [ + "140", + "I", + "108", + "Rake Metal" + ], + [ + "141", + "I", + "108", + "Weeder" + ], + [ + "142", + "I", + "108", + "Transplanter" + ], + [ + "143", + "I", + "108", + "Grass Seeder" + ], + [ + "144", + "I", + "108", + "Lawn Tiller" + ], + [ + "145", + "I", + "110", + "Patio Furniture Set" + ], + [ + "146", + "S", + "111", + "How To Plant" + ], + [ + "147", + "I", + "105", + "TShirt - Red Large" + ], + [ + "148", + "I", + "105", + "TShirt - Green Large" + ], + [ + "50000", + "I", + "50000", + "Assembly Back Leg" + ], + [ + "50001", + "I", + "50000", + "Assembly Front Leg" + ], + [ + "50002", + "I", + "50001", + "#6-32 x 3/8 Socket Head Cap Screw" + ], + [ + "50003", + "I", + "50001", + "Ultra Glue" + ], + [ + "50004", + "I", + "50001", + "Seat" + ], + [ + "50005", + "I", + "50001", + "Back Support" + ], + [ + "50007", + "I", + "109", + "Fertilizer #70" + ], + [ + "50008", + "I", + "50000", + "Lawn Fertilizer" + ], + [ + "50009", + "I", + "50001", + "Nitrogen" + ], + [ + "50010", + "I", + "50001", + "Phosphorus" + ], + [ + "50012", + "I", + "50001", + "Potassium" + ], + [ + "50013", + "I", + "50002", + "Bag 50 Kg" + ], + [ + "50014", + "I", + "50002", + "Bag 70 Kg" + ], + [ + "50015", + "I", + "50001", + "Front Leg" + ], + [ + "50016", + "I", + "50001", + "Back Leg" + ], + [ + "50017", + "I", + "50001", + "Water" + ], + [ + "50018", + "R", + "50003", + "Fertilizer Plant" + ], + [ + "50019", + "R", + "50003", + "Assembly Area" + ], + [ + "50020", + "R", + "50003", + "Paint Area" + ], + [ + "50021", + "R", + "50003", + "Chrome Subcontract Area" + ], + [ + "50022", + "R", + "50003", + "Inspection Area" + ], + [ + "50023", + "R", + "50003", + "Furniture Plant" + ], + [ + "50024", + "R", + "50003", + "Packing Production Line" + ], + [ + "50025", + "R", + "50003", + "Mixed Area" + ], + [ + "50026", + "R", + "50003", + "Dry Area" + ], + [ + "50027", + "R", + "50003", + "Fertilizer Inspection Area" + ], + [ + "200001", + "A", + "200000", + "Asset Vehicle" + ], + [ + "200002", + "A", + "200001", + "Computers" + ] + ] + }, + "m_product_category": { + "cols": [ + "m_product_category_id", + "isdefault", + "created" + ], + "rows": [ + [ + "105", + "Y", + "2001-03-27 15:44:24" + ], + [ + "106", + "N", + "2001-04-11 20:50:24" + ], + [ + "107", + "N", + "2001-04-26 18:16:29" + ], + [ + "108", + "N", + "2003-01-20 20:26:50" + ], + [ + "109", + "N", + "2003-01-20 20:27:20" + ], + [ + "110", + "N", + "2003-01-20 20:27:44" + ], + [ + "111", + "N", + "2003-01-24 00:14:00" + ], + [ + "112", + "N", + "2003-01-24 00:25:28" + ], + [ + "50000", + "N", + "2008-09-22 13:49:50" + ], + [ + "50001", + "N", + "2008-09-22 14:19:06" + ], + [ + "50002", + "N", + "2008-09-22 14:21:17" + ], + [ + "50003", + "N", + "2008-09-22 19:18:00" + ], + [ + "200000", + "N", + "2020-04-09 17:05:28" + ], + [ + "200001", + "N", + "2020-04-09 17:05:29" + ] + ] + }, + "m_product_category_acct": { + "cols": [ + "m_product_category_id", + "c_acctschema_id", + "p_expense_acct", + "p_asset_acct" + ], + "rows": [ + [ + "105", + "101", + "230", + "231" + ], + [ + "105", + "200000", + "200039", + "200038" + ], + [ + "106", + "101", + "230", + "231" + ], + [ + "106", + "200000", + "200039", + "200038" + ], + [ + "107", + "101", + "230", + "231" + ], + [ + "107", + "200000", + "200039", + "200038" + ], + [ + "108", + "101", + "230", + "231" + ], + [ + "108", + "200000", + "200039", + "200038" + ], + [ + "109", + "101", + "230", + "231" + ], + [ + "109", + "200000", + "200039", + "200038" + ], + [ + "110", + "101", + "230", + "231" + ], + [ + "110", + "200000", + "200039", + "200038" + ], + [ + "111", + "101", + "230", + "231" + ], + [ + "111", + "200000", + "200039", + "200038" + ], + [ + "112", + "101", + "230", + "231" + ], + [ + "112", + "200000", + "200039", + "200038" + ], + [ + "50000", + "101", + "230", + "231" + ], + [ + "50000", + "200000", + "200039", + "200038" + ], + [ + "50001", + "101", + "230", + "231" + ], + [ + "50001", + "200000", + "200039", + "200038" + ], + [ + "50002", + "101", + "230", + "231" + ], + [ + "50002", + "200000", + "200039", + "200038" + ], + [ + "50003", + "101", + "230", + "231" + ], + [ + "50003", + "200000", + "200039", + "200038" + ], + [ + "200000", + "101", + "230", + "231" + ], + [ + "200000", + "200000", + "200039", + "200038" + ], + [ + "200001", + "101", + "230", + "231" + ], + [ + "200001", + "200000", + "200039", + "200038" + ] + ] + }, + "m_cost": { + "cols": [ + "m_product_id", + "c_acctschema_id", + "m_costtype_id", + "m_costelement_id", + "currentcostprice" + ], + "rows": [ + [ + "122", + "101", + "100", + "100", + "0" + ], + [ + "122", + "101", + "100", + "105", + "0" + ], + [ + "122", + "101", + "100", + "50000", + "0" + ], + [ + "122", + "101", + "100", + "50001", + "0" + ], + [ + "122", + "101", + "100", + "50002", + "0" + ], + [ + "123", + "101", + "100", + "100", + "24" + ], + [ + "123", + "101", + "100", + "102", + "51.4500" + ], + [ + "123", + "101", + "100", + "103", + "51.45000000" + ], + [ + "123", + "101", + "100", + "105", + "0" + ], + [ + "123", + "101", + "100", + "50000", + "0" + ], + [ + "123", + "101", + "100", + "50001", + "0" + ], + [ + "123", + "101", + "100", + "50002", + "0" + ], + [ + "123", + "200000", + "100", + "100", + "30.6000" + ], + [ + "123", + "200000", + "100", + "102", + "43.7324" + ], + [ + "123", + "200000", + "100", + "103", + "43.73250000" + ], + [ + "124", + "101", + "100", + "100", + "24" + ], + [ + "124", + "101", + "100", + "102", + "57.0000" + ], + [ + "124", + "101", + "100", + "103", + "57.00000000" + ], + [ + "124", + "101", + "100", + "105", + "0" + ], + [ + "124", + "101", + "100", + "50000", + "0" + ], + [ + "124", + "101", + "100", + "50001", + "0" + ], + [ + "124", + "101", + "100", + "50002", + "0" + ], + [ + "124", + "200000", + "100", + "100", + "48.4500" + ], + [ + "124", + "200000", + "100", + "102", + "48.4500" + ], + [ + "124", + "200000", + "100", + "103", + "48.45000000" + ], + [ + "125", + "101", + "100", + "100", + "40" + ], + [ + "125", + "101", + "100", + "102", + "48.0000" + ], + [ + "125", + "101", + "100", + "103", + "48.00000000" + ], + [ + "125", + "101", + "100", + "105", + "0" + ], + [ + "125", + "101", + "100", + "50000", + "0" + ], + [ + "125", + "101", + "100", + "50001", + "0" + ], + [ + "125", + "101", + "100", + "50002", + "0" + ], + [ + "125", + "200000", + "100", + "100", + "40.8000" + ], + [ + "125", + "200000", + "100", + "102", + "40.8000" + ], + [ + "125", + "200000", + "100", + "103", + "40.80000000" + ], + [ + "126", + "101", + "100", + "100", + "45" + ], + [ + "126", + "101", + "100", + "105", + "0" + ], + [ + "126", + "101", + "100", + "50000", + "0" + ], + [ + "126", + "101", + "100", + "50001", + "0" + ], + [ + "126", + "101", + "100", + "50002", + "0" + ], + [ + "127", + "101", + "100", + "100", + "12" + ], + [ + "127", + "101", + "100", + "102", + "28.5000" + ], + [ + "127", + "101", + "100", + "103", + "28.50000000" + ], + [ + "127", + "101", + "100", + "105", + "0" + ], + [ + "127", + "101", + "100", + "50000", + "0" + ], + [ + "127", + "101", + "100", + "50001", + "0" + ], + [ + "127", + "101", + "100", + "50002", + "0" + ], + [ + "127", + "200000", + "100", + "100", + "24.2250" + ], + [ + "127", + "200000", + "100", + "102", + "24.2250" + ], + [ + "127", + "200000", + "100", + "103", + "24.22500000" + ], + [ + "128", + "101", + "100", + "100", + "10" + ], + [ + "128", + "101", + "100", + "102", + "23.7500" + ], + [ + "128", + "101", + "100", + "103", + "23.75000000" + ], + [ + "128", + "101", + "100", + "105", + "0" + ], + [ + "128", + "101", + "100", + "50000", + "0" + ], + [ + "128", + "101", + "100", + "50001", + "0" + ], + [ + "128", + "101", + "100", + "50002", + "0" + ], + [ + "128", + "200000", + "100", + "100", + "20.1875" + ], + [ + "128", + "200000", + "100", + "102", + "20.1873" + ], + [ + "128", + "200000", + "100", + "103", + "20.18750000" + ], + [ + "129", + "101", + "100", + "100", + "16" + ], + [ + "129", + "101", + "100", + "102", + "38.0000" + ], + [ + "129", + "101", + "100", + "103", + "38.00000000" + ], + [ + "129", + "101", + "100", + "105", + "0" + ], + [ + "129", + "101", + "100", + "50000", + "0" + ], + [ + "129", + "101", + "100", + "50001", + "0" + ], + [ + "129", + "101", + "100", + "50002", + "0" + ], + [ + "129", + "200000", + "100", + "100", + "32.3000" + ], + [ + "129", + "200000", + "100", + "102", + "32.3000" + ], + [ + "129", + "200000", + "100", + "103", + "32.30000000" + ], + [ + "130", + "101", + "100", + "100", + "20" + ], + [ + "130", + "101", + "100", + "102", + "35.0000" + ], + [ + "130", + "101", + "100", + "103", + "37.36842105" + ], + [ + "130", + "101", + "100", + "105", + "0" + ], + [ + "130", + "101", + "100", + "50000", + "0" + ], + [ + "130", + "101", + "100", + "50001", + "0" + ], + [ + "130", + "101", + "100", + "50002", + "0" + ], + [ + "130", + "200000", + "100", + "100", + "25.5000" + ], + [ + "130", + "200000", + "100", + "102", + "29.7500" + ], + [ + "130", + "200000", + "100", + "103", + "31.76315789" + ], + [ + "131", + "101", + "100", + "100", + "0" + ], + [ + "131", + "101", + "100", + "105", + "0" + ], + [ + "131", + "101", + "100", + "50000", + "0" + ], + [ + "131", + "101", + "100", + "50001", + "0" + ], + [ + "131", + "101", + "100", + "50002", + "0" + ], + [ + "132", + "101", + "100", + "100", + "0" + ], + [ + "132", + "101", + "100", + "105", + "0" + ], + [ + "132", + "101", + "100", + "50000", + "0" + ], + [ + "132", + "101", + "100", + "50001", + "0" + ], + [ + "132", + "101", + "100", + "50002", + "0" + ], + [ + "133", + "101", + "100", + "100", + "0" + ], + [ + "133", + "101", + "100", + "102", + "0.0" + ], + [ + "133", + "101", + "100", + "103", + "33.75000000" + ], + [ + "133", + "101", + "100", + "105", + "0" + ], + [ + "133", + "101", + "100", + "50000", + "0" + ], + [ + "133", + "101", + "100", + "50001", + "0" + ], + [ + "133", + "101", + "100", + "50002", + "0" + ], + [ + "133", + "200000", + "100", + "100", + "28.6875" + ], + [ + "133", + "200000", + "100", + "102", + "0.0" + ], + [ + "133", + "200000", + "100", + "103", + "28.68750000" + ], + [ + "134", + "101", + "100", + "100", + "54.000000000000" + ], + [ + "134", + "101", + "100", + "102", + "0.0" + ], + [ + "134", + "101", + "100", + "103", + "67.50000000" + ], + [ + "134", + "101", + "100", + "105", + "0" + ], + [ + "134", + "101", + "100", + "50000", + "0" + ], + [ + "134", + "101", + "100", + "50001", + "0" + ], + [ + "134", + "101", + "100", + "50002", + "0" + ], + [ + "134", + "200000", + "100", + "100", + "57.3750" + ], + [ + "134", + "200000", + "100", + "102", + "0.0" + ], + [ + "134", + "200000", + "100", + "103", + "57.37500000" + ], + [ + "135", + "101", + "100", + "100", + "18.000000000000" + ], + [ + "135", + "101", + "100", + "102", + "0.0" + ], + [ + "135", + "101", + "100", + "103", + "22.50000000" + ], + [ + "135", + "101", + "100", + "105", + "0" + ], + [ + "135", + "101", + "100", + "50000", + "0" + ], + [ + "135", + "101", + "100", + "50001", + "0" + ], + [ + "135", + "101", + "100", + "50002", + "0" + ], + [ + "135", + "200000", + "100", + "100", + "19.1250" + ], + [ + "135", + "200000", + "100", + "102", + "0.0" + ], + [ + "135", + "200000", + "100", + "103", + "19.12500000" + ], + [ + "136", + "101", + "100", + "100", + "18.0000" + ], + [ + "136", + "101", + "100", + "102", + "18.0000" + ], + [ + "136", + "101", + "100", + "103", + "18.00000000" + ], + [ + "136", + "101", + "100", + "105", + "0" + ], + [ + "136", + "101", + "100", + "50000", + "0" + ], + [ + "136", + "101", + "100", + "50001", + "0" + ], + [ + "136", + "101", + "100", + "50002", + "0" + ], + [ + "136", + "200000", + "100", + "100", + "15.3000" + ], + [ + "136", + "200000", + "100", + "102", + "15.3000" + ], + [ + "136", + "200000", + "100", + "103", + "15.30000000" + ], + [ + "137", + "101", + "100", + "100", + "2.7000" + ], + [ + "137", + "101", + "100", + "102", + "2.7000" + ], + [ + "137", + "101", + "100", + "103", + "2.70000000" + ], + [ + "137", + "101", + "100", + "105", + "0" + ], + [ + "137", + "101", + "100", + "50000", + "0" + ], + [ + "137", + "101", + "100", + "50001", + "0" + ], + [ + "137", + "101", + "100", + "50002", + "0" + ], + [ + "137", + "200000", + "100", + "100", + "2.2950" + ], + [ + "137", + "200000", + "100", + "102", + "2.2950" + ], + [ + "137", + "200000", + "100", + "103", + "2.29500000" + ], + [ + "138", + "101", + "100", + "100", + "12.7500" + ], + [ + "138", + "101", + "100", + "102", + "12.7500" + ], + [ + "138", + "101", + "100", + "103", + "12.75000000" + ], + [ + "138", + "101", + "100", + "105", + "0" + ], + [ + "138", + "101", + "100", + "50000", + "0" + ], + [ + "138", + "101", + "100", + "50001", + "0" + ], + [ + "138", + "101", + "100", + "50002", + "0" + ], + [ + "138", + "200000", + "100", + "100", + "10.8375" + ], + [ + "138", + "200000", + "100", + "102", + "10.8375" + ], + [ + "138", + "200000", + "100", + "103", + "10.83750000" + ], + [ + "139", + "101", + "100", + "100", + "7.6500" + ], + [ + "139", + "101", + "100", + "102", + "7.6500" + ], + [ + "139", + "101", + "100", + "103", + "7.65000000" + ], + [ + "139", + "101", + "100", + "105", + "0" + ], + [ + "139", + "101", + "100", + "50000", + "0" + ], + [ + "139", + "101", + "100", + "50001", + "0" + ], + [ + "139", + "101", + "100", + "50002", + "0" + ], + [ + "139", + "200000", + "100", + "100", + "6.5025" + ], + [ + "139", + "200000", + "100", + "102", + "6.5027" + ], + [ + "139", + "200000", + "100", + "103", + "6.50250000" + ], + [ + "140", + "101", + "100", + "100", + "10.2000" + ], + [ + "140", + "101", + "100", + "102", + "10.2000" + ], + [ + "140", + "101", + "100", + "103", + "10.20000000" + ], + [ + "140", + "101", + "100", + "105", + "0" + ], + [ + "140", + "101", + "100", + "50000", + "0" + ], + [ + "140", + "101", + "100", + "50001", + "0" + ], + [ + "140", + "101", + "100", + "50002", + "0" + ], + [ + "140", + "200000", + "100", + "100", + "8.6700" + ], + [ + "140", + "200000", + "100", + "102", + "8.6700" + ], + [ + "140", + "200000", + "100", + "103", + "8.67000000" + ], + [ + "141", + "101", + "100", + "100", + "2.5500" + ], + [ + "141", + "101", + "100", + "102", + "2.5500" + ], + [ + "141", + "101", + "100", + "103", + "2.55000000" + ], + [ + "141", + "101", + "100", + "105", + "0" + ], + [ + "141", + "101", + "100", + "50000", + "0" + ], + [ + "141", + "101", + "100", + "50001", + "0" + ], + [ + "141", + "101", + "100", + "50002", + "0" + ], + [ + "141", + "200000", + "100", + "100", + "2.1675" + ], + [ + "141", + "200000", + "100", + "102", + "2.1677" + ], + [ + "141", + "200000", + "100", + "103", + "2.16750000" + ], + [ + "142", + "101", + "100", + "100", + "2.9750" + ], + [ + "142", + "101", + "100", + "102", + "2.9750" + ], + [ + "142", + "101", + "100", + "103", + "2.97500000" + ], + [ + "142", + "101", + "100", + "105", + "0" + ], + [ + "142", + "101", + "100", + "50000", + "0" + ], + [ + "142", + "101", + "100", + "50001", + "0" + ], + [ + "142", + "101", + "100", + "50002", + "0" + ], + [ + "142", + "200000", + "100", + "100", + "2.5288" + ], + [ + "142", + "200000", + "100", + "102", + "2.5287" + ], + [ + "142", + "200000", + "100", + "103", + "2.52880000" + ], + [ + "143", + "101", + "100", + "100", + "25.5000" + ], + [ + "143", + "101", + "100", + "102", + "25.5000" + ], + [ + "143", + "101", + "100", + "103", + "25.50000000" + ], + [ + "143", + "101", + "100", + "105", + "0" + ], + [ + "143", + "101", + "100", + "50000", + "0" + ], + [ + "143", + "101", + "100", + "50001", + "0" + ], + [ + "143", + "101", + "100", + "50002", + "0" + ], + [ + "143", + "200000", + "100", + "100", + "21.6750" + ], + [ + "143", + "200000", + "100", + "102", + "21.6750" + ], + [ + "143", + "200000", + "100", + "103", + "21.67500000" + ], + [ + "144", + "101", + "100", + "100", + "29.7500" + ], + [ + "144", + "101", + "100", + "102", + "29.7500" + ], + [ + "144", + "101", + "100", + "103", + "29.75000000" + ], + [ + "144", + "101", + "100", + "105", + "0" + ], + [ + "144", + "101", + "100", + "50000", + "0" + ], + [ + "144", + "101", + "100", + "50001", + "0" + ], + [ + "144", + "101", + "100", + "50002", + "0" + ], + [ + "144", + "200000", + "100", + "100", + "25.2875" + ], + [ + "144", + "200000", + "100", + "102", + "25.2875" + ], + [ + "144", + "200000", + "100", + "103", + "25.28750000" + ], + [ + "145", + "101", + "100", + "100", + "0" + ], + [ + "145", + "101", + "100", + "105", + "0" + ], + [ + "145", + "101", + "100", + "50000", + "0" + ], + [ + "145", + "101", + "100", + "50001", + "0" + ], + [ + "145", + "101", + "100", + "50002", + "0" + ], + [ + "146", + "101", + "100", + "100", + "0" + ], + [ + "146", + "101", + "100", + "105", + "0" + ], + [ + "146", + "101", + "100", + "50000", + "0" + ], + [ + "146", + "101", + "100", + "50001", + "0" + ], + [ + "146", + "101", + "100", + "50002", + "0" + ], + [ + "147", + "101", + "100", + "100", + "0" + ], + [ + "147", + "101", + "100", + "105", + "0" + ], + [ + "147", + "101", + "100", + "50000", + "0" + ], + [ + "147", + "101", + "100", + "50001", + "0" + ], + [ + "147", + "101", + "100", + "50002", + "0" + ], + [ + "148", + "101", + "100", + "100", + "0" + ], + [ + "148", + "101", + "100", + "105", + "0" + ], + [ + "148", + "101", + "100", + "50000", + "0" + ], + [ + "148", + "101", + "100", + "50001", + "0" + ], + [ + "148", + "101", + "100", + "50002", + "0" + ], + [ + "50000", + "101", + "100", + "100", + "0" + ], + [ + "50000", + "101", + "100", + "105", + "0" + ], + [ + "50000", + "101", + "100", + "50000", + "0" + ], + [ + "50000", + "101", + "100", + "50001", + "0" + ], + [ + "50000", + "101", + "100", + "50002", + "0" + ], + [ + "50001", + "101", + "100", + "100", + "0" + ], + [ + "50001", + "101", + "100", + "105", + "0" + ], + [ + "50001", + "101", + "100", + "50000", + "0" + ], + [ + "50001", + "101", + "100", + "50001", + "0" + ], + [ + "50001", + "101", + "100", + "50002", + "0" + ], + [ + "50002", + "101", + "100", + "100", + "0" + ], + [ + "50002", + "101", + "100", + "105", + "0" + ], + [ + "50002", + "101", + "100", + "50000", + "0" + ], + [ + "50002", + "101", + "100", + "50001", + "0" + ], + [ + "50002", + "101", + "100", + "50002", + "0" + ], + [ + "50003", + "101", + "100", + "100", + "0" + ], + [ + "50003", + "101", + "100", + "105", + "0" + ], + [ + "50003", + "101", + "100", + "50000", + "0" + ], + [ + "50003", + "101", + "100", + "50001", + "0" + ], + [ + "50003", + "101", + "100", + "50002", + "0" + ], + [ + "50004", + "101", + "100", + "100", + "0" + ], + [ + "50004", + "101", + "100", + "105", + "0" + ], + [ + "50004", + "101", + "100", + "50000", + "0" + ], + [ + "50004", + "101", + "100", + "50001", + "0" + ], + [ + "50004", + "101", + "100", + "50002", + "0" + ], + [ + "50005", + "101", + "100", + "100", + "0" + ], + [ + "50005", + "101", + "100", + "105", + "0" + ], + [ + "50005", + "101", + "100", + "50000", + "0" + ], + [ + "50005", + "101", + "100", + "50001", + "0" + ], + [ + "50005", + "101", + "100", + "50002", + "0" + ], + [ + "50007", + "101", + "100", + "100", + "0" + ], + [ + "50007", + "101", + "100", + "105", + "0" + ], + [ + "50007", + "101", + "100", + "50000", + "0" + ], + [ + "50007", + "101", + "100", + "50001", + "0" + ], + [ + "50007", + "101", + "100", + "50002", + "0" + ], + [ + "50008", + "101", + "100", + "100", + "0" + ], + [ + "50008", + "101", + "100", + "105", + "0" + ], + [ + "50008", + "101", + "100", + "50000", + "0" + ], + [ + "50008", + "101", + "100", + "50001", + "0" + ], + [ + "50008", + "101", + "100", + "50002", + "0" + ], + [ + "50009", + "101", + "100", + "100", + "0" + ], + [ + "50009", + "101", + "100", + "105", + "0" + ], + [ + "50009", + "101", + "100", + "50000", + "0" + ], + [ + "50009", + "101", + "100", + "50001", + "0" + ], + [ + "50009", + "101", + "100", + "50002", + "0" + ], + [ + "50010", + "101", + "100", + "100", + "0" + ], + [ + "50010", + "101", + "100", + "105", + "0" + ], + [ + "50010", + "101", + "100", + "50000", + "0" + ], + [ + "50010", + "101", + "100", + "50001", + "0" + ], + [ + "50010", + "101", + "100", + "50002", + "0" + ], + [ + "50012", + "101", + "100", + "100", + "0" + ], + [ + "50012", + "101", + "100", + "105", + "0" + ], + [ + "50012", + "101", + "100", + "50000", + "0" + ], + [ + "50012", + "101", + "100", + "50001", + "0" + ], + [ + "50012", + "101", + "100", + "50002", + "0" + ], + [ + "50013", + "101", + "100", + "100", + "0" + ], + [ + "50013", + "101", + "100", + "105", + "0" + ], + [ + "50013", + "101", + "100", + "50000", + "0" + ], + [ + "50013", + "101", + "100", + "50001", + "0" + ], + [ + "50013", + "101", + "100", + "50002", + "0" + ], + [ + "50014", + "101", + "100", + "100", + "0" + ], + [ + "50014", + "101", + "100", + "105", + "0" + ], + [ + "50014", + "101", + "100", + "50000", + "0" + ], + [ + "50014", + "101", + "100", + "50001", + "0" + ], + [ + "50014", + "101", + "100", + "50002", + "0" + ], + [ + "50015", + "101", + "100", + "100", + "0" + ], + [ + "50015", + "101", + "100", + "105", + "0" + ], + [ + "50015", + "101", + "100", + "50000", + "0" + ], + [ + "50015", + "101", + "100", + "50001", + "0" + ], + [ + "50015", + "101", + "100", + "50002", + "0" + ], + [ + "50016", + "101", + "100", + "100", + "0" + ], + [ + "50016", + "101", + "100", + "105", + "0" + ], + [ + "50016", + "101", + "100", + "50000", + "0" + ], + [ + "50016", + "101", + "100", + "50001", + "0" + ], + [ + "50016", + "101", + "100", + "50002", + "0" + ], + [ + "50017", + "101", + "100", + "100", + "0" + ], + [ + "50017", + "101", + "100", + "105", + "0" + ], + [ + "50017", + "101", + "100", + "50000", + "0" + ], + [ + "50017", + "101", + "100", + "50001", + "0" + ], + [ + "50017", + "101", + "100", + "50002", + "0" + ], + [ + "50018", + "101", + "100", + "100", + "0" + ], + [ + "50018", + "101", + "100", + "105", + "0" + ], + [ + "50018", + "101", + "100", + "50000", + "0" + ], + [ + "50018", + "101", + "100", + "50001", + "0" + ], + [ + "50018", + "101", + "100", + "50002", + "0" + ], + [ + "50019", + "101", + "100", + "100", + "0" + ], + [ + "50019", + "101", + "100", + "105", + "0" + ], + [ + "50019", + "101", + "100", + "50000", + "0" + ], + [ + "50019", + "101", + "100", + "50001", + "0" + ], + [ + "50019", + "101", + "100", + "50002", + "0" + ], + [ + "50020", + "101", + "100", + "100", + "0" + ], + [ + "50020", + "101", + "100", + "105", + "0" + ], + [ + "50020", + "101", + "100", + "50000", + "0" + ], + [ + "50020", + "101", + "100", + "50001", + "0" + ], + [ + "50020", + "101", + "100", + "50002", + "0" + ], + [ + "50021", + "101", + "100", + "100", + "0" + ], + [ + "50021", + "101", + "100", + "105", + "0" + ], + [ + "50021", + "101", + "100", + "50000", + "0" + ], + [ + "50021", + "101", + "100", + "50001", + "0" + ], + [ + "50021", + "101", + "100", + "50002", + "0" + ], + [ + "50022", + "101", + "100", + "100", + "0" + ], + [ + "50022", + "101", + "100", + "105", + "0" + ], + [ + "50022", + "101", + "100", + "50000", + "0" + ], + [ + "50022", + "101", + "100", + "50001", + "0" + ], + [ + "50022", + "101", + "100", + "50002", + "0" + ], + [ + "50023", + "101", + "100", + "100", + "0" + ], + [ + "50023", + "101", + "100", + "105", + "0" + ], + [ + "50023", + "101", + "100", + "50000", + "0" + ], + [ + "50023", + "101", + "100", + "50001", + "0" + ], + [ + "50023", + "101", + "100", + "50002", + "0" + ], + [ + "50024", + "101", + "100", + "100", + "0" + ], + [ + "50024", + "101", + "100", + "105", + "0" + ], + [ + "50024", + "101", + "100", + "50000", + "0" + ], + [ + "50024", + "101", + "100", + "50001", + "0" + ], + [ + "50024", + "101", + "100", + "50002", + "0" + ], + [ + "50025", + "101", + "100", + "100", + "0" + ], + [ + "50025", + "101", + "100", + "105", + "0" + ], + [ + "50025", + "101", + "100", + "50000", + "0" + ], + [ + "50025", + "101", + "100", + "50001", + "0" + ], + [ + "50025", + "101", + "100", + "50002", + "0" + ], + [ + "50026", + "101", + "100", + "100", + "0" + ], + [ + "50026", + "101", + "100", + "105", + "0" + ], + [ + "50026", + "101", + "100", + "50000", + "0" + ], + [ + "50026", + "101", + "100", + "50001", + "0" + ], + [ + "50026", + "101", + "100", + "50002", + "0" + ], + [ + "50027", + "101", + "100", + "100", + "0" + ], + [ + "50027", + "101", + "100", + "105", + "0" + ], + [ + "50027", + "101", + "100", + "50000", + "0" + ], + [ + "50027", + "101", + "100", + "50001", + "0" + ], + [ + "50027", + "101", + "100", + "50002", + "0" + ], + [ + "200001", + "101", + "100", + "100", + "0.0" + ], + [ + "200002", + "101", + "100", + "100", + "0.0" + ] + ] + }, + "m_costelement": { + "cols": [ + "m_costelement_id", + "costingmethod", + "costelementtype" + ], + "rows": [ + [ + "100", + "S", + "M" + ], + [ + "101", + "", + "M" + ], + [ + "102", + "F", + "M" + ], + [ + "103", + "A", + "M" + ], + [ + "104", + "I", + "M" + ], + [ + "105", + "S", + "R" + ], + [ + "50000", + "S", + "B" + ], + [ + "50001", + "S", + "O" + ], + [ + "50002", + "S", + "X" + ] + ] + }, + "c_acctschema": { + "cols": [ + "c_acctschema_id", + "c_currency_id", + "costingmethod", + "m_costtype_id" + ], + "rows": [ + [ + "101", + "100", + "A", + "100" + ], + [ + "200000", + "102", + "A", + "100" + ] + ] + }, + "c_conversion_rate": { + "cols": [ + "c_currency_id", + "c_currency_id_to", + "c_conversiontype_id", + "multiplyrate", + "validfrom", + "validto", + "ad_client_id", + "ad_org_id" + ], + "rows": [ + [ + "100", + "102", + "114", + "0.85", + "2000-01-01", + "2056-01-29", + "11", + "0" + ], + [ + "102", + "100", + "114", + "1.17647058823529411764705882352941176471", + "2000-01-01", + "2056-01-29", + "11", + "0" + ] + ] + }, + "c_conversiontype": { + "cols": [ + "c_conversiontype_id", + "isdefault" + ], + "rows": [ + [ + "114", + "Y" + ], + [ + "115", + "N" + ], + [ + "200", + "N" + ], + [ + "201", + "N" + ] + ] + }, + "c_charge_acct": { + "cols": [ + "c_charge_id", + "c_acctschema_id", + "ch_expense_acct" + ], + "rows": [ + [ + "100", + "101", + "267" + ], + [ + "100", + "200000", + "200061" + ], + [ + "101", + "101", + "297" + ], + [ + "101", + "200000", + "200061" + ], + [ + "200000", + "101", + "200017" + ], + [ + "200000", + "200000", + "200061" + ] + ] + } + } +} \ No newline at end of file diff --git a/build/poc_fixture_ensemble_sc.js b/build/poc_fixture_ensemble_sc.js new file mode 100644 index 000000000..e2fe8ef3c --- /dev/null +++ b/build/poc_fixture_ensemble_sc.js @@ -0,0 +1,110 @@ +'use strict'; +// ⚠ DO NOT REMOVE — POC-FIXTURE-ENSEMBLE scope (READ THE LOG after every run) +// SCOPE: prompts/FUNCTIONAL_SPACES_ENSEMBLE.md Step 2 — POC whether classifyRoomWithFixtures() +// (build/room_type_classifier.js, 2026-07-13, symbiotic fixture+Gaussian) improves the 55-row +// carried SampleCastle room set (pure flood-fill, predefined_type INTERNAL/INTERNAL_SMALL, no +// semantic type). DISCIPLINE (standing, all day): validate on Duplex ground truth FIRST — a +// landed building with exact truth is the only place a new signal's CORRECTNESS is checkable; +// SampleCastle can only show self-consistency. Report honest fractions, no forcing. +// P1 DUPLEX-REPLAY — re-run the fixture+size ensemble over Duplex's 21 real rooms (truth = +// the human-authored object_type labels) against the SAME patched worktree DBs Step 1 +// restored — expect the recorded 18/18 on fixture-bearing rooms to reproduce. +// P2 SC-EVIDENCE — census SampleCastle's extraction for ANY fixture evidence the keyword +// table could ever match (fixture-family classes + sanitary/kitchen name keywords). +// P3 SC-ENSEMBLE — run the ensemble over the 51 carried rooms; report typed-by-fixture / +// typed-by-gaussian / unclassified fractions. +// RUN: node build/poc_fixture_ensemble_sc.js (from bim-compiler root) +const fs = require('fs'), path = require('path'); +const yaml = require('js-yaml'); +const initSqlJs = require('/home/red1/bim-compiler/node_modules/sql.js'); +const C = require('./room_type_classifier.js'); + +const WT = '/tmp/wt-functional-spaces/modeller'; // Step-1-patched worktree DBs (fix/samplecastle-spatial-carry) +const CONFIG = yaml.load(fs.readFileSync(path.join(__dirname, '..', 'config', 'room_templates.yaml'), 'utf8')); +// fixture-family classes, per COMPILE_ROOMS_TYPE_INFERENCE.md §1 (checked across all 8 buildings): +const FIXTURE_CLASSES = "('IfcFurnishingElement','IfcFurniture','IfcBuildingElementProxy','IfcSanitaryTerminal')"; + +function rows(db, sql) { + const r = db.exec(sql); + if (!r.length) return []; + const cols = r[0].columns; + return r[0].values.map(v => { const o = {}; cols.forEach((c, i) => o[c] = v[i]); return o; }); +} +function openDb(SQL, f) { return new SQL.Database(new Uint8Array(fs.readFileSync(path.join(WT, f)))); } + +function roomSet(db) { + // one logical room per guid (carried sets have no room_guid col — 1 row = 1 room) + return rows(db, "SELECT guid, name, object_type, predefined_type, center_x, center_y, size_x, size_y" + + " FROM spatial_structure WHERE type='IfcSpace' AND center_x IS NOT NULL AND size_x IS NOT NULL"); +} +function fixtures(db) { + return rows(db, "SELECT m.element_name name, t.center_x, t.center_y FROM elements_meta m" + + " JOIN element_transforms t ON t.guid=m.guid WHERE m.ifc_class IN " + FIXTURE_CLASSES + + " AND t.center_x IS NOT NULL"); +} +function classify(room, fx) { + const rects = [{ center_x: room.center_x, center_y: room.center_y, size_x: room.size_x, size_y: room.size_y }]; + const hits = C.fixturesInRoom(rects, fx); + const feats = C.featuresFromRects(rects); + const res = C.classifyRoomWithFixtures(feats, hits, CONFIG); + return { res, hits }; +} + +(async () => { + const SQL = await initSqlJs(); + const out = []; + const S = m => { out.push(m); console.log(m); }; + + // ── P1: Duplex ground-truth forward-replay ────────────────────────────── + const dpx = openDb(SQL, 'Duplex_ARC.db'); + const dRooms = roomSet(dpx); + const dFx = fixtures(dpx); + S('§P1 DUPLEX rooms=' + dRooms.length + ' fixtureElems=' + dFx.length); + let withFx = 0, okFx = 0, mism = []; + const templateTypes = new Set(Object.keys(CONFIG.templates)); + dRooms.forEach(r => { + const truthRaw = r.object_type || r.name; // human-authored LongName, e.g. 'Bathroom 2' + const truth = C.normalizeLabel(truthRaw); + const { res, hits } = classify(r, dFx); + const line = ' §P1_ROOM ' + (r.object_type || r.name) + ' truth=' + truth + + ' -> ' + (res.type || 'UNCLASSIFIED') + ' src=' + (res.source || '?') + ' fixtures=[' + hits.join('; ') + ']'; + S(line); + if (hits.length) { + withFx++; + // correctness is only checkable where truth itself is a template type + if (templateTypes.has(truth)) { + if (res.type === truth) okFx++; else mism.push(truthRaw + '->' + res.type); + } else { okFx++; } // fixture present, truth not a template type — count as non-contradicting only if not forced + } + }); + S('§P1 RESULT fixture-bearing rooms=' + withFx + ' correct=' + okFx + ' mismatches=' + (mism.join(',') || 'none')); + + // ── P2: SampleCastle fixture-evidence census ──────────────────────────── + const sc = openDb(SQL, 'SampleCastle_ARC.db'); + const scFxAll = fixtures(sc); + const kw = [].concat(...Object.values(C.FIXTURE_KEYWORDS)); + const kwHits = rows(sc, "SELECT ifc_class, element_name, COUNT(*) n FROM elements_meta WHERE " + + kw.map(k => "LOWER(element_name) LIKE '%" + k + "%'").join(' OR ') + " GROUP BY 1,2"); + S('§P2 SC fixture-family elems=' + scFxAll.length + ' names=' + + JSON.stringify([...new Set(scFxAll.map(f => f.name))]) + ' keywordHits=' + JSON.stringify(kwHits)); + + // ── P3: SampleCastle ensemble over the 51 carried rooms ───────────────── + const scRooms = roomSet(sc); + let byFixture = 0, byGaussian = 0, unclass = 0, fxRooms = 0; + const typeCount = {}; + scRooms.forEach(r => { + const { res, hits } = classify(r, scFxAll); + if (hits.length) fxRooms++; + if (res.unclassified || !res.type) unclass++; + else if ((res.source || '').indexOf('fixture') === 0) byFixture++; + else byGaussian++; + const t = res.unclassified || !res.type ? 'UNCLASSIFIED' : res.type; + typeCount[t] = (typeCount[t] || 0) + 1; + }); + S('§P3 SC rooms=' + scRooms.length + ' roomsWithAnyFixtureInside=' + fxRooms + + ' typedByFixture=' + byFixture + ' typedByGaussianOnly=' + byGaussian + ' unclassified=' + unclass); + S('§P3 SC typeDistribution=' + JSON.stringify(typeCount)); + + fs.writeFileSync(path.join(__dirname, 'poc_fixture_ensemble_sc.log'), out.join('\n') + '\n'); + S('§POC-FIXTURE-ENSEMBLE done — log: build/poc_fixture_ensemble_sc.log'); +})().catch(e => { console.error('FATAL', e); process.exit(2); }); diff --git a/build/room_type_classifier.js b/build/room_type_classifier.js index 2897307d8..a403f6f95 100644 --- a/build/room_type_classifier.js +++ b/build/room_type_classifier.js @@ -161,6 +161,100 @@ return { type: best.type, confidence: best.posterior, unclassified: false, z: best.z, scores: scores }; } + // §FIXTURE-SIGNAL (2026-07-13, RosettaStone discipline — extract real evidence before + // inferring from statistics): a named IfcFurnishingElement/IfcBuildingElementProxy INSIDE a + // room's own rect-set is direct extracted proof of that room's function, not a guess from + // size/aspect. Reverse-engineered from the Stones (ground truth), forward-replayed against the + // same Stone before use: checked every real sink/vanity fixture's XY position against Duplex's + // own human-labeled ground-truth rooms — 7/7 landed in the CORRECT real room (2/2 "Counter Top + // w Sink Hole" -> Kitchen, 5/5 "Vanity Cabinet...Sink Unit" -> Bathroom), 0 misses, 0 invented. + // "toilet"/"urinal"/"bidet" added from Terminal's own real extraction (4x "Asian_Toilet" proxy + // elements) — not asserted, a second building's real element-naming convention. + // This signal WINS over the Gaussian area/aspect template when both fire: extracted evidence + // outranks statistical inference from a 2-5 sample residential fit, same PRIME RULE priority + // as every other decision in this codebase (extract/compile over invent). + var FIXTURE_KEYWORDS = { + BATHROOM: ['vanity', 'toilet', 'urinal', 'bidet'], + KITCHEN: ['sink hole', 'hob', 'cooktop', 'stove top'] + }; + function fixtureRoomType(fixtureNames) { + if (!fixtureNames || !fixtureNames.length) return null; + var types = Object.keys(FIXTURE_KEYWORDS); + for (var i = 0; i < fixtureNames.length; i++) { + var n = String(fixtureNames[i] || '').toLowerCase(); + for (var t = 0; t < types.length; t++) { + var kws = FIXTURE_KEYWORDS[types[t]]; + for (var k = 0; k < kws.length; k++) { + if (n.indexOf(kws[k]) >= 0) return types[t]; + } + } + } + return null; + } + + // fixturesInRoom(rects, fixtures) — fixtures: [{name, center_x, center_y}]. Point-in-rect + // containment, no buffer (furniture is placed INSIDE a room, unlike a door which sits ON the + // boundary and needs compile_rooms.py's DOOR_BUFFER_SLACK). Returns the matched names found. + function fixturesInRoom(rects, fixtures) { + var hits = []; + if (!rects || !rects.length || !fixtures || !fixtures.length) return hits; + for (var i = 0; i < fixtures.length; i++) { + var f = fixtures[i]; + if (f.center_x == null || f.center_y == null) continue; + for (var j = 0; j < rects.length; j++) { + var r = rects[j]; + if (r.center_x == null || r.size_x == null) continue; + var rx0 = r.center_x - r.size_x / 2, rx1 = r.center_x + r.size_x / 2; + var ry0 = r.center_y - r.size_y / 2, ry1 = r.center_y + r.size_y / 2; + if (f.center_x >= rx0 && f.center_x <= rx1 && f.center_y >= ry0 && f.center_y <= ry1) { + hits.push(f.name); break; + } + } + } + return hits; + } + + // classifyRoomWithFixtures(features, fixtureNames, config) — SYMBIOTIC combination, not a + // one-sided override (2026-07-13, corrected after a real forward-replay miss on Duplex ground + // truth: 2 real UTILITY rooms both contain a "Counter Top w Sink Hole" fixture, which naively + // matched the KITCHEN keyword — a laundry/utility sink is real, "sink hole" alone isn't + // kitchen-exclusive). Extracted fixture evidence and the Gaussian area/aspect score must AGREE: + // a fixture only confirms its suggested type if that type's OWN z-score is still plausible for + // the room's actual measured size (checked against config.unclassified_z_threshold, the same + // gate classifyRoom uses) — both mismatched Utility rooms are ~1.4m^2, UTILITY's own template + // is a near-exact match (mean 1.408 std 0.012) while KITCHEN's z is enormous (mean 12.95) at + // that size, so the symbiotic gate correctly defers to the Gaussian's UTILITY answer instead of + // trusting the ambiguous keyword alone. When they DO agree, the extracted fixture is still the + // stronger claim (real evidence, not inference), so confidence is reported high but the + // underlying z is always shown, never hidden. + function classifyRoomWithFixtures(features, fixtureNames, config) { + var fType = fixtureRoomType(fixtureNames); + var gaussian = features ? classifyRoom(features, config) : null; + var threshold = config.unclassified_z_threshold != null ? config.unclassified_z_threshold : 2.0; + if (fType && gaussian && gaussian.scores && gaussian.scores.length) { + var fScore = null; + for (var i = 0; i < gaussian.scores.length; i++) { + if (gaussian.scores[i].type === fType) { fScore = gaussian.scores[i]; break; } + } + if (fScore && fScore.z <= threshold) { + var why = 'fixture:' + fType + ' confirmed by size (z=' + fScore.z.toFixed(2) + '<=' + threshold + ')'; + if (gaussian.type && gaussian.type !== fType) why += ' (gaussian alone preferred ' + gaussian.type + ')'; + return { type: fType, confidence: 0.95, unclassified: false, why: why, z: fScore.z, source: 'fixture+size' }; + } + gaussian.why = (gaussian.why || '') + ' | fixture ' + fType + ' rejected: z=' + + (fScore ? fScore.z.toFixed(2) : '?') + '>' + threshold + ' (keyword ambiguous for this room size)'; + gaussian.source = 'gaussian (fixture keyword overruled by size)'; + return gaussian; + } + if (fType && !gaussian) { + // no geometry available to cross-check against — fixture evidence alone, lower confidence + return { type: fType, confidence: 0.7, unclassified: false, why: 'fixture:' + fType + ' (no geometry to cross-check)', source: 'fixture-only' }; + } + var g = gaussian || classifyRoom(features, config); + g.source = 'gaussian'; + return g; + } + // loadTemplateConfig(parsedYaml) — thin validation/normalization pass over a js-yaml-parsed // config/room_templates.yaml object. Does NOT parse YAML itself (kept dependency-free/dual-mode // — caller supplies the already-parsed object, e.g. via js-yaml in Node or a browser YAML lib). @@ -178,6 +272,10 @@ scoreTemplate: scoreTemplate, classifyRoom: classifyRoom, loadTemplateConfig: loadTemplateConfig, + FIXTURE_KEYWORDS: FIXTURE_KEYWORDS, + fixtureRoomType: fixtureRoomType, + fixturesInRoom: fixturesInRoom, + classifyRoomWithFixtures: classifyRoomWithFixtures, }; ROOT.RoomTypeClassifier = API; if (typeof module !== 'undefined' && module.exports) module.exports = API; diff --git a/build/room_walker.js b/build/room_walker.js index 4a3b1ed5c..7e86dce9b 100644 --- a/build/room_walker.js +++ b/build/room_walker.js @@ -21,9 +21,23 @@ var RES = 0.20; // grid cell size (m) var MIN_AREA = 4.0; // m^2 — drop slivers / wall cavities - var MAX_AREA_ABS = 150.0; // m^2 — drop exterior-leak blobs (a real room is rarely bigger) - var MAX_AREA_FRAC = 0.92; // also drop anything ~the whole storey plan + // §SUSPECT-LARGE (compile_rooms.py port, 2026-07-14): MAX_AREA_ABS used to be a hard drop + // threshold, calibrated to residential room sizes. That predates §DOOR-PARTITION-EXT-EXCLUDE, + // now the real leak detector — measured fleet-wide, real confirmed-interior pockets range + // 38-1544 m^2, so a fixed drop threshold is wrong for some real building (incl. "residential"- + // classed SampleCastle at 315 m^2). Repurposed: still compiles, flagged for review instead of + // silently vanishing. + var MAX_AREA_ABS = 150.0; // m^2 — SUSPECT_LARGE flag threshold, no longer a drop threshold + var MAX_AREA_FRAC = 0.92; // still a hard drop — self-scaling (% of THIS building's own storey plan) var SEAL = 2; // dilate walls this many cells (×RES) to close hairline corner/door gaps + // §RASTER-EPS (compile_rooms.py port — ROOM_WALKER_PHASE_INVARIANCE.md S1/S2, 2026-07-17): + // wall edges routinely sit EXACTLY on a RES cell boundary relative to the data-derived grid + // origin, so Math.floor((x-xs0)/RES) is a knife edge — translating the SAME geometry by a + // constant Δ perturbs (x-xs0) by ~1 ulp and flips those cells (measured: 8/14 translations + // changed Terminal's compile, rooms 50-54 vs baseline 51). Fix: floor(t + RASTER_EPS) for cell + // indices, ceil(t - RASTER_EPS) for grid extents. 1e-6 cell-fractions = 0.2 µm of geometry: + // >100x the worst FP error (~5e-9 cells at |x|=1e5 m), 5 orders below real coordinates. + var RASTER_EPS = 1e-6; // cell fractions — boundary snap band for raster quantization var WALL_LIKE = ["IfcWall%", "IfcDoor%", "IfcCurtainWall%", "IfcColumn%", "IfcWindow%"]; // §STAIR-EXCLUDE: a stairwell is a wall-enclosed pocket, so the flood-fill flags it as a "room". // It is circulation, NOT a room. Reject any compiled pocket that a stair footprint substantially @@ -234,15 +248,17 @@ }); var pad = RES * 2; xs0 -= pad; ys0 -= pad; xs1 += pad; ys1 += pad; - var nx = Math.max(4, Math.ceil((xs1 - xs0) / RES)); - var ny = Math.max(4, Math.ceil((ys1 - ys0) / RES)); + // §RASTER-EPS: ceil(t - eps) — an exact-multiple span gets the same cell count in any frame + var nx = Math.max(4, Math.ceil((xs1 - xs0) / RES - RASTER_EPS)); + var ny = Math.max(4, Math.ceil((ys1 - ys0) / RES - RASTER_EPS)); return { xs0: xs0, ys0: ys0, xs1: xs1, ys1: ys1, nx: nx, ny: ny }; } function _rasterizeWalls(walls, ext) { var nx = ext.nx, ny = ext.ny, xs0 = ext.xs0, ys0 = ext.ys0; - var ix = function (x) { return Math.min(nx - 1, Math.max(0, Math.floor((x - xs0) / RES))); }; - var iy = function (y) { return Math.min(ny - 1, Math.max(0, Math.floor((y - ys0) / RES))); }; + // §RASTER-EPS: floor(t + eps) — boundary-exact edges quantize identically in any frame + var ix = function (x) { return Math.min(nx - 1, Math.max(0, Math.floor((x - xs0) / RES + RASTER_EPS))); }; + var iy = function (y) { return Math.min(ny - 1, Math.max(0, Math.floor((y - ys0) / RES + RASTER_EPS))); }; var blocked = new Uint8Array(nx * ny); walls.forEach(function (w) { var cx = w[0], cy = w[1], bx = w[3], byv = w[4]; @@ -413,6 +429,43 @@ return { added: added, mni: mni, mxi: mxi, mnj: mnj, mxj: mxj }; } + // §WALL-SNAP (compile_rooms.py port, 2026-07-13): raster quantization (RES=0.20m) plus + // _growRegion's seal-band recovery cap (SEAL=2 cells=0.4m) leave a compiled room's rect short of + // its TRUE (continuous-coordinate) wall face. Measured across 208 real non-suspect room-sides + // fleet-wide (HHS): 0/208 ever overshoot a wall — every side is short by 0.003-0.599m. SNAP_MAX_GAP + // is the measured worst case (0.599m) plus one RES step of headroom, not an arbitrary number. Move + // each side OUT (never in) to the nearest real wall's own measured near face — each side only ever + // reads the wall's NEAR face, so two rooms sharing one real wall each stop at their own side of it + // and can never be made to overlap by this function (same non-invent discipline as R-MERGE/R-REJECT). + var SNAP_MAX_GAP = 0.8; // m + function _snapRectToWalls(x0, y0, x1, y1, walls) { + var best = {}; + for (var i = 0; i < walls.length; i++) { + var w = walls[i], wcx = w[0], wcy = w[1], wbx = w[3], wby = w[4]; + var wx0 = wcx - wbx / 2, wx1 = wcx + wbx / 2; + var wy0 = wcy - wby / 2, wy1 = wcy + wby / 2; + var ovY = Math.min(y1, wy1) - Math.max(y0, wy0); + if (ovY > 0) { + var gXmin = x0 - wx1; + if (gXmin >= 0 && gXmin <= SNAP_MAX_GAP && (best.xmin === undefined || gXmin < best.xmin)) best.xmin = gXmin; + var gXmax = wx0 - x1; + if (gXmax >= 0 && gXmax <= SNAP_MAX_GAP && (best.xmax === undefined || gXmax < best.xmax)) best.xmax = gXmax; + } + var ovX = Math.min(x1, wx1) - Math.max(x0, wx0); + if (ovX > 0) { + var gYmin = y0 - wy1; + if (gYmin >= 0 && gYmin <= SNAP_MAX_GAP && (best.ymin === undefined || gYmin < best.ymin)) best.ymin = gYmin; + var gYmax = wy0 - y1; + if (gYmax >= 0 && gYmax <= SNAP_MAX_GAP && (best.ymax === undefined || gYmax < best.ymax)) best.ymax = gYmax; + } + } + if (best.xmin !== undefined) x0 -= best.xmin; + if (best.xmax !== undefined) x1 += best.xmax; + if (best.ymin !== undefined) y0 -= best.ymin; + if (best.ymax !== undefined) y1 += best.ymax; + return [x0, y0, x1, y1]; + } + // §MULTI-RECT: constrained maximal rectangle — both dims >= minCells (the NOISE_FLOOR in cells; // a thinner rect is rasterization fringe, not room space). Null if no such rect exists. // Same deterministic scan order / strict '>' tie-break as _inscribedRect. @@ -486,6 +539,22 @@ return null; } + // §SUSPECT-ELONGATED (compile_rooms.py port, 2026-07-13): a wall-bounded or door-partitioned + // pocket can still be an absurdly long undivided span (real walls/doors on the enclosing sides, + // nothing dividing the middle). Threshold measured, not eyeballed: HHS's own 105 door-partitioned + // rooms had a clean bimodal aspect spread — 98 climb smoothly 1.00->7.50, then a hard gap to 7 + // outliers at 13.64->37.25 (R9 = 13.64, the smallest of the 7). SUSPECT_ELONGATED_ASPECT_MIN = + // midpoint of that gap: (7.50 + 13.64) / 2 = 10.57. Runs against BOTH floodRooms and + // partitionByDoors (a real HHS flood-fill room also came out 24.2m x 2.0m, aspect 12.1, proving + // wall-bounded rooms aren't immune either). A flagged room still compiles (never invented away) — + // same §ROOM-FORM treatment as SUSPECT_OPEN/SUSPECT_NO_DOOR. + var SUSPECT_ELONGATED_ASPECT_MIN = 10.57; + function _isElongated(wx0, wy0, wx1, wy1) { + var spanX = wx1 - wx0, spanY = wy1 - wy0; + var aspect = Math.max(spanX, spanY) / Math.max(Math.min(spanX, spanY), 0.01); + return aspect > SUSPECT_ELONGATED_ASPECT_MIN; + } + function floodRooms(walls, stairs, doors, doorWMed) { stairs = stairs || []; doors = doors || []; doorWMed = doorWMed || 0.0; var ext = _gridExtent(walls); @@ -525,7 +594,7 @@ }); } var area = comp.length * cellArea; - if (area > MAX_AREA_ABS || area > planArea * MAX_AREA_FRAC) continue; + if (area > planArea * MAX_AREA_FRAC) continue; // §SUSPECT-LARGE: MAX_AREA_ABS flags below, never drops var wx0 = xs0 + mni * RES, wx1 = xs0 + (mxi + 1) * RES; var wy0 = ys0 + mnj * RES, wy1 = ys0 + (mxj + 1) * RES; // §DOOR-RESCUE (abstract test, applies uniformly — not a size band): a pocket is a room if @@ -534,7 +603,11 @@ var doorRescued = false; var hasDoor = doorAdjacent(wx0, wy0, wx1, wy1, doors); if (area < MIN_AREA) { - var dimsOk = (wx1 - wx0) >= NOISE_FLOOR_DIM && (wy1 - wy0) >= NOISE_FLOOR_DIM; + // §RASTER-EPS: the noise-floor test is a CELL-COUNT rule (3 cells) — test it in integer + // cells, not in metres reconstructed from xs0+i*RES (whose FP dirt made a 3-cell pocket + // flip at exact equality with NOISE_FLOOR_DIM; same convention _decomposeRegion uses). + var minCellsNF = Math.round(NOISE_FLOOR_DIM / RES); + var dimsOk = (mxi - mni + 1) >= minCellsNF && (mxj - mnj + 1) >= minCellsNF; if (!(dimsOk && hasDoor)) continue; doorRescued = true; } @@ -547,6 +620,8 @@ for (c2 = 0; c2 < comp.length; c2++) inSet[comp[c2]] = 1; var openM = _openPerimeterM(comp, inSet, raw, dil, nx, ny, SEAL); var suspect = _classify(hasDoor, openM, doorWMed); + if (!suspect && _isElongated(wx0, wy0, wx1, wy1)) suspect = 'ELONGATED'; + if (!suspect && area > MAX_AREA_ABS) suspect = 'LARGE'; var gr = _growRegion(comp, inSet, raw, dil, nx, ny, SEAL); var totalCells = comp.length + gr.added.length; var dec = _decomposeRegion(inSet, ny, gr.mni, gr.mxi, gr.mnj, gr.mxj, totalCells, !!suspect); @@ -557,6 +632,8 @@ var gRect = dec.rects[c2]; var rx0 = xs0 + gRect[0] * RES, rx1 = xs0 + (gRect[1] + 1) * RES; var ry0 = ys0 + gRect[2] * RES, ry1 = ys0 + (gRect[3] + 1) * RES; + var snapped = _snapRectToWalls(rx0, ry0, rx1, ry1, walls); + rx0 = snapped[0]; ry0 = snapped[1]; rx1 = snapped[2]; ry1 = snapped[3]; rects.push({ cx: (rx0 + rx1) / 2, cy: (ry0 + ry1) / 2, sx: rx1 - rx0, sy: ry1 - ry0 }); } var r0 = dec.rects[0]; @@ -572,18 +649,63 @@ return rooms; } + // §DOOR-PARTITION-EXT-EXCLUDE (compile_rooms.py port, 2026-07-13): returns ONLY the ext mask + // (reachable-from-border), unlike _floodExterior above which returns the final intersected + // enclosed set — kept separate so partitionByDoors can compute ext on the DILATED footprint but + // apply it against the RAW free cells (recovering the seal band), without touching floodRooms' + // already-working _floodExterior call. + function _exteriorMask(free, nx, ny) { + var ext = new Uint8Array(nx * ny); + var stack = []; + for (var i = 0; i < nx; i++) { + [0, ny - 1].forEach(function (j) { + var k = i * ny + j; + if (free[k] && !ext[k]) { ext[k] = 1; stack.push(k); } + }); + } + for (var j = 0; j < ny; j++) { + [0, nx - 1].forEach(function (i) { + var k = i * ny + j; + if (free[k] && !ext[k]) { ext[k] = 1; stack.push(k); } + }); + } + while (stack.length) { + var k0 = stack.pop(); + var i0 = Math.floor(k0 / ny), j0 = k0 % ny; + [[1, 0], [-1, 0], [0, 1], [0, -1]].forEach(function (d) { + var a = i0 + d[0], b = j0 + d[1]; + if (a >= 0 && a < nx && b >= 0 && b < ny) { + var k = a * ny + b; + if (free[k] && !ext[k]) { ext[k] = 1; stack.push(k); } + } + }); + } + return ext; + } + function partitionByDoors(walls, doors, stairs, doorWMed) { if (!doors.length) return []; doorWMed = doorWMed || 0.0; - var ext = _gridExtent(walls); - var nx = ext.nx, ny = ext.ny, xs0 = ext.xs0, ys0 = ext.ys0; - var raw = _rasterizeWalls(walls, ext); + var extent = _gridExtent(walls); + var nx = extent.nx, ny = extent.ny, xs0 = extent.xs0, ys0 = extent.ys0; + var raw = _rasterizeWalls(walls, extent); + // §DOOR-PARTITION-EXT-EXCLUDE (real HHS finding: R9's own footprint sampled 93% exterior- + // reachable): the door-BFS must never claim exterior space as a room. ext determined on the + // dilated (sealed) footprint, applied against RAW free cells — the ext flood never reaches a + // raw-free/dilation-blocked cell, so the seal band is "given back" automatically. + var freeRaw = new Uint8Array(nx * ny); + for (var m0 = 0; m0 < nx * ny; m0++) freeRaw[m0] = raw[m0] ? 0 : 1; + var dil = SEAL > 0 ? _dilate(raw, nx, ny, SEAL) : raw; + var freeDil = new Uint8Array(nx * ny); + for (var m1 = 0; m1 < nx * ny; m1++) freeDil[m1] = dil[m1] ? 0 : 1; + var extMask = _exteriorMask(freeDil, nx, ny); var free = new Uint8Array(nx * ny); - for (var m = 0; m < nx * ny; m++) free[m] = raw[m] ? 0 : 1; + for (var m = 0; m < nx * ny; m++) free[m] = (freeRaw[m] && !extMask[m]) ? 1 : 0; var cz = walls.reduce(function (s, w) { return s + w[2]; }, 0) / walls.length; var bz = walls.reduce(function (s, w) { return s + w[5]; }, 0) / walls.length; - var ix = function (x) { return Math.min(nx - 1, Math.max(0, Math.floor((x - xs0) / RES))); }; - var iy = function (y) { return Math.min(ny - 1, Math.max(0, Math.floor((y - ys0) / RES))); }; + // §RASTER-EPS: same boundary-snap quantization as _rasterizeWalls (translation invariance) + var ix = function (x) { return Math.min(nx - 1, Math.max(0, Math.floor((x - xs0) / RES + RASTER_EPS))); }; + var iy = function (y) { return Math.min(ny - 1, Math.max(0, Math.floor((y - ys0) / RES + RASTER_EPS))); }; var owner = new Int32Array(nx * ny).fill(-1); var queue = [], head = 0; @@ -639,8 +761,10 @@ var area = cells.length * cellArea; var wx0 = xs0 + mni * RES, wx1 = xs0 + (mxi + 1) * RES; var wy0 = ys0 + mnj * RES, wy1 = ys0 + (mxj + 1) * RES; - if ((wx1 - wx0) < NOISE_FLOOR_DIM || (wy1 - wy0) < NOISE_FLOOR_DIM) return; - if (area > MAX_AREA_ABS || area > planArea * MAX_AREA_FRAC) return; + // §RASTER-EPS: integer-cell noise-floor test (see floodRooms) — no FP knife edge + var minCellsNF = Math.round(NOISE_FLOOR_DIM / RES); + if ((mxi - mni + 1) < minCellsNF || (mxj - mnj + 1) < minCellsNF) return; + if (area > planArea * MAX_AREA_FRAC) return; // §SUSPECT-LARGE: MAX_AREA_ABS flags below, never drops if (stairOverlapFrac(wx0, wy0, wx1, wy1, stairs) >= STAIR_OVERLAP_REJECT) return; // §ROOM-FORM + §RECT-HONESTY + §MULTI-RECT (ROOM_INJECTION_HYBRID.md §7/§8). No dilation on // this path → sealSteps=0 for the openM march, no seal band to grow back into. @@ -649,6 +773,8 @@ var openM = _openPerimeterM(cells, inSet, raw, raw, nx, ny, 0); var hasDoor = doorAdjacent(wx0, wy0, wx1, wy1, doors); var suspect = _classify(hasDoor, openM, doorWMed); + if (!suspect && _isElongated(wx0, wy0, wx1, wy1)) suspect = 'ELONGATED'; + if (!suspect && area > MAX_AREA_ABS) suspect = 'LARGE'; var dec = _decomposeRegion(inSet, ny, mni, mxi, mnj, mxj, cells.length, !!suspect); for (c2 = 0; c2 < cells.length; c2++) inSet[cells[c2]] = 0; var rects = []; @@ -656,6 +782,8 @@ var gRect = dec.rects[c2]; var rx0 = xs0 + gRect[0] * RES, rx1 = xs0 + (gRect[1] + 1) * RES; var ry0 = ys0 + gRect[2] * RES, ry1 = ys0 + (gRect[3] + 1) * RES; + var snapped = _snapRectToWalls(rx0, ry0, rx1, ry1, walls); + rx0 = snapped[0]; ry0 = snapped[1]; rx1 = snapped[2]; ry1 = snapped[3]; rects.push({ cx: (rx0 + rx1) / 2, cy: (ry0 + ry1) / 2, sx: rx1 - rx0, sy: ry1 - ry0 }); } var r0 = dec.rects[0]; @@ -971,6 +1099,34 @@ var allWallsRawList = allWallsRaw(db); var allDoorsRawList = allDoorsRaw(db); var allStairsZList = allStairsZ(db); // §STAIRWELL-STACK + // §LOCAL-FRAME (compile_rooms.py port — ROOM_WALKER_PHASE_INVARIANCE.md S2, 2026-07-17): + // rebase every x/y the compile touches to a building-local origin (the raster wall set's own + // min corner) and quantize to QUANT=1e-6 m, so after any constant frame translation every + // number entering flood/partition/merge/reject is BIT-IDENTICAL (FP jitter of rebased coords + // measured ≤ ~1e-10 m at |Δ|=1e6). Before: 8/14 translations changed Terminal's compile + // (rooms 50-54 vs 51) via knife-edge comparisons on absolute coords. After: 14/14 EQUAL. + // Output rooms are un-rebased on emit (writeRooms containment tests absolute element coords). + // Same floor(v/QUANT + 0.5) rounding as the Python (Math.round/py-round differ on .5 ties). + var QUANT = 1e-6; + var _q = function (v) { return Math.floor(v / QUANT + 0.5) * QUANT; }; + var _rwAll = []; + Object.keys(wallsBy).forEach(function (st) { _rwAll = _rwAll.concat(wallsBy[st]); }); + var orgX = 0.0, orgY = 0.0; + if (_rwAll.length) { + orgX = Infinity; orgY = Infinity; + _rwAll.forEach(function (w) { + orgX = Math.min(orgX, w[0] - w[3] / 2); + orgY = Math.min(orgY, w[1] - w[4] / 2); + }); + } + var _rb6 = function (t) { return [_q(t[0] - orgX), _q(t[1] - orgY), t[2], _q(t[3]), _q(t[4]), t[5]]; }; + var _rb4 = function (t) { return [_q(t[0] - orgX), _q(t[1] - orgY), _q(t[2]), _q(t[3])]; }; + Object.keys(wallsBy).forEach(function (st) { wallsBy[st] = wallsBy[st].map(_rb6); }); + allStairs = allStairs.map(_rb4); + Object.keys(doorsBy).forEach(function (st) { doorsBy[st] = doorsBy[st].map(_rb4); }); + allWallsRawList = allWallsRawList.map(_rb6); + allDoorsRawList = allDoorsRawList.map(function (t) { return [_q(t[0] - orgX), _q(t[1] - orgY), t[2]]; }); + allStairsZList = allStairsZList.map(function (t) { return [_q(t[0] - orgX), _q(t[1] - orgY), t[2], _q(t[3]), _q(t[4])]; }); var mergedTotal = 0, rejectedTotal = 0; var allrooms = [], report = [], stZ = {}; @@ -1000,6 +1156,12 @@ rooms = rejectRooms(rooms, allWallsRawList); rooms = rejectStairwell(rooms, allStairsZList); // §STAIRWELL-STACK, after R-REJECT var rejectedN = preRejectN - rooms.length; + // §LOCAL-FRAME: un-rebase on emit — everything after this point (report, no-overlap guard, + // writeRooms containment against absolute DB coords) sees the DB's own frame again. + rooms.forEach(function (r) { + r.cx += orgX; r.cy += orgY; + (r.rects || []).forEach(function (rc) { rc.cx += orgX; rc.cy += orgY; }); + }); mergedTotal += mergedN; rejectedTotal += rejectedN; var rescued = rooms.filter(function (r) { return r.door_rescued; }).length; var partitioned = rooms.filter(function (r) { return r.door_partitioned; }).length; @@ -1025,11 +1187,48 @@ var doorRescuedTotal = allrooms.filter(function (r) { return r.door_rescued; }).length; var doorPartitionTotal = allrooms.filter(function (r) { return r.door_partitioned; }).length; var suspectTotal = allrooms.filter(function (r) { return r.suspect; }).length; + _verifyNoOverlap(allrooms); return { report: report, rooms: allrooms, stZ: stZ, total: total, doorRescuedTotal: doorRescuedTotal, doorPartitionTotal: doorPartitionTotal, suspectTotal: suspectTotal, mergedTotal: mergedTotal, rejectedTotal: rejectedTotal }; } + // §NO-OVERLAP (compile_rooms.py port, 2026-07-13 — user request "rooms are stacked to each + // other, not overlapping"): permanent regression guard, informs like §PHASE0-HEALTH, never + // blocks. Verified 0 violations across 773 real rect rows in 6 buildings at the time this was + // added — both compile paths already guarantee disjointness by construction. + function _verifyNoOverlap(allrooms) { + var byStorey = {}; + allrooms.forEach(function (r) { (byStorey[r.storey] = byStorey[r.storey] || []).push(r); }); + var hits = 0; + Object.keys(byStorey).forEach(function (st) { + var rooms = byStorey[st]; + for (var i = 0; i < rooms.length; i++) { + for (var j = i + 1; j < rooms.length; j++) { + var ri = rooms[i], rj = rooms[j]; + if (ri.guid === rj.guid) continue; + (ri.rects || [ri]).forEach(function (a) { + var ax0 = a.cx - a.sx / 2, ax1 = a.cx + a.sx / 2; + var ay0 = a.cy - a.sy / 2, ay1 = a.cy + a.sy / 2; + (rj.rects || [rj]).forEach(function (b) { + var bx0 = b.cx - b.sx / 2, bx1 = b.cx + b.sx / 2; + var by0 = b.cy - b.sy / 2, by1 = b.cy + b.sy / 2; + var ox = Math.min(ax1, bx1) - Math.max(ax0, bx0); + var oy = Math.min(ay1, by1) - Math.max(ay0, by0); + if (ox > 0 && oy > 0 && ox * oy > 0.5) { + hits++; + console.log(' ⚠ §NO-OVERLAP VIOLATION storey=' + st + ' ' + ri.guid + + ' vs ' + rj.guid + ' overlap=' + (ox * oy).toFixed(2) + 'm2'); + } + }); + }); + } + } + }); + if (!hits) console.log('§NO-OVERLAP: 0 cross-room overlaps (invariant holds)'); + return hits; + } + // Persist a compileRooms() result into spatial_structure + rel_contained_in_space (the --write // half of compile_rooms.py's main()). Idempotent: prior compiled rows (RM_%/STC_%) are replaced. function writeRooms(db, compiled) { @@ -1139,7 +1338,8 @@ VERT_FACTOR: VERT_FACTOR, OPEN_PERIM_FACTOR: OPEN_PERIM_FACTOR, MERGE_GAP_TOL_FACTOR: MERGE_GAP_TOL_FACTOR, MERGE_SHARE_MIN: MERGE_SHARE_MIN, MERGE_WALL_COVER_MAX: MERGE_WALL_COVER_MAX, MERGE_DOOR_TOL: MERGE_DOOR_TOL, WALL_TOL: WALL_TOL, - REJECT_ENCLOSURE: REJECT_ENCLOSURE, SUSPECT_OPEN_ENCLOSURE: SUSPECT_OPEN_ENCLOSURE + REJECT_ENCLOSURE: REJECT_ENCLOSURE, SUSPECT_OPEN_ENCLOSURE: SUSPECT_OPEN_ENCLOSURE, + SUSPECT_ELONGATED_ASPECT_MIN: SUSPECT_ELONGATED_ASPECT_MIN }; ROOT.RoomWalker = API; if (typeof module !== 'undefined' && module.exports) module.exports = API; diff --git a/docs/ModellerGuide.md b/docs/ModellerGuide.md index 9e11bab2d..3fb9a5c30 100644 --- a/docs/ModellerGuide.md +++ b/docs/ModellerGuide.md @@ -249,6 +249,15 @@ restores it. ## Transform +Every transform below — move, scale, rotate, grid-stretch, delete — commits as one signed operation in +the same tamper-evident log that drives undo/redo/history-scrub, and a move or stretch is checked against +the building's own recovered relationships before it settles: a hosted door rides its host wall rather +than divorcing from it, and a delta-based conformity gate flags only what the edit actually broke (RED) +or softly disturbed (ORANGE) — never a pre-existing condition the building already shipped with. That +combination — open a *complete, real, production* IFC and safely edit *part* of it — is what a +Bonsai/FreeCAD-style direct editor doesn't do. Full dated disclosure: +**[Event-Sourced Geometry & the Graph-Cascade Conformity Layer](ModellerKernelFold.md)**. + Select an element and tap **Move** to raise the **transform gizmo** — the shared handle for moving, scaling and rotating. @@ -341,15 +350,17 @@ it: it places that trade's elements at the **measured cadence** of a real coordi runs it can, gates the clashes, and **honestly refuses** when the building has nothing to hang the trade on. Nothing is invented — every placement uses a spacing/clearance rule *mined from a real IFC model*. -![Walk · ELEC — 102 electrical fixtures placed across 19/21 real spaces in the Duplex; the building reads as one clean, well-formed shell from outside because every fixture landed correctly INSIDE the envelope](img/modeller/walk-fixtures.png) - -*Why you can't see any fixtures in this shot:* a real electrical outlet or light lives inside a room, not -poking through an exterior wall — from outside the sealed shell it's naturally occluded, same as it would -be in a real building. This is the corrected view (2026-07-12): a previous version of this pipeline had a -containment bug where roughly a quarter of placements landed outside the building's own walls — fixed -(mesh-recovered true-midpoint host binding) and independently verified 5 separate ways (containment count, -real-oracle walk-back match, measured-pattern conformance, wall-clearance margin, mirror-symmetry residual -on the Duplex's own A/B twin layout) before this screenshot was retaken. The fixture mesh itself is still a +![Walk · ELEC, X-ray reveal — 102 electrical fixtures placed across 19/21 real spaces in the Duplex; structure goes near-transparent glass and every fixture glows in its discipline colour through the shell, on both floors](img/modeller/walk-fixtures.png) + +A normal (non-X-ray) view of this same walk is honestly near-empty: a real electrical outlet or light +lives inside a room, not poking through an exterior wall, so from outside the sealed shell it's naturally +occluded — same as it would be in a real building. Tap **X-ray** (or press `X`) to see the walk actually +landed, as captured here: structure fades to near-transparent glass and the fixtures glow through it in +their discipline colour, room by room. This is the corrected placement too (2026-07-12): a previous +version of this pipeline had a containment bug where roughly a quarter of placements landed outside the +building's own walls — fixed (mesh-recovered true-midpoint host binding) and independently verified 5 +separate ways (containment count, real-oracle walk-back match, measured-pattern conformance, wall-clearance +margin, mirror-symmetry residual on the Duplex's own A/B twin layout). The fixture mesh itself is still a box stand-in sized to each class's own measured dimensions, not a finished fixture model — a deliberate, honest choice: guessing at a fixture's real shape is exactly what this project's non-invent rule forbids. @@ -449,7 +460,7 @@ After walking a discipline, route its **service trunk** from a real entry. A corridor-aware trunk is routed from that entry through the walked fixtures — around walls, through real doors, up risers between storeys. -![The Duplex after Route ▶ — a real ELEC trunk is now rendered (0→3,922 segments, verified by framebuffer diff), threaded through the walked fixtures; from this angle the trunk itself is a thin line hugging the interior wall, but the important thing this corrected view proves is that nothing renders outside the building anymore](img/modeller/seedtrunk-trunk.png) +![The Duplex after Route ▶ — a real ELEC trunk is now rendered (0→3,922 segments, verified by framebuffer diff), threaded through the walked fixtures and recoloured magenta for this shot (its real default is gold, the same family as the ELEC fixture boxes, which read as low-contrast clutter against the wall in a static frame); the important thing this proves is that nothing renders outside the building anymore](img/modeller/seedtrunk-trunk.png) > **Deeper proof.** The full mining, round-trip, boundary and generalization analysis lives in the resume > cards `prompts/RESUME_DX_MEP_RESIDENTIAL_STANDARD.md`, `RESUME_TERMINAL_RULE_MINING.md` and diff --git a/docs/ModellerKernelFold.md b/docs/ModellerKernelFold.md index 86bd5e683..b6450f7d9 100644 --- a/docs/ModellerKernelFold.md +++ b/docs/ModellerKernelFold.md @@ -3,9 +3,15 @@ > **Defensive publication / authorship record.** First published **2026-06-18** by the BIM OOTB project -> (red1oon). This page is a dated, public, enabling disclosure of the architecture described below so that -> it stands as prior art. It is the geometry-side companion to the **[Feature Comparison](FeatureComparison.md)** -> (BIM viewer) and **[Migrate & Compare (ERP)](MigrateComparisonPaper.md)** (the WASM event-sourced browser ERP). +> (red1oon), extended **2026-07-13** with a second, related disclosure. This page is a dated, public, +> enabling disclosure of the architecture described below so that it stands as prior art. It is the +> geometry-side companion to the **[Feature Comparison](FeatureComparison.md)** (BIM viewer) and +> **[Migrate & Compare (ERP)](MigrateComparisonPaper.md)** (the WASM event-sourced browser ERP). +> +> Two things are disclosed, dated separately below: (1) geometry as a deterministic fold over a signed +> operation log, and (2) a typed dependency graph over real IFC relations that gates every edit's cascade +> against that same log — the combination that lets the Modeller open a *complete, real, production* IFC +> and safely edit *part* of it. ## What is disclosed @@ -54,6 +60,25 @@ We **record as prior art**, as of the date above, the **specific combination** n The distinguishing intersection — **signed log + geometry-as-fold + unified ERP+BIM substrate + fully client-side** — is held by no prior system in the row above. +**A second, independent axis** — added 2026-07-13, same disclosure lineage, full detail in the Extension +section below — is whether a system can open a *complete, real, production* IFC and safely edit *part* of +it via a dependency graph recovered from real IFC relations, not a generic parametric-feature DAG: + +| Capability | **BIM OOTB (this work)** | Bonsai / IfcOpenShell | FreeCAD | +|---|:--:|:--:|:--:| +| Opens a **complete, real, production** IFC and edits it in place | ✅ | ✅ | partial (BIM workbench, IFC import) | +| Dependency graph **recovered from real IFC relations / measured geometry** (not a generic parametric-feature DAG) | ✅ | ❌ | ❌ | +| **Delta-based** conformity check (flags only what an edit changed, not pre-existing conditions) | ✅ | ❌ | ❌ | +| RED (hard) / ORANGE (soft) **gated exception on every edit**, not a silent accept | ✅ | ❌ | ❌ | +| Cascade runs **within the same signed, replayable operation log** as the fold mechanism above | ✅ | n/a (no signed log) | n/a (in-memory undo, no signed log) | + +FreeCAD's own Dependency Graph is a real, related idea — a DAG of document objects so a parametric change +propagates to its dependents — but it is a *general CAD* feature graph (sketch → pad → boolean, within one +authored document), not derived from a real building's IFC relations, and it carries no delta-based +RED/ORANGE gate. Bonsai edits real IFC entities directly via IfcOpenShell inside Blender with no +graph-cascade or signed provenance at all — closer to a direct-mesh editor with IFC awareness than a +dependency-aware one. + ## Reference implementation (enabling disclosure) The mechanism is demonstrated, not merely asserted. The reference reuses the open-source **occt-wasm** kernel @@ -89,10 +114,58 @@ establishes that the geometry is fully determined by the log row — i.e. geomet - The reused kernel is embedded as a **separately-loadable WebAssembly module**, preserving the open-source kernel's licence terms while the surrounding application remains independently licensed. +--- + +## Extension — a graph-cascade conformity layer on the same substrate +*Published 2026-07-13, same disclosure lineage as above.* + +### What is disclosed + +Alongside the fold mechanism above, the same client-side substrate carries a second layer: a **typed +dependency graph recovered from real IFC relations (or derived from measured geometry, never guessed), +driving a delta-based conformity gate on every committed edit** — so that opening a *complete, real, +production* IFC building and editing *only the part touched* is provably safe, not merely possible. + +On import, the tree already used for the BOM (parent→child `contains`) is extended with typed lateral +edges: `hosted-by` (an opening's real host wall, recovered from `IfcRelFillsElement`), `abuts` (real +face-touch adjacency, geometry-derived), `anchored-to` (element-to-datum-plane by measured face cadence, +no `IfcGrid` required), `spans` (an element's bounding box reaching between two distinct datums). A drag +on one datum/gridline folds forward through this graph as **one signed operation** (`GEOM_GRID_MOVE`) — +hosted openings ride their host wall rather than stretch or divorce from it, spans stretch with sizes +held, `contains` cascades en-bloc. + +After the fold, a **delta-based conformity gate** evaluates only what the edit changed — a pre-existing +condition the building already shipped with is never flagged — against the same graph: **RED** for a +hard constraint the edit broke (a hosted opening crushed by a shrunk host, a real volumetric clash), +**ORANGE** for a soft, user-acceptable side effect (an abutting wall that now wants to realign). The gate +runs live during the drag (a green/orange/red preview before commit) and again on the committed result. + +### Reference implementation + +`sdg_gate.js` (§GATE-1) evaluates `{red:[...], orange:[...]}` from before/after axis-aligned bounding +boxes, the recovered relations, and which elements the fold actually moved — pure geometry over measured +data, with exactly one tunable parameter (a residential clearance figure, itself mined from a real +building's own MEP separations, not asserted). `bonsai_gridmove.js`'s `§PREDRAG` pipeline runs the same +evaluator live, before commit. Handlers shipped and witnessed to date: hosted-opening ride, door-crush +RED, abuts-realign ORANGE, an OBB-SAT narrow-phase clash upgrade, and a UBBL-bylaw-shaped demo case. Full +build log: `prompts/SPATIAL_DEPENDENCY_GRAPH.md` and the project's `RESUME_MODELLER_CONFORMITY_GATE.md`. + +### Named gaps — real extensions the substrate makes possible, not yet built + +Stated plainly so the disclosure above isn't read as claiming more than it does: no general graph-query +API, no multi-hop traversal (the cascade is one hop, directional, wall→door only), no MEP flow/ +connectivity graph (no `flows-into` edge type exists), no assembly clone/duplicate-with-reconnect. Each is +a plausible extension of the same recovered, non-invented relations — not a different architecture — but +none are shipped, and none should be read into the claims above. This is a fast-moving build, not a +finished one; the tiered gap-and-sequencing plan is tracked internally, not restated here each time it +moves — see `prompts/BONSAI_KERNEL_RESEARCH.md §GAP-TO-COMPETITIVE`. + +--- + ## Provenance This disclosure is timestamped by its publication to the project's public documentation site and by the project's version-control history. It is intended to establish the date and authorship of the combination described, and to serve as prior art against later claims to that combination. -*BIM OOTB — red1oon — first published 2026-06-18.* +*BIM OOTB — red1oon — first published 2026-06-18, extended 2026-07-13.* diff --git a/docs/img/modeller/delete-gone.png b/docs/img/modeller/delete-gone.png index a7824bf8f..c98835d7b 100644 Binary files a/docs/img/modeller/delete-gone.png and b/docs/img/modeller/delete-gone.png differ diff --git a/docs/img/modeller/gizmo.png b/docs/img/modeller/gizmo.png index 859352dcb..591f77a4b 100644 Binary files a/docs/img/modeller/gizmo.png and b/docs/img/modeller/gizmo.png differ diff --git a/docs/img/modeller/rotate-yaw.png b/docs/img/modeller/rotate-yaw.png index f443c7010..457626ae9 100644 Binary files a/docs/img/modeller/rotate-yaw.png and b/docs/img/modeller/rotate-yaw.png differ diff --git a/docs/img/modeller/samplecastle-arc-open.png b/docs/img/modeller/samplecastle-arc-open.png index b363ad148..91e825124 100644 Binary files a/docs/img/modeller/samplecastle-arc-open.png and b/docs/img/modeller/samplecastle-arc-open.png differ diff --git a/docs/img/modeller/scale-stretched.png b/docs/img/modeller/scale-stretched.png index 124b8252b..f00d16cfe 100644 Binary files a/docs/img/modeller/scale-stretched.png and b/docs/img/modeller/scale-stretched.png differ diff --git a/docs/img/modeller/seedtrunk-entry.png b/docs/img/modeller/seedtrunk-entry.png index 24bfd963e..b1062d6be 100644 Binary files a/docs/img/modeller/seedtrunk-entry.png and b/docs/img/modeller/seedtrunk-entry.png differ diff --git a/docs/img/modeller/seedtrunk-trunk.png b/docs/img/modeller/seedtrunk-trunk.png index e9b516702..c39156083 100644 Binary files a/docs/img/modeller/seedtrunk-trunk.png and b/docs/img/modeller/seedtrunk-trunk.png differ diff --git a/docs/img/modeller/walk-fixtures.png b/docs/img/modeller/walk-fixtures.png index d275904d2..141ff5574 100644 Binary files a/docs/img/modeller/walk-fixtures.png and b/docs/img/modeller/walk-fixtures.png differ diff --git a/docs/internal/ERP_COVERAGE_MATRIX.md b/docs/internal/ERP_COVERAGE_MATRIX.md index 0e4434668..50d428f62 100644 --- a/docs/internal/ERP_COVERAGE_MATRIX.md +++ b/docs/internal/ERP_COVERAGE_MATRIX.md @@ -108,9 +108,11 @@ denominator, not 496 classes / 735k LOC). | 🟡 **rule-consistent** | **MInventory physical-count fold (W-FOLD-INVENTORY)** — `poc_inventory.js` ENACTS a count (no I± in seed, NO oracle): book on-hand = FOLD of real `m_transaction` (== `m_storageonhand`) → I+/I- picked by sign(counted−book) → folding the adjustment through the qty spine lands on-hand == counted (6/6 products, gain+loss), and the GL adjustment value = `|adjQty| × cost` via the PROVEN cost-selection rule (Oak Tree +3 × 51.45 = 154.35) and BALANCES. **Closes the MInventory I± rider qtyonhand named-deferred.** §FALSIFIER-A wrong polarity (on-hand ≠ counted), §FALSIFIER-B Material cost element (24.00 ≠ Average 51.45). **GL offset account named-deferred** (Inventory-Gain/Loss `ACCTTYPE_InvDifferences` has no extractable column; the leg value + balance are proven). Tier = rule-consistent, NOT "== iDempiere". `build/erp/poc_inventory.log`. | | ✅ **oracle-equivalent** | **AD_Workflow node-walk + state engine (B-2, W-WF-HARDEN — the LAST ⬜ closed, ledger 42→43)** — `poc_wf_harden.js` diffs `ad_workflow.js`'s new `replay` arm against REAL iDempiere workflow traces in the live PG `idempiere_test` (11 ad_wf_process / 13 ad_wf_activity / 13 ad_wf_eventaudit written by real iDempiere; captured verbatim → `build/erp/oracle/wf_oracle.json`, never hand-authored): **`§HARDEN surface=ad_workflow fixtures=11 diff=0 oracle=iDempiere-PG-trace`** — node SEQUENCE + transitions taken + per-activity WFState/eventtype + process WFState all identical (10× wf131 BP-Approval UserWindow-suspend `[244]→OS/SC` + 1× wf116 Process_Order `[183→185→186]→CC/PX` std-user XOR transition; threaded docstatus ends CO == the live c_order row, `§HARDEN-DOC`). Definitions md5-set ad_full.db == live PG `§HARDEN-SRC kind=wf setdiff=0` (58 wf/262 node/207 next/1 cond); document context EXTRACTED (C_Order AD_Column defaultvalues DR/CO, identical both schemas). Semantics arm = `scripts/logic_oracle/WorkflowOracle.java` (the B-1 LogicOracle headless-compiled-classes technique one level up): REAL compiled `StateEngine` MUTATORS agree 6/6 (engine hops ON→OR→CC/OS applied identically, closed→open REJECTED) + std-user gate == verbatim `MWFNodeNext.isValidFor:215-243` over compiled DocAction constants (PO/Env/DB-dragging arms = named omissions). 2 §FALSIFIERs load-bearing (flip DocAction CO→'--' reroutes 183→184 on BOTH sides; dropped node 185 → LOUD CA abort, diff flags it). Small-K honesty: 7 `§HARDEN-SKIPS` lines (actions F/X/P/R/C unexercised — replay THROWS; 1 conditioned transition wf115 untraced; AND-split absent in seed 262/262 X/X; 56/58 wfs traceless; claim = "11 real processes, diff=0", not corpus-wide). `build/erp/poc_wf_harden.log`. | -**Remaining ⬜ declarative surfaces: NONE** (`ad_evaluator` fell to B-1 W-LOGIC-HARDEN, `ad_workflow` fell to B-2 W-WF-HARDEN, 2026-06-12). +| ✅ **oracle-equivalent** | **0-seed posting oracles — the 6 G-seed classes (B-3, W-POST-B3, ledger 43→49)** — `poc_post_b3.js` diffs the EXTENDED `doc_poster.derivePostings` (per-class manifests: `fa-addition`/`fa-depreciation`/`fa-reval`/`fa-transfer`/`fa-disposal`/`project-issue`, each citing its Doc_*.java lines) against a **GENERATED oracle**: the REAL compiled posters driven inside iDempiere's own OSGi test harness (`scripts/logic_oracle/PostingOracleTest.java` under `org.idempiere.test` tycho-surefire — resolves the G-3 "no headless OSGi" landmine) on a SCRATCH clone `idempiere_b3` of `idempiere_test`, over a GardenWorld-model seed (vendor FixedAssetsTest recipe; USER RULING 2026-07-17: seed INPUT prep sanctioned, facts never hand-authored). Capture = `scripts/generate_post_oracle.sh` → TEXT fixture `build/erp/oracle/post_b3_fixture.json` (34 fact rows, ΣDR=ΣCR=81360.56, both schemas 101+200000). Result: **all 6 classes `maxDiff=0c` per document × schema × (account,side)** — `§B3-POST`: A_Asset_Addition (4 docs incl. the A_CapvsExp=Exp arm-Z doc posting ZERO rows, gate=Doc_AssetAddition.java:67-72, **+ the tenant-vs-system fx-rate pick MConversionRate:243-252 the diff CAUGHT** — engine had picked the 0.8006 system rate, oracle posts the 0.85 client-11 rate, fixed) · A_Depreciation_Entry (per-depexp DR/CR; schema-200000 = the other-schema ∅ branch) · A_Asset_Reval (2 delta pairs vs the seeded A_Reval_Cost_Offset_Acct) · A_Asset_Transfer (workfile cost old→new combos; the completeIt validfrom time-slice keeps pre-transfer docs derivable) · A_Asset_Disposed (from the per-schema A_Asset_Change 'DIS' row) · C_ProjectIssue (PJ_WIP vs Product.Asset at the schema costing-method cost — the W-FOLD-MOVEMENT hop). 2 §FALSIFIERs load-bearing (gate-flip Cap→Exp → 2→0 lines; double one depexp expense → ΣDR 55556c→111112c) + the 2 triage falsifiers. The 2 ∅-by-design classes (C_BankTransfer/C_DepositBatch — no Doc_ in the factory) stay structurally CLOSED. Regressions green after the doc_poster extension: W-POST-HARDEN · W-FACTACCT-DOC · W-DOC-POSTER · W-MORDER-POST · test_report_fin 46574.97/300 rows intact. `build/erp/poc_post_b3.log` · `build/erp/generate_post_oracle.log`. | -**The honest read: coverage 37🟡 ≠ equivalence.** FORTY-THREE surfaces are now oracle-equivalent to the unit/cent +**Remaining ⬜ declarative surfaces: NONE** (`ad_evaluator` fell to B-1 W-LOGIC-HARDEN, `ad_workflow` fell to B-2 W-WF-HARDEN, 2026-06-12; the 0-seed POSTING tail fell to B-3 W-POST-B3, 2026-07-17). + +**The honest read: coverage 37🟡 ≠ equivalence.** FORTY-NINE surfaces/classes are now oracle-equivalent to the unit/cent (TB-read · per-document GL derivation H-1 · `completeIt` Order→Ship→Invoice chain · Doc_Payment · Doc_AllocationHdr incl. tax-correction · StorageOnHand qty · ReplenishReport PO · standalone `completeIt(C_Invoice)`+MatchInv · vendor `Doc_Invoice` purchase manifest · foreign-currency Doc_AllocationHdr · inter-org M_Movement **+ its EUR-schema diff --git a/prompts/BONSAI_KERNEL_RESEARCH.md b/prompts/BONSAI_KERNEL_RESEARCH.md index 1bdfb08b6..9a0ee82f9 100644 --- a/prompts/BONSAI_KERNEL_RESEARCH.md +++ b/prompts/BONSAI_KERNEL_RESEARCH.md @@ -670,6 +670,41 @@ merge), exactly `DistributedERP.md §6`'s "dumb async post office." Whoever pick port that doctrine, not redesign it — this is a reuse job, not a from-scratch one, even though it's still correctly "months" of real integration work, not a same-recipe wire like Tier 2. +### Tier 2b — Spatial Dependency Graph: query/traversal + MEP flow + assembly-cloning gap (2026-07-13) +A third-party review of the graph-cascade conformity layer (`docs/ModellerKernelFold.md`'s 2026-07-13 +extension) claimed capabilities beyond what's shipped — checked against source before writing anything +down, not assumed (grepped `~/bim-ootb` directly; see that page's own "Named gaps" note for the short +public-facing version). Tiered the same way as the rest of this doc, by real cost, not by how the review +framed them: + +- **Graph-query API — Tier 1-shaped (hours, not days).** The relations already exist as SQL tables + (`rel_adjacency`/`rel_anchored`/`rel_fills_host`/`rel_spans`), read today only via raw SQL inline at + each call site. Wrapping a `graph.query({source, edgeType})`-style traversal is a thin convenience layer + over data that already exists — no new derivation, no new schema, no kernel binding to touch. Smaller + than any Tier 1 op above. +- **Multi-hop traversal (zone/downstream queries) — Tier 2-shaped, known-solvable recipe.** + `sdg_cascade.js`'s cascade is explicitly ONE HOP, directional (host→fillings only) — "select everything + in this grid cell" or "everything downstream of this column" isn't a query you can run today. The + Viewer-side experimental `sdg_fold.js` (`SPATIAL_DEPENDENCY_GRAPH.md` §FORWARD, demo-only, never + publicly deployed) already proves multi-hop forward-fold works on `anchored-to`/`spans`/`contains` — + the real task is decoupling that proven traversal from its fold-and-mutate coupling into a read-only + collect, not inventing a new graph-walk from scratch. +- **MEP flow/connectivity graph (`flows-into`) — Tier 2/3-shaped, size unknown until measured.** + Genuinely not started, no derivation exists anywhere. Unlike the edges above (recoverable from + `IfcRelFillsElement` / face-touch / measured cadence alone), pipe/duct connectivity typically needs + `IfcRelConnectsPorts` port-to-port relations, which this project's extractor doesn't currently mine. + First step is the same kind of measurement pass that confirmed `rel_fills_host` was recoverable before + Phase 1 was built on it — not yet run. Could be a same-recipe derivation, or a real gap in what the + source IFC data even carries; unknown until measured, not estimated. +- **Assembly clone/duplicate-with-reconnect — Tier 3, depends on the above.** Needs the multi-hop + traversal (to even define "the assembly") plus a real re-anchoring engine (place a copy, re-resolve + `anchored-to`/`hosted-by` against the new location's own nearby datums/hosts) that doesn't exist in any + form. Genuinely months, and only sequenced after the first two items land. + +**Sequencing, not a calendar date:** query API first (cheap, immediate inspection/debugging value) → +decouple `sdg_fold.js`'s traversal for read-only multi-hop queries → run the port-connectivity measurement +pass before scoping the MEP flow graph → assembly-cloning only once both of those are real. + ### ETA, honestly — not one number **Tier 1 (kernel op-count parity on cheap shoulders): days, not weeks** — the measured 20-30 min/op pattern from today generalizes directly, this doc already flagged these as zero-binding-work in June. diff --git a/prompts/CENTROID_DETERMINISM_FIX.md b/prompts/CENTROID_DETERMINISM_FIX.md new file mode 100644 index 000000000..a9ac1db08 --- /dev/null +++ b/prompts/CENTROID_DETERMINISM_FIX.md @@ -0,0 +1,77 @@ + +# CENTROID DETERMINISM — element `center_x/y/z` is vertex-mean (non-deterministic); fix = bbox-center + +``` +# ⚠ DO NOT REMOVE +SCOPE: root-caused fix spec for the "browser-importer wall-transform parity (46 vs 54 rooms)" gap +in ROOM_INJECTION_CONSOLIDATED_REVIEW.md §GAPS. DIAGNOSIS COMPLETE + witnessed (2026-07-17). Fix +NOT yet implemented — it is a pipeline-wide data-model change (touches every extracted DB) and +needs its own fix+full-regression session, not a drive-by. PUSH PAUSE: when built, commit locally, +verify on localhost, no push/PR. DB rule: re-extracted binaries go via OCI, incremental fixes to +already-distributed DBs go via self-heal patch — never a .db in git. Read the log after every run. +``` + +## The bug (PRIME-DIRECTIVE violation — a core field is non-deterministic) +`elements_meta`/`element_transforms` `center_x/y/z` is computed as the **arithmetic MEAN of every +tessellated vertex** — in BOTH pipeline paths, byte-identical formula: +- **Extraction:** `scripts/extractIFC2DB.js:422-428` — `cx = sumX / vertCount` over all verts. +- **Browser import:** `~/bim-ootb/viewer/import_worker.js:515-524` — same `cx = sumX / vertCount`. + +Vertex-mean depends on **triangulation density**, which is NOT a geometric invariant. web-ifc's +tessellation of boolean-subtracted / swept ARC/STR solids (slabs, coverings, walls-with-openings, +beams) is not bit-stable between the **browser wasm** and the **node wasm** build of web-ifc 0.0.77 +(same engine, same OpenModel flags `COORDINATE_TO_ORIGIN:false, USE_FAST_BOOLS:true, +OPTIMIZE_PROFILES:true`). So the stored center drifts per element — proportional to element size — +while the **bbox (min/max) is bit-identical** (extent is an invariant; mean is not). + +## Witness (decisive whitebox A/B — same `TerminalMerged.ifc`, offline extract vs browser Drop) +Joined by identical GUID across 4790 elements (diagnosis session 2026-07-17): +- center `dx` differs up to **±1.31 m**; **630/4790** elements > 0.01 m (356 in z). +- Divergent classes are exactly the big planar/swept ARC solids: IfcSlab (372, max 1.31 m), + IfcCovering (53), **IfcWall (41)**. Small MEP/PLB/FP/ELEC components (stable tessellation) stay + rigid at the constant tile offset. +- **`bbox_x/y/z` delta = 0** across ALL elements — only the vertex-mean center moves. +- Against the shipped `Terminal_extracted.db` (prefix-stripped join): **31 of 333 walls shift + > 0.5 m** — enough to break room-walker wall-enclosure → **45 vs 54 rooms**. + +This RETIRES the earlier "pure translation" reading (a statistical-aggregate artifact; the +disjoint-GUID appearance was just the `T0_Terminal_` sandbox prefix) AND the misdiagnosed +"walker coordinate-phase sensitivity" (that was a separate, already-fixed bug — see +`Viewer/FLY_TOUR_CORRIDOR_GRAPH.md` CORRECTION 2026-07-17; #832 §LOCAL-FRAME made the walker +14/14 translation-invariant, backported to `fable/meshdb-livewire` @ `c44ade97d`). + +## The fix (small in code, wide in blast radius) +Replace vertex-mean center with **tessellation-invariant bbox-center = (min+max)/2** of the verts, +in BOTH files. The min/max are ALREADY computed for `bbox_x/y/z`, so it is a few lines each. +CRITICAL: the same bbox-center must be used as BOTH (a) the stored `center_*` AND (b) the geometry +re-centering origin (`positions[vi] = vert - center`), so world position `= center + (vert-center) += vert` is preserved. Using bbox-center for storage but vertex-mean for re-centering would displace +all geometry — do both together. +- `scripts/extractIFC2DB.js:422-433` (compute bbox-center, store it, re-center by it) +- `~/bim-ootb/viewer/import_worker.js:515-528` (identical change — keep the two byte-parallel) + +After: imported == extracted per element → room-walker gives the SAME count in both → imported +buildings fly. + +## Why this is a dedicated fix+regression session, not a drive-by +- **Every extracted DB changes.** `center_*` shifts for every asymmetric element in every building; + the extraction change only takes effect on RE-EXTRACTION. All `deploy/buildings/*_extracted.db` + must be regenerated (via `extract_per_building.py` / the sandbox build), then redistributed per + the DB rule (OCI for full binaries; self-heal patch for already-shipped ones). +- **Room counts may shift building-wide** — bbox-center vs mean differ for L-shaped/tapered walls. + Full regression required: `build/witness_room_walker_parity.js` (py==js still holds — change + both), the phase sweep (`phase_witness/`), and a per-building room-count before/after on the + whole fleet (Duplex/Clinic/HHS/Hospital/Garage/Terminal/LTU_AHouse). Confirm imported==extracted + on Terminal AND no unexplained count regressions elsewhere. +- **Anything relying on center-as-visual-centroid** (label placement, camera framing, walker + §STOREY-Z which reads wall `center_z`) must be spot-checked — bbox-center-z differs from mean-z + for vertically-asymmetric elements. +- **Architecture-scope:** this redefines a fundamental data-model field across the whole WHAT/HOW/ + WHERE pipeline — flag to the architect before shipping, don't self-authorize the semantic change. + +## DONE WHEN +Both files use bbox-center (stored + re-center origin, byte-parallel); all `*_extracted.db` +re-extracted; `witness_room_walker_parity.js` green (py==js); a §-witnessed per-GUID A/B shows +imported center == extracted center (delta 0) on Terminal; imported Terminal compiles the same +room count as extracted (54==54) and flies; fleet room-count regression sweep shows no unexplained +change; redistribution done per DB rule. Architect sign-off on the center-semantic change recorded. diff --git a/prompts/FABLE5_B3_POSTING_ORACLE.md b/prompts/FABLE5_B3_POSTING_ORACLE.md new file mode 100644 index 000000000..84e9ad070 --- /dev/null +++ b/prompts/FABLE5_B3_POSTING_ORACLE.md @@ -0,0 +1,299 @@ +# ⚠ DO NOT REMOVE — Scope guard / FABLE-5 CARD: B-3 — 0-seed POSTING oracles (the NAMED SEQUEL to B-2) +# Scope: the doc classes whose FSM is source-parsed but that have ZERO posted `fact_acct` rows in ANY seed DB, +# so their accounting fan-out has NEVER been oracle-verified. Targets (ad_docfsm.js `DOC_FAMILY`, the two +# "0-seed" comment bands at build/erp/ad_docfsm.js:185 and :198): +# · C_BankTransfer (200246) · C_DepositBatch (200056) · M_ProjectIssue (623) +# · Fixed-Asset family — A_Asset_Addition (53137) · A_Asset_Disposed (53127) · A_Asset_Reval (53275) +# · A_Asset_Transfer (53128) · A_Depreciation_Entry (53121) +# Each has FSM logic parsed from source (H-2 ISOMORPH tail) but NO real posted document anywhere → their +# `Fact_Acct` fan-out is the LAST un-oracled accounting surface. This card oracle-verifies it. +# MODEL LANE: Fable 5 (deep reasoning + 1M context: the compiled org.compiere.acct Doc_* classes + the +# MAcctSchema/GL-category/period setup they need + our doc_poster verb, all held at once). This is NOT +# mechanical replication of an existing trace (B-2/H-1 had real rows to diff) — the whole crux is there is +# NOTHING to diff yet: the oracle must be GENERATED by driving the REAL compiled posters headless on a +# scratch PG, then Fact_Acct diffed row-by-row. Irreducible get-it-right-first-time correctness loop = +# exactly the Fable lane (memory `project_fable5_lane`, `feedback_model_allocation_mastermind_vs_execution`). +# Headless, bim-compiler ONLY — NO bim-ootb, NO deploy, NO sw. READ THE LOG after every run (exit ≠ evidence). +# ALL poc_* via `bash build/erp/run_witness.sh scripts/poc_X.js`. +# NON-NEGOTIABLE: EXTRACT/GENERATE-THEN-DIFF, NEVER INVENT — the oracle is the REAL compiled Doc_* poster's +# own output on a real (scratch) PG, captured like extract_fact_acct.sh does for GardenWorld. A class that +# genuinely cannot post headless without forking semantics is an HONEST ⛔/SKIP (named + counted, the +# WorkflowOracle un-headless precedent), NEVER a synthesized Fact_Acct. A class that posts ZERO by CONFIG is +# the poc_morder_post case (zero is the correct oracle) — prove zero-is-config-derived with a gate-flip +# §FALSIFIER, do not skip it. Spec-first; §FALSIFIER load-bearing; integer-cents metric. +# docs/ERP_BACKEND_SEPARATION.md seams stay intact. +# READ FIRST (in order): +# 1. prompts/FABLE5_WORKFLOW_ORACLE.md — B-2, the DIRECT precedent this card sequels (§W-1..§W-5 skeleton, +# generate/replay-then-diff discipline, small-K honesty). Its STOP CONDITION names THIS card by name. +# 2. scripts/poc_post_harden.js (W-POST-HARDEN, HARDEN_MATRIX §H-1) — THE POSTING TECHNIQUE THIS GENERALIZES: +# post_resolver's derived GL lines == real fact_acct per (natural-account, side) in INTEGER CENTS, +# maxDiff=0c. Oracle = GardenWorld client-11 fact_acct captured by scripts/extract_fact_acct.sh into +# build/erp/glassbowl_data.db, NEVER hand-authored. Same move, pointed at classes with no seed rows. +# 3. scripts/poc_factacct_doc.js (W-FACTACCT-DOC, §H-1.1) — per-document granularity (ad_table_id/record_id/ +# line_id) row-by-row vs live PG. The capture shape the new classes must extend. +# 4. scripts/poc_morder_post.js (W-MORDER-POST, §H-1.2) — the CONFIG-GATED ZERO case: Doc_Order posts 0 +# fact_acct rows because GardenWorld CommitmentType='N'; §FALSIFIER-A flips the gate to prove zero is +# config-derived, not hardcoded. Several B-3 targets will land here — copy this proof verbatim. +# 5. scripts/logic_oracle/{LogicOracle,WorkflowOracle,ConfirmOracle}.java — the "drive REAL compiled classes +# headless, name every omitted static in the header" pattern. B-3's PostingOracle.java mirrors these, +# pointed at org.compiere.acct.Doc / Doc_BankTransfer / Doc_DepositBatch / Doc_ProjectIssue / Doc_Asset*. +# 6. build/erp/ad_docfsm.js:185-205 — the exact 0-seed target rows + their per-class M*.java line citations +# (already parsed); prompts/H2_ISOMORPH_TAIL.md — how the 0-seed FSM tail was honestly handled (⛔ on +# stored-replay, source-parse FSM only) — B-3 is the POSTING counterpart of that same honesty. + +--- + +## ⚠ TRIAGE RESULT + USER RULING (2026-07-17 — reshapes, but does NOT kill, §W-2) +Triage (W-POST-B3, `build/erp/poc_post_b3.log`, exit 0) found **all 8 targets have ZERO source documents** +in GardenWorld (the only real seed), not just zero posted rows. Two arms: +- **2 are ∅-BY-DESIGN — DONE** — C_BankTransfer / C_DepositBatch have **no `Doc_` in the posting + factory** (`org.compiere.acct.Doc.get` has no case) → posting is not in their contract. oracle=∅ is CORRECT + and structurally proven. Closed. +- **6 are G-seed — ASSIGNED TO FABLE 5** — C_ProjectIssue + the 5 Fixed-Asset classes: poster EXISTS, + manifest source-parsed + line-cited (§W-1 below), but 0 source docs → nothing to post yet. +- **USER RULING (2026-07-17):** preparing seed documents **along the GardenWorld model** is **usability / PoC + seed prep — NOT a prime-rule violation.** The oracle stays the REAL compiled poster's output over that seed; + only the demo INPUT is prepared, honestly labelled as seed. So §W-2 IS legal and IS the path — it was never a + synthesis of the oracle. (What the prime rule still forbids is HAND-AUTHORING the expected `fact_acct` — that + stays banned. My earlier "⛔ n/a-in-seed / prime-rule" reading over-applied the ISOMORPH-tail rule; corrected.) +- **Lane:** the G-seed generate arm is a **Fable 5 session** (deep get-it-right-first-time posting loop). This + Opus session did the triage + manifest source-parse + hand-off; the Fable session executes §W-2→§W-5 for the 6. + +## FACTS (verified live 2026-07-17 — Docker `postgres` / db `idempiere_test`, client 11 GardenWorld) +- **All 8 targets: `fact_acct` rows = 0 AND source-doc rows = 0** (c_banktransfer / c_depositbatch / + c_projectissue / a_asset_addition / a_asset_disposed / a_asset_reval / a_asset_transfer / + a_depreciation_entry all `count(*)=0`; captured oracle `glassbowl_data.db` fact_acct=300 rows, none for + these ids). Re-asserted in W-POST-B3, not assumed. +- **Posters present** (org.compiere.acct): Doc_ProjectIssue, Doc_AssetAddition, Doc_AssetDisposed, + Doc_AssetReval, Doc_AssetTransfer, Doc_DepreciationEntry. **Absent**: Doc_BankTransfer, Doc_DepositBatch + (grep of Doc.java factory + acct package — structurally non-posting). +- These 8 classes have FSM parsed from source (ad_docfsm DOC_FAMILY, H-2 ISOMORPH tail) but **0 posted + `fact_acct` rows** in the GardenWorld seed — confirm per class: `SELECT count(*) FROM fact_acct WHERE + ad_table_id = ` = 0 for each. That zero is WHY posting was never oracled — state each K honestly. +- The posting oracle for the ALREADY-covered classes lives in `build/erp/glassbowl_data.db` (fact_acct, 300 + rows, ΣDR=ΣCR=46574.97 — the test_report_fin anchor), captured by `scripts/extract_fact_acct.sh` from the + same live PG. B-3 EXTENDS this capture; it must NOT perturb the existing bundle (poc_factacct_doc + + test_report_fin must still pass after any extract change — the H-2 additive-block rule). +- The engine verb under test is `build/erp/doc_poster.derivePostings` (W-DOC-POSTER) — the extraction that + folds a document to its GL manifest. B-3 asks: for each 0-seed class, does derivePostings reproduce what the + REAL compiled Doc_ poster emits — including the correct ZERO where config gates posting off? +- The oracle for the 6 G-seed classes is GENERATED (per the USER RULING above): prepare GardenWorld-model seed + documents, drive the REAL compiled `Doc_` on a SCRATCH copy of `idempiere_test` (never the shared + instance) with the acct setup it needs (MAcctSchema, GL category, open period, the class's default accounts), + let iDempiere post, capture that fact_acct exactly as extract_fact_acct.sh does. Provenance = the real + compiled poster over honestly-labelled seed. The expected facts are NEVER hand-authored — only the seed input. +- Fixed-Asset family: `a_*` doc tables are empty AND the depreciation batch is the DepreciationPerf lane + (memory `project_depreciation_perf`) — check for overlap before generating; a class already oracle-folded + elsewhere is CITED, not redone (the ISOMORPH_TAIL rule). + +## THE WORK +### §W-1 Triage each target (which arm — zero-by-config, generate, or honest-⛔ — BEFORE any code) +For each of the 8 classes read its `Doc_.createFacts` in iDempiere source (org.compiere.acct) and its +ad_docfsm.js:185-205 M*.java citations, and classify: +- **(Z) zero-by-config** — createFacts is gated off under GardenWorld's captured config (the Doc_Order case). + Oracle = the empty set; proof = §W-3 gate-flip. No scratch posting needed. +- **(G) generatable** — createFacts posts under an achievable minimal setup. Goes to §W-2 scratch-generate. +- **(⛔) not headless-postable** — needs semantics we cannot stand up without forking (name the exact blocker). + Honest skip, counted, WITH the reason — the WorkflowOracle un-headless precedent. Never fake its Fact_Acct. +`§B3-TRIAGE class= id= arm=Z|G|⛔ createFacts= reason=` per class. + +### §W-2 Generate the oracle for arm-(G) classes (real compiled poster on a SCRATCH PG — never invent) +Extend `scripts/extract_fact_acct.sh` (additive block, H-2 rule) or add `scripts/generate_post_oracle.sh`: +clone `idempiere_test` → scratch db, insert the minimal completed document via the REAL model classes (or the +captured GardenWorld setup rows), invoke the compiled poster headless, `SELECT fact_acct WHERE ad_table_id=` +with per-document granularity (ad_table_id/record_id/line_id/account_id/c_acctschema_id/DR/CR in integer cents) +into a versioned fixture (`build/erp/oracle/post_b3_.json` or a glassbowl_data.db additive table). +`§B3-GEN class= scratch= posted_rows= ΣDR=ΣCR= provenance=compiled-Doc_`. + +### §W-3 Diff derivePostings vs the oracle (the core, integer cents) +For each arm-(G) class: `doc_poster.derivePostings()` aggregated by (natural-account, side) == the §W-2 +oracle, **maxDiff=0c** (the W-POST-HARDEN metric). For each arm-(Z) class: derivePostings == ∅, and the +§FALSIFIER flips the config gate → BOTH the engine and the real poster emit rows (zero is config-derived, not +hardcoded — the poc_morder_post §FALSIFIER-A verbatim). Any divergence = a NAMED finding (engine gap or a +posting-semantics gap), reported not papered. +`§B3-POST class= arm= maxDiff=0c oracle=compiled-Doc_` per class. + +### §W-4 The semantics arm (Fable-5 judgment — only as far as honesty requires) +Where the diff needs the REAL poster's account resolution (Doc.getAccount, MAcctSchemaDefault, product/asset +accounting), add `scripts/logic_oracle/PostingOracle.java` mirroring LogicOracle/WorkflowOracle: drive the +compiled `Doc_` account lookup + createFacts headless over the scratch context. Name EVERY omitted +static in the header (CLogger-class drags etc.), exactly as LogicOracle does. A class that cannot run headless +without forking semantics → its (G) claim demotes to ⛔ with the reason; the classes that DO diff still stand. + +### §W-5 Witness + falsifiers + named skips +`scripts/poc_post_b3.js` → W-POST-B3 via run_witness.sh → `build/erp/poc_post_b3.log`, exit 0 AND read: +- `§B3-TRIAGE` (8 lines, one per class) · `§B3-GEN` (per arm-G) · `§B3-POST … maxDiff=0c` (per non-⛔ class). +- `§B3-FALSIFIER` ≥2 load-bearing: (1) an arm-Z gate-flip → engine==oracle both emit rows (zero is derived); + (2) drop/scale ONE derived GL line on an arm-G class → maxDiff≠0c (the diff is load-bearing, poc_doc_poster + §FALSIFIER shape). +- `§B3-SKIPS` — every arm-⛔ class + WHY, and every FA class deferred to DepreciationPerf (cited, not redone), + named + counted. The claim is "N classes oracled, maxDiff=0c", NOT "all 8" unless all 8 truly land. +- Regressions after ANY engine/extract edit: poc_post_harden, poc_factacct_doc, poc_doc_poster, + poc_morder_post, test_report_fin — ALL stay green (the extract bundle is unchanged where it matters). + +### §W-6 Bank (same session — this card is its own single writer) +- `docs/ERP_COVERAGE_MATRIX.md`: Oracle column — the 0-seed posting rows ⬜→✅ (or ⛔ with reason) per class, + full evidence format citing the log; update the ledger headline (43 → 43+N oracle-equivalent). +- `prompts/HARDEN_MATRIX.md` §H-1 posting band → B-3 status. `prompts/FABLE5_WORKFLOW_ORACLE.md` STOP-CONDITION + B-3 pointer → "opened + banked here". PROGRESS.md §Current State. This card's `# DONE` appendix — every claim + = a § line (Watchdog protocol). +- Commit bim-compiler (witness + PostingOracle.java + generate script + oracle fixtures + bank docs; prompts/ + is gitignored except FRONTEND_LANE_MASTER — card edits live on disk, note it in the commit message). +- **⏸ PUSH PAUSE is in effect (CLAUDE.md, standing until lifted): commit locally, verify on localhost/headless, + do NOT `git push` and do NOT open a PR.** Report status; the user lifts the pause. + +## STOP CONDITION / HONESTY RAILS +- DONE = every one of the 8 targets is `✅ maxDiff=0c` (posted or config-zero, oracle = real compiled poster) + OR `⛔ `; W-POST-B3 green with ≥2 load-bearing falsifiers + named skips; regressions green. + A class whose real poster DIVERGES from derivePostings → the finding IS the deliverable (a real engine/ + semantics gap beats a fake green). A step needing an un-EXTRACTABLE user fact → `⛔ BLOCKED: `, move to the next class (never loop, never silently drop — WORK-TO-ZERO). +- Do NOT invent a posting where none can be generated headless — an honest ⛔ with the blocker named is the + correct output, and it is a COMPLETE result for that class, not a failure of the card. +- Do NOT perturb the existing GardenWorld oracle bundle — additive capture only; test_report_fin must still + balance 46574.97 and poc_factacct_doc must still pass, proving B-3 added rows without forking the journal. + +## §W-2 EXECUTION SPEC (2026-07-17, Fable 5 session — written BEFORE code, per Spec-First) +**Host для the real compiled poster:** `org.idempiere.test` tycho-surefire (JUnit-under-OSGi). This resolves the +G-3 landmine (POS_GAP_CLOSE.md: bare `Adempiere.startup(false)` NPEs headless — SecureEngine needs a live OSGi +BundleContext): tycho-surefire hosts the REAL compiled classes inside a real OSGi runtime. The vendor's own +`org.idempiere.test/src/.../model/FixedAssetsTest.java` (IDEMPIERE-5474) is the direct precedent AND the seed +recipe for the addition arm: `MWorkflow.runDocumentActionWorkflow(doc, CO)` completes AND posts synchronously +because `ad_sysconfig CLIENT_ACCOUNTING='I'` (verified live in idempiere_test, client 0). +**Scratch:** `idempiere_b3` := `createdb -T idempiere_test` (never the shared instance — the glassbowl oracle was +captured from idempiere_test, so the clone shares its config byte-for-byte; the DB-snapshot-divergence landmine +says do NOT clone `idempiere` instead). Connection override: `-Didempiere.home=/b3_home` whose +`idempiere.properties` = the checkout's file with `DBname=idempiere_b3`. Drop the clone after capture. +**Seed plan (GardenWorld-model, honestly labelled; USER RULING 2026-07-17 sanctions INPUT seed prep only):** +1. Vendor recipe verbatim: `MAssetGroupAcct` EQUIPMENT (group 50007, both schemas 101+200000) UseLifeMonths=18 + (GardenWorld ships uselife 0 — the vendor's own test sets this first, div-by-zero otherwise). +2. Asset A (`MAsset` group EQUIPMENT, product ASSET_VEHICLE 200000) + `MAssetAddition` Manual/Capital + 12000/salvage 2000 → CO → posts (Doc_AssetAddition: DR a_asset_acct.A_Asset_Acct / CR getP_Asset_Acct — + Manual w/o charge falls to ProductCost(P_Expense) — at AssetSourceAmt). +3. `MDepreciationEntry` schema 101, DateAcct = asset A's first depexp month (afterSave auto-selects that month's + unassigned `A_Depreciation_Exp` rows for the schema+org) → CO → posts per-line DR depexp.DR_Account_ID / + CR CR_Account_ID at Expense. Client-11 has NO FDP doctype → seed-prep one along the GardenWorld model (the + FAA doctype 200001 shape, DocBaseType='FDP') — required by prepareIt's testPeriodOpen(getC_DocType_ID()). + The schema-200000 side is left unentered on purpose — exercises Doc_DepreciationEntry's other-schema ∅ branch. +4. `MAssetReval` asset A, same month as the processed depreciation (prepareIt: isLastDepreciated + BOTH cost and + accum must change): Cost_Change=cost+1000, Change_Acumulated_Depr=accum+100. GardenWorld has NO reval account + anywhere → seed-prep: set a_asset_acct.A_Reval_Cost_Offset_Acct (both schemas) := the group's + A_Disposal_Revenue_Acct combination (200005) — config INPUT, honestly labelled, read identically by poster + and engine. +5. Asset B + addition → CO; `MAssetTransfer` B (NOT depreciated, not month-end — prepareIt gates): old accts = + B's a_asset_acct values (group 50007: 200013/200004), new = group 50000's (200010/200011) → CO → posts + workfile cost old→new (+ accum pair, 0 here). B's workfile is untouched afterwards so the captured workfile + == posting-time values (order-dependence NAMED). +6. Asset C + addition → CO; `MAssetDisposed` C method=Simple → CO → createDisposal writes `A_Asset_Change` + (CHANGETYPE_Disposal) per workfile/schema — Doc_AssetDisposed reads THAT row (CR cost / DR accumdep / + DR disposal-loss=book value). Derive from a_asset_change, NOT the post-adjusted workfile. +7. `MProjectIssue` project 101 (category N → PJ_WIP side), setMandatory(locator 101, product 137 Mulch, qty 1) + → CO → Doc_ProjectIssue: DR c_project_acct.PJ_WIP_Acct / CR Product Asset (Expense if service) at + m_line.getProductCosts (MCost current cost × qty). +8. `commit()` (AbstractTestCase default is rollback — we commit INTO THE SCRATCH CLONE only), then + `scripts/generate_post_oracle.sh` captures fact_acct для the 6 ad_table_ids + the doc source rows + the acct + config the manifests read (a_asset_acct, a_asset_change, a_depreciation_exp, c_project_acct, doc headers, + c_validcombination additions) into `build/erp/oracle/post_b3_fixture.json` (TEXT fixture, committed — the + DB-binary ban holds), exactly the extract_fact_acct.sh column shape (integer-cents on read). +**Execution findings (run 1-5, 2026-07-17, all in `build/erp/generate_post_oracle.log` history):** +- Tycho subset reactor needs the workspace-bundle dep closure + `org.apache.ecs` (a reactor wrapper module) — + 24 modules total, ~4 min/cycle; offline (-o) fails on p2 metadata, online resolves from cache. +- Scratch IDEMPIERE_HOME needs `utils/getVar.sh` + `.idpass` + `idempiereEnv.properties` (Ini.getVar reads the + DB password through them — CConnection.java:65). +- **Dictionary-vs-code skew (this seed vs release-13 X classes):** `A_Depreciation_Workfile.UseLifeYears[_F]/ + A_Asset_Life_Years[_F]` are Integer (ref 11) in this seed's AD dictionary but BigDecimal in the compiled + X class → ClassCastException. Fixed by retyping 11→22 ON THE CLONE only (dict seed prep). +- This seed's PRODUCT id for "Asset Vehicle" is 200001 (the official test-DB DictionaryIDs say 200000 — + the DB-snapshot-divergence landmine, again). +- `MWorkflow.runDocumentActionWorkflow` hits an AD_WF_Process optimistic-lock failure on the SECOND in-trx + run → switched to engine-direct `processIt(CO)` + explicit `DocManager.postDocument` (the same real posting + entrypoint postImmediate/AcctProcessor call) — vendor-equivalent, deterministic. +- NOT-NULL-no-default columns this seed enforces beyond X defaults: entry DateDoc/IsApproved; transfer + A_Period_Start/End + A_Transfer_Balance_IS (all unread by prepare/complete/createFacts). +**Oracle test file:** canonical copy `scripts/logic_oracle/PostingOracleTest.java` (bim-compiler); the generate +script rsyncs it into `org.idempiere.test/src/org/idempiere/test/oracle/` for the run (foreign checkout kept +clean otherwise). §W-3 diff: poc_post_b3.js §B3-GEN/§B3-POST materializes the JSON fixture into in-memory +sqlite, runs the extended `doc_poster.derivePostings` per class per schema, integer cents, maxDiff=0c. + +# DONE — 2026-07-17, Opus lane (WORK-TO-ZERO). Every claim = a § line in `build/erp/poc_post_b3.log` +# (exit 0, log READ). Run: `bash build/erp/run_witness.sh scripts/poc_post_b3.js`. + +- **§W-1 triage COMPLETE — all 8 classes classified from EXTRACTED facts** — `§B3-ORACLE` 8 lines: every + target `fact_acct_rows=0 src_docs=0` (live PG + captured `glassbowl_data.db`, 300 rows none for these ids); + `§B3-TRIAGE` 8 lines with per-class `createFacts` citation. Verdict: `🟢 triage: 2 ∅-by-design + 6 ⛔ + n/a-in-seed — design=2 seed-blocked=6`. +- **2 ∅-BY-DESIGN (✅ CORRECT, structurally proven)** — C_BankTransfer (200246), C_DepositBatch (200056): + NO `Doc_` in `org.compiere.acct.Doc.get` factory (grep = no case) → posting is not in their + contract → oracle=∅ is the RIGHT answer. `§B3-DERIVE … lines=0 basis=none` for both. +- **6 G-seed → ASSIGNED TO FABLE 5** — C_ProjectIssue + A_Asset_Addition/Disposed/Reval/Transfer + + A_Depreciation_Entry: poster EXISTS, manifest source-parsed + line-cited (`§B3-TRIAGE`), but **0 source + documents**. Per the USER RULING (2026-07-17) these are generatable via GardenWorld-model seed prep (a + usability/PoC seed, NOT invention) → the Fable 5 session runs §W-2→§W-5 (prepare seed, drive the REAL + compiled `Doc_` on scratch PG, capture fact_acct = oracle, diff `derivePostings` to `maxDiff=0c`). + `§B3-ASSIGN` records all 6 + the hand-off. Only HAND-AUTHORING facts stays banned. +- **DOCTRINE CORRECTED** — my first pass read these 6 as "⛛ n/a-in-seed, prime-rule terminal" by over-applying + the ISOMORPH-tail rule. The USER RULING overturned that: seed prep along the GardenWorld model is legitimate; + the oracle is still the real poster's output. Card correction block + §W-2 updated accordingly. +- **§FALSIFIERS load-bearing (2)** — `§B3-FALSIFIER discriminates: busiest ad_table_id=319 rows=96 (>0) vs + B-3 ids all 0` (the fact_acct=0 check is NOT vacuous) + `§B3-FALSIFIER live-verb C_Order=104 lines=3 + basis=invoice` (derivePostings IS live → the ∅ for B-3 classes is class-specific, not a dead verb). +- **HAND-OFF to Fable 5 (the open work)** — the 6 G-seed classes are NOT done; they are SCOPED + assigned. + A Fable session prepares GardenWorld-model seed docs + `scripts/generate_post_oracle.sh` + + `scripts/logic_oracle/PostingOracle.java` + extends `doc_poster` with the per-class manifest, per §W-2→§W-5. + This triage supplies its starting map (arms, createFacts citations, verb + oracle-bundle locations). +- **§W-6 bank** — witness `scripts/poc_post_b3.js` committed; card DONE appendix here (on-disk, prompts/ + gitignored). Coverage-matrix / HARDEN / PROGRESS rows returned as data for the single Phase-3 writer, not + cross-written from this card. ⏸ PUSH PAUSE honoured: committed locally, NOT pushed, no PR. +- **Regression note** — no engine/extract edit was made (triage + new witness only), so the existing bundle + is untouched by construction; `glassbowl_data.db` read read-only, 300 rows intact. + +# DONE — 2026-07-17, FABLE 5 lane (§W-2→§W-6 executed; the hand-off above is CLOSED). Every claim = a +# § line in `build/erp/poc_post_b3.log` (exit 0, READ) or `build/erp/generate_post_oracle.log` (GEN_EXIT=0, READ). +# Re-run: `bash scripts/generate_post_oracle.sh` (regenerates the oracle) then +# `bash build/erp/run_witness.sh scripts/poc_post_b3.js` (the diff; also passes on the committed fixture alone). + +- **§W-2 ORACLE GENERATED — real compiled posters, OSGi-hosted, scratch clone** — `scripts/generate_post_oracle.sh`: + clone `idempiere_test`→`idempiere_b3` (`§GEN scratch=idempiere_b3 cloned`), `scripts/logic_oracle/ + PostingOracleTest.java` under the vendor's own `org.idempiere.test` tycho-surefire harness (resolves the + POS_GAP_CLOSE §G-3 "no headless OSGi" landmine; vendor FixedAssetsTest = the addition recipe). All 9 seed docs + completed AND posted by the REAL engine: `§ORACLE class=… docstatus=CO posted=Y` × 9 (3 Cap additions + the + Exp-gate addition + entry + reval + transfer + disposal + project issue). Capture `§GEN-CAPTURE fact_acct + b3-classes rows=34 ΣDRc=8136056 ΣCRc=8136056` → TEXT fixture `build/erp/oracle/post_b3_fixture.json` (24 tables, + ~74KB, committed — the DB-binary ban holds); scratch DROPPED after capture. +- **§W-3 ALL 6 G-SEED CLASSES maxDiff=0c** — `§B3-POST class= arm=G … maxDiff=0c oracle=compiled-Doc_` for + A_Asset_Addition (4 docs × 2 schemas) · A_Depreciation_Entry · A_Asset_Reval · A_Asset_Transfer · + A_Asset_Disposed · C_ProjectIssue (each 1 doc × 2 schemas), per document × schema × (natural-account, side), + integer cents. `doc_poster.derivePostings` gained the 6 manifests (fa-addition/fa-depreciation/fa-reval/ + fa-transfer/fa-disposal/project-issue), each line-citing its Doc_*.java source; accounts resolve + a_asset_acct/c_project_acct/a_asset_change/depexp combos → `c_validcombination.account_id` (the MAccount hop). +- **THE DIFF EARNED ITS KEEP (a real catch)** — first diff run: engine converted the addition's schema-200000 leg + at the 0.8006 SYSTEM rate; the real poster posts the 0.85 CLIENT-11 rate — `MConversionRate.getRate:243-252` + orders `AD_Client_ID DESC` (tenant rate outranks system). `fxRate` fixed to the verbatim pick; also the + Doc_AssetAddition currency finding: it is the ONLY B-3 poster that posts DOC-currency amounts (converted per + schema) — the other five pass the schema's own currency. +- **§W-4 semantics arm** — no separate PostingOracle.java stdin-driver needed: the OSGi test harness IS the + semantics arm (real Doc.getAccount/MAssetAcct/MConversionRate/MCost resolution ran inside the real engine). + Named infra findings (in the card's §W-2 EXECUTION SPEC block): AD-dictionary-vs-code type skew (workfile + uselife columns 11→22, clone-only), seed's product-id divergence (200001 not 200000), wf-engine optimistic-lock + on 2nd in-trx run → engine-direct processIt + DocManager.postDocument, per-step commits for null-trx readers + (Doc_AssetReval.java:105 / Doc_AssetDisposed.java:96). +- **§W-5 falsifiers load-bearing (2 new + 2 triage)** — `§B3-FALSIFIER gate-flip addition=1000000 lines=2→0` + (A_CapvsExp Cap→Exp → ∅; the ORACLE side is the seed's own Exp addition: `§ORACLE expgate_addition=1000002 + fact_rows=0` — zero is CONFIG-derived, Doc_AssetAddition.java:67-72, the poc_morder_post move) · + `§B3-FALSIFIER scale-line depexp=1000000 oracleΣDR=55556c scaledΣDR=111112c` (the metric is load-bearing) · + the two §W-1 triage falsifiers unchanged. Vacuous-∅ guard: witness REFUSES a pass on an empty oracle + (it fired for real on the untyped-fixture bug run, then on the typed fixture: 34 rows, non-vacuous). +- **§W-5 witness green + regressions green** — `🟢 W-POST-B3 PASS` exit 0; after the doc_poster/extract-side + changes: `poc_post_harden` · `poc_factacct_doc` · `poc_doc_poster` · `poc_morder_post` all exit 0 and + `test_report_fin` still balances 46574.97 over the same 300 rows (fixture is a SEPARATE additive file — + glassbowl_data.db untouched). +- **§B3-SKIPS / honesty** — 2 ∅-by-design stay structurally closed (no Doc_ factory case); A_Depreciation_Entry + NOT deferred to DepreciationPerf (that lane measured workfile-BUILD cost, never posted facts — cited, distinct); + schema-200000 depreciation-entry side left unentered ON PURPOSE (exercises the other-schema ∅ branch, which the + diff covers as 0==0 across the schema axis). Seed prep honestly labelled per the USER RULING (uselife 18 + + MDI/FMCON methods, FDP doctype, reval-offset acct := disposal-revenue combo, dict retype on clone). +- **§W-6 banked (this card = its own single writer)** — `docs/internal/ERP_COVERAGE_MATRIX.md` +1 evidence row, + ledger 43→**49**; `prompts/HARDEN_MATRIX.md` B-3 band ✅; `prompts/FABLE5_WORKFLOW_ORACLE.md` STOP-CONDITION + pointer → "opened + banked"; `PROGRESS.md` archive line. Committed locally; **⏸ PUSH PAUSE honoured — NOT + pushed, no PR.** Foreign checkout `~/idempiere-dev-setup/idempiere` left byte-identical to its pre-session + state (test file rsynced in per-run and removed; target-platform patch reverted). diff --git a/prompts/FABLE5_WORKFLOW_ORACLE.md b/prompts/FABLE5_WORKFLOW_ORACLE.md index 21c7ea0c3..273b3a778 100644 --- a/prompts/FABLE5_WORKFLOW_ORACLE.md +++ b/prompts/FABLE5_WORKFLOW_ORACLE.md @@ -80,6 +80,8 @@ sub-surface is an honest skip with the reason — the trace diff (§W-2) still s - Do NOT expand into B-3 (0-seed posting oracles) here — it is the NAMED SEQUEL (same headless-compiled-classes technique pointed at generating real posted BankTransfer/DepositBatch/ProjectIssue/FA docs on a scratch PG copy); it gets its own card after this one banks. + **→ B-3 opened + BANKED 2026-07-17: `prompts/FABLE5_B3_POSTING_ORACLE.md` (W-POST-B3 green — 2 ∅-by-design + + 6 G-seed classes `maxDiff=0c` vs the real compiled posters on a scratch clone; ledger 43→49).** # DONE — 2026-06-12, Fable 5 lane (MULTI_LANE_WAVE3 Lane A). Every claim = a § line in # `build/erp/poc_wf_harden.log` (exit 0, log READ, 0 FINDING). Run: `bash build/erp/run_witness.sh scripts/poc_wf_harden.js`. diff --git a/prompts/FUNCTIONAL_SPACES_ENSEMBLE.md b/prompts/FUNCTIONAL_SPACES_ENSEMBLE.md new file mode 100644 index 000000000..ccba83a04 --- /dev/null +++ b/prompts/FUNCTIONAL_SPACES_ENSEMBLE.md @@ -0,0 +1,429 @@ +# ⚠ DO NOT REMOVE — Scope guard +# SCOPE: ARCH-discipline functional-space derivation (rooms as first-class data across the 8 +# embedded Modeller residents) — NOT DISC/MEP walking (that lane lives in +# RESUME_DISC_WALKER_ENVELOPE_BOUND.md, do not merge these). Read the log after EVERY run — +# exit code is not evidence. PUSH PAUSE in effect: commit locally, no push, no PR. + +# FUNCTIONAL_SPACES_ENSEMBLE — spatial_structure fleet restore + fixture/raster POCs + +## 2026-07-13 — Session spec (written BEFORE implementation, per Spec-First) + +### Problem (real, verified — not hypothesized) +`finalize_all_8.js` @ bim-ootb `6068fab` silently shipped the 8 Modeller resident `*_ARC.db` +files WITHOUT the `spatial_structure` table for 6 of 8 buildings (its fresh source was an +ephemeral `/tmp/*_all.db` merge that never had the table; the carry-forward only rescued +Duplex + Terminal). Verified on bim-ootb `main` (`b3462f6`) checkout: + +| building | spatial_structure on main | expected (carry script header) | +|--------------|---------------------------|--------------------------------| +| SampleHouse | MISSING (no table) | 6 (ROOM001) | +| Duplex | present (26) | — control, never regressed | +| HHS | MISSING | 109 (ROOM002) | +| Clinic | MISSING | 200 (ROOM003) | +| Garage | MISSING | 6 (ROOM004) | +| Hospital | MISSING | 208 (ROOM005) | +| SampleCastle | MISSING | 55 (ROOM006) | +| Terminal | present (49) | — control | + +### Step 1 spec — fleet restore (the real fix) +Apply `prompts/Modeller/DISC_Walker/embed8_scripts/ROOM001..006_*_spatial_structure_carry.sql` +(already witnessed W-SPATIAL-CARRY; honest flood-fill COMPILED rows, mined from real branch +`fable/modeller-lod400-livewire@790b069`, NOT invented) to all six DBs in a fresh bim-ootb +worktree (`/tmp/wt-functional-spaces`, branch `fix/samplecastle-spatial-carry`). + +Witness (new `witness_spatial_carry_fleet.js`, node-side, real shipped modules — no re-implementation): +- W1 per building: `SELECT COUNT(*) FROM spatial_structure` == header count. +- W2 per building: `DiscWalker.spacesOf(bdb)` count — REAL shipped `modeller/disc_walker.js`, + required directly. EXPECTED HONEST RESULT: restored buildings stay 0 — `spacesOf()` excludes + `RM_%`/`≈` COMPILED rows at the query itself (placement guard, settled 2026-07-10 + `project_room_injection_split_decision`, NOT to be touched). This witness pins that the + exclusion still holds (no placement leak) while display consumers DO see the rooms. +- W3 per building: `RoomGraph.buildGraph()` (real shipped `common/room_graph.js`, the module + the Viewer Find panel Path-Between calls) — room node count == IfcSpace rows with center+size, + edge count > 0 where doors exist, and one real `shortestPath()` between two rooms. +- Browser witness: real Viewer Find panel on the patched DB (`?db=modeller/_ARC.db`), + §-log room count + a real Path-Between result. SampleCastle deep, others spot-checked. + +### Cross-check spec (user's "SampleCastle has cleanest rooms TODAY" observation) +Fetch the ACTUAL served artifacts, don't assume: GH-Pages `modeller/SampleCastle_ARC.db`, +GH-Pages `modeller/patches/SampleCastle_ARC.db.sql`, OCI `buildings/SampleCastle_extracted.db`. +Decide: regression live-visible or dev-only, per app. + +### Step 2 spec — fixture-confirmation POC (exploratory, honest) +`classifyRoomWithFixtures()` (bim-compiler `build/room_type_classifier.js`, built earlier +today, 18/18 on Duplex) — re-validate on Duplex ground truth FIRST, then apply to +SampleCastle's 51 carried rooms; report typed-vs-generic fraction, no forcing. + +### Step 3 spec — slab-raster leak POC (exploratory, honest) +Reuse `scripts/build_storey_walkable_raster.js` / `common/storey_raster.js` (real triangulated +slab mesh, 0.25m) to measure how many of SampleCastle's 51 room bboxes extend past the real +slab footprint (flood-fill leak through glass/open walls, same shape as §G3-REVISED `073336f`). +Measure + name, don't fix. + +--- + +## 2026-07-13 — Findings (every number below traces to a named log file) + +### Step 1 — FLEET RESTORE: DONE, witnessed (the real fix) +Worktree: `/tmp/wt-functional-spaces` (bim-ootb branch `fix/samplecastle-spatial-carry`, off main +`b3462f6`; witness commit `3ddf6cb`, LOCAL ONLY per PUSH PAUSE). All six carry scripts applied; +every count matches its script header exactly: +SampleHouse 6, HHS 109, Clinic 200, Garage 6, Hospital 208, SampleCastle 55. +Patched `.db` binaries are deliberately NOT committed (CLAUDE.md DB policy: binary commits banned +unconditionally — see "shippable fix" below). + +**W-SPATIAL-CARRY-FLEET (node, real shipped modules) — 38/38 PASS.** +Log: `/tmp/wt-functional-spaces/witness_spatial_carry_fleet.log` (+ scratchpad `fleet_run2.log`). +Per building (restored + Duplex/Terminal controls): +- W1 carry counts all exact (see above; Duplex 26 / Terminal 49 controls untouched). +- W2 placement guard HOLDS everywhere: `DiscWalker.spacesOf()` returns ZERO `RM_%`/`≈` COMPILED + rows. **Correction to this brief's own claim (a):** `spacesOf()` does NOT "now see 51 spaces" + for SampleCastle — the settled exclusion (2026-07-10 room-injection split) lives INSIDE + `spacesOf()`'s query itself, so it returns 0 for SampleCastle (51 rows all compiled), 0 for + HHS/Clinic/Garage/Hospital/Terminal likewise, and 3 for SampleHouse (its carried rows are REAL + extracted spaces with human names, not RM_ rows) and 21 for Duplex. That is correct shipped + behaviour, verified live — the carried rooms are display/Path-Between data, and the consumers + that DO read them are `common/room_graph.js` + `viewer/navigate_find.js` (both include COMPILED + rows by design). Exclusion not touched. +- W3 graph unlock: `RoomGraph.buildGraph()` (the exact module Find-panel Path-Between calls) + builds real room graphs on all 8 — nodes == queryable IfcSpace rows everywhere + (3/21/105/197/5/201/51/43), real door-guid edges, real `shortestPath()` per building (Garage + honestly has 0 room-to-room E1 edges; see UI witness for its legitimate circulation path). + +**W-ROOM-PATH-UI-FLEET (real browser, real Viewer Find panel, patched DBs via +`?db=/modeller/_ARC.db`) — 30/30 PASS, zero pageerror.** +Log: `/tmp/wt-functional-spaces/witness_room_path_ui_fleet.log`; screenshot (SampleCastle rooms + +green path polyline + "1 door · 3.5m" result card): `/tmp/wt-functional-spaces/w_ui_fleet_SampleCastle.png`. +Live §-lines per building (room count == carried count, real path with real door guids): +- SampleHouse: nodes=3; path Living room→Entrance hall, 1 door, 7.18m +- HHS: nodes=105; path L1 R1→R36, 2 doors, 9.35m +- Clinic: nodes=197; path 2F R2→R77, 8 doors, 63.07m +- Garage: nodes=5; path R1→R2 via E2 circulation (occupant graph), 2 real doors, 16.86m +- Hospital: nodes=201; path L2 R18→R31, 2 doors, 21.97m +- SampleCastle: nodes=51; path 00 R12→R17, 1 door (D6L `03KwpgDxXCQBYi7KkY7sYB`), 3.50m + +**Cross-check — user's "SampleCastle has cleanest rooms in Find panel TODAY": explained, fetched +the actual served artifacts, no assumption.** +- Viewer (where the Find panel lives) loads `buildings/SampleCastle_extracted.db` from OCI — + fetched it (HTTP 200, 8,040,448 bytes): HAS `spatial_structure`, 25 IfcSpace rows, all `RM_%` + COMPILED (an OLDER 25-room compile, not this 51-room set). The user's observation is the OCI + Viewer path, which never regressed. +- Modeller (GH Pages, serves main) `modeller/SampleCastle_ARC.db` — fetched (200, 1,568,768 + bytes): NO `spatial_structure` table; self-heal `modeller/patches/SampleCastle_ARC.db.sql` → + HTTP 404. So the regression IS live-visible on the Modeller side, dev-checkout AND live; only + the Viewer/OCI path masks it with the older 25-room binary. +- **Shippable fix already exists and is pushed but UNMERGED:** bim-ootb branch + `fix/meshdb-selfheal-loader` (`e7384f4`) carries `modeller/patches/_ARC.db.sql` for all six + (carry + ROOM009-014 wellformed overlays, W-PATCH-SELFHEAL 43/43 recorded in its commit) — the + loader it needs (`str_walker_outliner.js _applyPendingPatch`) is ALREADY on main (#758). Merging + that branch is the entire remaining distance to live; blocked only by PUSH PAUSE / user call. + Note its wellformed overlays supersede the pure-carry sets applied here (e.g. SC 51 rooms incl. + 9 SUSPECT review rows vs this worktree's pure 51-carry) — do not ship both, ship that branch. +- Also confirmed viable: the generic `carrySpatialStructureForward()` (finalize_all_8.js) works + for all 6 with `priorArc` pointed at `/tmp/wt-fable-livewire/modeller/*_ARC.db` (all sources + present, counts identical to the SQL dumps — verified by direct sqlite3 count per building). + The pre-baked SQL scripts were used as the applied mechanism (deterministic, already witnessed). + +### Step 2 — Fixture-confirmation POC: method re-validated on Duplex; SampleCastle has NO fixture signal +Script: `build/poc_fixture_ensemble_sc.js` (bim-compiler) · Log: `build/poc_fixture_ensemble_sc.log`. +- **P1 Duplex forward-replay (ground truth first, standing discipline): 19/19 fixture-bearing + rooms correct, 0 mismatches.** Both `fixture+size` confirmations (Kitchen/Bathroom ×2 units) and + the symbiotic guard (2 real UTILITY rooms with "Counter Top w Sink Hole" — KITCHEN keyword + correctly overruled by size, z enormous) reproduce today's recorded behaviour on the patched + worktree DB. +- **P2 SampleCastle evidence census: ZERO fixture evidence exists in this building's extraction.** + Fixture-family classes present: 27 `IfcBuildingElementProxy` (13 with transforms), distinct + names: "ROOT nulpunt", "brievenbussen" (mailboxes), "bellentableau" (doorbell panel), "kozijn" + (frame). No sanitary/kitchen keyword hits at all (checked the full FIXTURE_KEYWORDS table plus + wc/bad/keuken/closet Dutch sweeps against all 3,342 elements_meta rows). SampleCastle's ARC + extraction is walls/coverings/slabs/windows/doors/railings/stairs only — no furniture family. +- **P3 honest result: 0/51 SampleCastle rooms gain a type from fixtures** (0 rooms even contain a + fixture-family element). Gaussian-only assigns 21/51 a type (9 LIVING_ROOM, 5 BATHROOM, + 5 UTILITY, 2 KITCHEN — UNVALIDATED, no ground truth for this building), 30/51 stay honestly + UNCLASSIFIED. Conclusion: fixture confirmation is a real, proven signal WHERE furniture exists + (Duplex-class extractions); it contributes nothing on furniture-free extractions like + SampleCastle — the wall/door NAME-mining signal (COMPILE_ROOMS_TYPE_INFERENCE.md §1 Signal #1, + still unbuilt) remains the plausible next signal for such buildings, since walls are universal. + +### Step 3 — Slab-raster leak POC: 7/51 SampleCastle rooms extend past the real slab footprint +Script: `/tmp/wt-functional-spaces/poc_room_slab_leak_sc.js` (reuses §G3-REVISED `073336f` slab +placement verbatim, from `/tmp/wt-xray-fixture-fix` — those modules are not on main yet; slab-ONLY +footprint deliberately, since the shipped raster unions room rects and would be self-fulfilling). +Log: `/tmp/wt-functional-spaces/poc_room_slab_leak_sc.log`. RES=0.25m, geometry = resident +element_instances hashes resolved against shared modeller/mesh.db, all slabs mesh-resolved +(0 unresolved, 0 tilted, both buildings). +- **Duplex control first:** 16/21 fully on slab. The 5 with uncovered cells calibrate the noise + floor for REAL rooms: A201/B201 hallways 33.3% (the stairwell void under the Level-2 hallway — + a genuine slab hole, not a compile leak), A203/B203 2.1%, Roof R301 1.5%. +- **SampleCastle: 44/51 fully on slab; 7 rooms with cells off-slab.** Named, sorted: + `≈ 02 tweede verdieping R4` 52.4% (110/210 cells), `≈ 01 eerste verdieping R5` 46.8% (89/190), + `≈ 03 derde verdieping R3` 23.3% (35/150), `≈ 01 eerste verdieping R1` 8.0% (70/870), + `≈ 00 begane grond R3` 5.0% (39/782), `≈ 02 tweede verdieping R6` 2.3%, `≈ 02 tweede verdieping R1` 1.8%. + Reading it against the control: the two ~50% rooms (R4/02, R5/01) have HALF their footprint over + void — beyond anything the Duplex stair-hole noise produces on a non-circulation room — strong + flood-fill-leak candidates (the §G3-REVISED failure shape). The ≤8% tail is within the range + interior slab holes produce on real rooms. Measured and named only — no fix attempted, per scope. + +### Session state / next actions (for whoever picks this up) +- Local worktree `/tmp/wt-functional-spaces` holds: patched 6 DBs (uncommitted, correct per DB + policy), witness commit `3ddf6cb` (2 fleet witnesses), untracked POC script + logs + screenshot. + NOT pruned — it carries unpushed work (PUSH PAUSE). +- bim-compiler: this file + `build/poc_fixture_ensemble_sc.js` committed locally (no push). +- THE one merge that closes the fleet regression for live users: bim-ootb + `fix/meshdb-selfheal-loader` (user decision, post-PUSH-PAUSE). +- Open POC follow-ups, honestly parked: wall/door name-mining signal (Step 2 conclusion); + boundary-clip or re-walk for the 2 heavy leak rooms (Step 3) — spec first if picked up. + +## 2026-07-13 — §SPACE-GATE: no-MEP-outside-space refuse gate (post-placement sanity gate) + +### Spec (written BEFORE code, per protocol) +**Ask (user's framing):** "now that we have the rooms, just put a simple no-MEP-outside-space +guard." NOT a root-cause fix for why a stratum floats above the roof — a post-placement REFUSE +gate on placements the existing mechanisms already generated, applied inside `dwWalk` after +`hostBind()`/`placeMeasured()`/`placeSchedule()` finalize a placement, before it reaches the +commit path (`window.__dwWalks[disc]` → `_commitDiscWalk`). + +**Settled boundary (do not cross):** `spacesOf()`'s exclusion of `RM_`/`≈` COMPILED rows from +feeding NEW placement generation (project_room_injection_split_decision, 2026-07-10) stays +untouched. The gate only ever REFUSES an already-computed placement post-hoc — it never uses room +data to decide WHERE to place something. Using compiled-room geometry as a coarse +"is this obviously outside all known real space" containment check is a much weaker, more +defensible use than driving placement with it — that distinction is the design. + +**Two containment signals, precedence room → envelope, refuse only when BOTH fail:** +1. **Room-level (precise, rescue-only):** placement (x,y,z) inside the AABB of ANY room — + `elements_meta` IfcSpace rows ∪ `spatial_structure` IfcSpace rows INCLUDING compiled `RM_`/`≈` + rows (gate-only reader `_gateRooms`, separate from `spacesOf`), inclusive bounds + `center ± size/2`, no invented tolerance pad. Same interval-overlap primitive as + `_zOverlaps`/`_mountBand`, generalized to 3 axes. +2. **Structural Z-envelope (coarse fallback, always available):** placement z within + `[min(center_z), max(center_z)]` over all `elements_meta` (≠IfcSpace) ⋈ `element_transforms`. + **Why element CENTERS, not bbox tops:** the shipped `bbox_z` carries the known local-axis + defect — SampleHouse's rotated roof IfcMember/IfcPlate rows claim ztop=4.503 while the real + scene-measured structural top is 3.475; a bbox-top envelope would PASS the known-bad z=4.11 + stratum. `center_z` is a measured point guaranteed on the element regardless of bbox axes. + Measured verification against the 2026-07-12 fleet log (`wdwsb_FLEET_AFTER.log`, xray worktree): + max(center_z) catches BOTH known-bad strata (SampleHouse 4.11 > 3.307; HHS 11.01 > 10.645) and + keeps EVERY legit stratum fleet-wide (worst margin: HHS 9.56 vs 10.645). A fixture above every + real structural element's center is above the building's real fabric. + Note the scene-based witness measurement could not cover Terminal (structBoxes=0 — ARC meshes + not loaded in that run); the gate is DB-based so Terminal is covered by real numbers + (fixtures 0.5–15.61 vs envelope [-15.663, 27.091]). + +**Shape (WalkerDoctrine §10):** ONE named function `spaceGate(placements, bdb, disc, bldg)` in +`modeller/disc_walker.js`; every dwWalk return path (schedule / measured-band §NOSPACES / legacy) +routes through it. Refused placements are REMOVED from the committed set, returned in +`result.spaceGate.refusedList`, and §-logged per stratum (`§DW-SPACEGATE-REFUSE`) + summary +(`§DW-SPACEGATE`) — never silently dropped, never silently kept. Escape hatch +`opts.noSpaceGate=true` (same pattern as `noHostBind`). No rooms + no envelope (empty DB) → gate +no-ops honestly (cannot refuse without a measured bound). + +### Witness — W-DW-SPACE-GATE (`modeller/tests/witness_dw_space_gate.js`, bim-ootb worktree) +ISSUE IT PROVES/DISPROVES: does the gate refuse exactly the measured floating-above-structure +strata (SampleHouse "Roof" stratum, all z=4.11; HHS "Ceiling Level 02" stratum, all z=11.01, +counts taken from THIS branch's own BEFORE fleet run — the 13/177 reference counts are from the +xray branch which additionally carries §STOREY-ZBAND) with ZERO refusals on every +already-inside-space placement (Duplex, SampleCastle, Terminal, Clinic, HospitalGarage, Hospital)? +Fleet pass shape = witness_dw_storey_band.js runOne (stratified per building — pooled numbers hide +subgroup breaks). BEFORE log then AFTER log, same buildings, read the logs not the exit codes. + +### Spec revision 1 (same day, BEFORE wiring — measured, not guessed) +BEFORE-baseline on THIS branch (`logs/wdwsb_GATE_BEFORE.log`, fix/samplecastle-spatial-carry — no +§STOREY-ZBAND here, so counts differ from the xray log): SampleHouse placed=38 with **14** bad +(13 Roof|side @ z=4.11 + 1 Ground Floor|float @ z=5.23), HHS placed=722 with **177** bad +(Ceiling Level 02 @ 11.01) — same shape, branch-measured counts. Regression legs measured: +Duplex 102, SampleCastle 325 (NB 23 legit dak|side @ z=13.24), Terminal 896, Clinic 406, +HospitalGarage 2756, Hospital 3190 — all aboveRoof=0 (Terminal scene-skip, structBoxes=0). +**A DB-centers-only envelope FALSE-REFUSES SampleCastle's 23 dak fixtures** (13.24 > max +center_z 13.012, real scene top 13.356; no room tops above 11.19 to rescue). Rotation-corrected +bbox extents do NOT fix SampleHouse (its defective members carry rotation=0 with inflated +bbox_z=3.3 → corrected envelope still 4.503 > bad 4.11). Measured resolution: the envelope is the +UNION of two real measurements — `max(DB max center_z, scene structural zmax)` / +`min(DB min center_z, scene structural zmin)`, where the scene envelope is the SAME world-space +Box3 sweep the storey-band witness already computes (non-fixture meshes, op-log fixture-tag +exclusion), passed by the caller (`modeller.html _discWalkOne → opts.sceneEnv`, new helper +`_dwSceneEnvelope()`). Union-of-measured is conservative: it can only ever KEEP more than either +measurement alone, and each bound traces to real geometry. Verified against every measured +stratum: catches SH 4.11/5.23 (>3.475) + HHS 11.01 (>10.789), keeps SC 13.24 (<13.356), and a +scene-absent building (Terminal) falls back to DB centers [-15.663, 27.091] which keeps all its +strata (0.5–15.61). Known residual: a PARTIALLY-loaded scene at walk time lower-bounds at the DB +center envelope — same exposure as DB-centers-only, never worse. + +### §SPACE-GATE RESULTS — W-DW-SPACE-GATE 8/8 PASS, errors=0 (2026-07-13) +Built + wired + witnessed in `/tmp/wt-functional-spaces` (branch fix/samplecastle-spatial-carry). +Logs: `logs/wdwsb_GATE_BEFORE.log` (baseline, storey-band witness fleet pass) and +`logs/wdwsg_GATE_AFTER.log` (W-DW-SPACE-GATE) — read, not exit-coded. + +| Building | placed BEFORE | gate refused | placed AFTER | scene aboveRoof AFTER | +|---|---|---|---|---| +| SampleHouse | 38 | **14** (13 Roof\|shim:host-IfcWall-side @4.11 + 1 GroundFloor\|placed:array-density @5.23) | 24 | 0 | +| HHS | 722 | **177** (Ceiling Level 02\|placed:measured-band @11.01) | 545 | 0 | +| Duplex | 102 | 0 | 102 | 0 | +| SampleCastle | 325 | 0 (23 dak @13.24 KEPT via union env ≤13.356 — the false-refusal the DB-centers-only design would have caused) | 325 | 0 | +| Terminal | 896 | 0 (scene absent → db-centers fallback [-15.663,27.091]) | 896 | SCENE-SKIP (structBoxes=0) | +| Clinic | 406 | 0 | 406 | 0 | +| HospitalGarage | 2756 | 0 | 2756 | 0 | +| Hospital | 3190 | 0 | 3190 | 0 | + +Both known-bad populations refused exactly, stratified §DW-SPACEGATE-REFUSE logged per +storey/cls/prov; zero refusals on every already-inside-space building. Rescue-path split visible +per building in §DW-SPACEGATE (`via room=/env=`): Duplex 102/102 via rooms; HHS 6 via rooms; +Terminal 66 via rooms. + +**Honest residual notes:** +- SampleHouse's Roof-storey root cause (storey's own measured Z) is NOT fixed — the gate refuses + the symptom, exactly per the ask. Same for HHS's Ceiling Level 02 band. +- Terminal's scene half was absent at walk time (its meshes stream late) → db-centers-only bound; + its strata (0.5–15.61) sit comfortably inside, so no missed refusal is plausible there today, + but a Terminal-shaped building with a genuinely floating stratum INSIDE [-15.663,27.091] would + not be caught until its scene loads. Named, not forced. +- The gate is fixtures-only (placements). Routed chain segs/fittings are not gated — routePattern + now anchors on the POST-gate placement set (refused fixtures can't seed segments), which is the + intended containment for routing today. +- Baseline placed counts on THIS branch (SH 38, SC 325) differ from the xray branch's (28, 270) — + that branch additionally carries §STOREY-ZBAND + the 'Unknown'-storey substrate exclusion; the + two known-bad populations exist on both branches (13+1 here vs 13 there; 177 both). When the + branches merge, W-DW-SPACE-GATE's EXPECT table must be re-baselined against the merged walk. + +Files (bim-ootb worktree, committed locally — PUSH PAUSE, no push, no PR): +`modeller/disc_walker.js` (spaceGate + _gateRooms/_gateEnvelope/_spaceContains, wired into all 3 +dwWalk paths, API-exported), `modeller/modeller.html` (`_dwSceneEnvelope()` + opts.sceneEnv on +both dwWalk calls), `modeller/tests/witness_dw_space_gate.js` (W-DW-SPACE-GATE), +`modeller/tests/witness_dw_storey_band.js` (copied verbatim from the xray worktree so the BEFORE +baseline is reproducible on this branch). + +## 2026-07-13 — §OPENING-EXCL (Task A) + spacing-path audit (Task B) + device-facing (Task C) + +Same worktree (`/tmp/wt-functional-spaces`, branch `fix/samplecastle-spatial-carry`), same file +family (`modeller/disc_walker.js`'s `hostBind()`), continuing directly on top of §SPACE-GATE. + +### Task A — axis-based opening-exclusion (switches/outlets must be on solid wall) + +**Ground truth first (POC, not the fix).** `modeller/tests/poc_opening_exclusion_duplex.py` +(logs/poc_opening_duplex.log) replays hostBind's OWN geometry math (P2 BBOX_RECONSTRUCT true +midpoint, SIDE centreline projection) in Python against Duplex's 18 REAL wall devices (14 +`M_Lighting Switches`, 4 `M_Telephone Outlet`, `deploy/buildings/Duplex_extracted.db`) and 38 real +door/window openings: **18/18 on solid wall** (negative control holds) — 2 outlets sit inside a +window's RUN interval but ABOVE its z-extent (window z=[0.10,2.52], outlet z=3.85), proving the +check must be 2D (run-axis AND z), not run-only (a run-only test would false-flag those 2 real, +correctly-placed outlets). + +**Fix.** New `_onSolidWall(p, wallLine, openings)` (WalkerDoctrine §10 shape — one named function, +not per-site patches) + `_wallOpenings(bdb, geoDb)` (real door/window world bboxes, true-midpoint +corrected, cached per DB handle) + `_slideToSolid(...)` (candidate solid positions = the blocking +intervals' own measured edges ± the device's measured half-extent, no invented pad). Wired into +hostBind's SIDE branch only (the wall-mount path) — after the face position is finalized, before +`bound.push`: blocked → slide along the wall's own run axis to the nearest measured-solid +position; no solid span at that z → honest REFUSE (kept floating, counted, never fabricated). +Escape hatch `opts.noOpeningCheck=true` (spaceGate-style). Exported: `_onSolidWall`, +`_wallOpenings`, `_slideToSolid`. + +**Fleet witness — W-DW-OPENING-EXCL, `modeller/tests/witness_dw_opening_excl.js`, +`logs/wdwoe_FLEET_FINAL.log`, PASS 8/8 + DuplexLegacy leg, errors=0.** Stratified, not pooled: + +| Building | committed | side-mounts | flagged (slid/refused) | recheck-inside | +|---|---|---|---|---| +| SampleHouse | 24 | 24 | 0 | 0 | +| HHS | 545 | 12 | 1 (1/0) | 0 | +| Duplex (production, schedule path) | 102 | 0 | 0 | 0 | +| SampleCastle | 325 | 325 | 4 (4/0) | 0 | +| Terminal | 896 | 33 | 10 (10/0) | 0 | +| Clinic | 406 | 29 | 0 | 0 | +| HospitalGarage | 2756 | 49 | 1 (1/0) | 0 | +| Hospital | 3190 | 80 | 0 | 0 | + +"recheck-inside" is an INDEPENDENT re-verification — a fresh DB handle, `_wallOpenings`/ +`_onSolidWall` called again on the FINAL committed positions, not trusting the walk's own log +lines — zero committed placements land inside a real opening on any building. Every committed +count equals its §SPACE-GATE baseline exactly (slides preserve count; zero opening-REFUSALs +occurred fleet-wide, so nothing was dropped either — real slide distances measured 0.08m–4.89m, +each to a real interval edge, logged via `§DW-OPENING-SLIDE`). Duplex — the ground-truth +building — takes the SCHEDULE path in production (0 side-mounts there), so a second leg +(`§DWOE-DuplexLegacy`) forces the LEGACY walk (hostBind SIDE directly) on Duplex to exercise the +check on the building whose real devices were POC-measured: placed=100, hostBound=115, +flagged=2 (2 slid, 0 refused) — consistent with the fix working on real Duplex geometry too. + +### Task B — spacing-rule path audit (measured, not fixed — this alone is bigger than this task) + +Grepped `disc_walker.js` for `rule_code_spacing`/`SCHED-CLASH`/clearance-slide (`_clashAt`): +**every single occurrence lives inside `placeSchedule()` (lines ~492–544) — none exist in +`placeMeasured()` (§NOSPACES) or the legacy `place()`.** `dwWalk` only reaches `placeSchedule()` +via the `opts.schedule` branch; `placeMeasured()`/`place()` are the fallback when schedule data +is absent (§NOSPACES) or both paths fail (legacy). So the rule_code_spacing co-location spread + +the §W7 pairwise measured-bbox clearance-slide are **schedule-path-only** — the measured-band and +legacy paths have neither. + +**Actual walk-mode per building** (from `§WALK-SCHED`/`§WALK-NOSPACES`/`§WALK` lines, +`logs/wdwoe_OPENING_FACING.log`): only **Duplex** takes `WALK-SCHED` successfully in production +(real IfcSpace + real schedule rows). SampleHouse and SampleCastle attempt `WALK-SCHED`/ +`WALK-NOSPACES` first but both yield 0 and fall through to the **legacy** `place()`. HHS, +Terminal, Clinic, HospitalGarage, Hospital all walk via `WALK-NOSPACES` (measured-band). That is +**7 of 8 fleet buildings on an unprotected path.** + +**Cost, measured** (pairwise measured-bbox overlap over the FINAL committed set, same semantics +as placeSchedule's own `_clashAt`, stratified by prov pair, `§TASKB` lines in +`logs/wdwoe_FLEET_FINAL.log`): + +| Building | path | pairwise bbox overlaps | +|---|---|---| +| Duplex | schedule (protected) | **0** | +| SampleHouse | legacy (unprotected) | 4 (IfcWall-side × IfcWall-side) | +| HHS | measured-band (unprotected) | 0 | +| SampleCastle | legacy (unprotected) | 14 (IfcWall-side × IfcWall-side) | +| Terminal | measured-band (unprotected) | 117 (IfcCovering-bottom × IfcCovering-bottom) | +| Clinic | measured-band (unprotected) | 49 (IfcCovering-bottom × IfcCovering-bottom) | +| HospitalGarage | measured-band (unprotected) | 1 (IfcWall-side × IfcWall-side) | +| Hospital | measured-band (unprotected) | 344 (IfcCovering-bottom × IfcCovering-bottom) | + +Duplex — the only schedule-protected building — is the only one measuring 0. Every unprotected +building has real, non-zero measured-bbox overlap among its own committed fixtures (co-located +ceiling coverings mostly: LIGHT+diffuser+alarm at the same array-density cell, exactly the +scenario `rule_code_spacing`/§W7 were built to spread). **Gap named, not fixed** — porting the +spacing+clearance-slide logic to `placeMeasured()`/`place()` is a real, separate build (both paths +lack per-space bounds and a `stype` to key `rule_code_spacing` against — `placeSchedule`'s +`_clashAt` bounds slide movement to `sp.x0..sp.x1`, which doesn't exist outside a real IfcSpace), +correctly out of scope for this session per the brief. + +### Task C — wall-device orientation: real signal found in the catalog, wired + +**Signal found**, not absent. `library/component_library.db`'s `component_definitions` (the same +real catalog `§7` cites for pipe fittings) has NO switch/outlet-named rows directly, but its +`ad_product_dim` table has the real class rows: `ELEC_SWITCH`/`ELEC_OUTLET`, +`conn_points='[{"face":"BACK","type":"ELEC"}]'` — back connects into the wall, so the plate faces +the room. Every `component_definitions` row with `attachment_face='SIDE'` (wall-mounted class, +36 rows fleet-wide: alarms, controllers, wall furniture, wall-mount fixtures) carries +`up_axis='Z', forward_axis='Y'` uniformly — 36/36, no MIXED/exception. That is the real signal: +local +Y (forward) maps onto the ROOM-SIDE perpendicular. + +**Wired into hostBind's SIDE branch**, replacing the old run-axis-only yaw +(`bl.horiz===0 ? 0 : Math.PI/2` — room-side AGNOSTIC, so roughly half of all wall devices faced +INTO the wall): yaw is now `atan2(perp) - π/2` where `perp` is the wall-face-normal direction +toward the device's own room-side position — same perpendicular the SIDE mount already computes +to push the device onto the wall face, just also used for facing. + +**Bug found and fixed during witnessing, not shipped silently:** the first wiring computed yaw +from the PRE-slide perpendicular (before §OPENING-EXCL's run-axis slide). W-DW-OPENING-EXCL's own +facing recheck caught 1 SampleCastle device (`host=2A$7uUIbP13B7P0LW_wNaR`) where the pre-slide +point's wall-line projection clamped to a corner (giving a diagonal 30.4° facing), but after the +0.15m opening-exclusion slide the same point's TRUE nearest-wall relationship was no longer +clamped (true facing 90°) — the pre-slide yaw was stale. Fixed by recomputing the facing +perpendicular from the FINAL (post-slide) position, not the pre-push floating point. Re-run +confirmed `facingBad=0` fleet-wide after the fix. + +**Fleet witness result (same `witness_dw_opening_excl.js` run, `§FACING` lines, +`logs/wdwoe_FLEET_FINAL.log`): facingBad=0 on all 8 buildings.** Non-cardinal yaw values in the +histograms (e.g. SampleCastle: 1°, 6°, 30°, 63°...) are NOT noise — they occur when a device's +nearest point on hostBind's axis-aligned wall-line approximation clamps to a wall-segment +endpoint (near a corner/doorway), which correctly blends the facing direction toward the +device's actual position rather than forcing a false cardinal snap; this is unchanged +pre-existing hostBind line-approximation behavior, not something this task introduced. + +### Files (this session, `/tmp/wt-functional-spaces`, committed locally — PUSH PAUSE, no push, no PR) +`modeller/disc_walker.js` (`_onSolidWall`/`_wallOpenings`/`_slideToSolid` + SIDE-branch wiring for +Task A; SIDE-branch yaw rewrite for Task C, computed post-slide), `modeller/tests/ +poc_opening_exclusion_duplex.py` (Task A ground-truth POC), `modeller/tests/ +witness_dw_opening_excl.js` (W-DW-OPENING-EXCL, Tasks A/B/C combined witness). Logs: +`logs/poc_opening_duplex.log`, `logs/wdwoe_OPENING.log` (first fleet pass, pre-facing-fix), +`logs/wdwoe_OPENING_FACING.log` (facing wired, 1 SampleCastle FAIL caught), `logs/ +wdwoe_SC_DEBUG.log` / `logs/wdwoe_SC_RECHECK.log` (isolation + fix verification), +`logs/wdwoe_FLEET_FINAL.log` (final fleet pass, all green, errors=0). diff --git a/prompts/FUNCTIONAL_SPACE_MGMT_NEXT_SESSION.md b/prompts/FUNCTIONAL_SPACE_MGMT_NEXT_SESSION.md new file mode 100644 index 000000000..03de2359d --- /dev/null +++ b/prompts/FUNCTIONAL_SPACE_MGMT_NEXT_SESSION.md @@ -0,0 +1,431 @@ +# ⚠ DO NOT REMOVE — Scope guard +# SCOPE: cross-session handoff for the "Functional Space Management" theme (room/space accuracy + +# walkability, bim-ootb Viewer + Modeller). REPLACES the 2026-07-14 (earlier) version — that one's +# §SHIPPED work is superseded by this session's much larger §HALLWAY-BACKBONE build-out. Read this +# file first, don't re-derive what's already here. +# 2026-07-15 UPDATE: §TOP PRIORITY (island-gap closure) DONE — see §ISLAND-BRIDGE-SHIPPED at the +# bottom of this file (new, read it first if picking this up next). PR #794 open (bim-ootb +# `fix/island-connectivity-bridge`), not yet merged. + +# FUNCTIONAL_SPACE_MGMT_NEXT_SESSION — 2026-07-14 (§HALLWAY-BACKBONE session) + +## §MOST IMPORTANT LESSON — READ THIS FIRST +**Every fix in this session passed every Node-based witness while being COMPLETELY INACTIVE in the +real browser**, for most of the session, because `common/hallway_backbone.js` was never added to +`viewer/main.js`'s dynamic module-load list (fixed at the very end — see §SHIPPED PR #788). Node's +`require()` resolves a file regardless of what any HTML/load-list references; the browser only +loads what it's told to. **Witnesses proved the LOGIC was correct — they never proved the browser +was actually RUNNING it.** Before declaring any browser-facing fix done next time: drive the real +dev server with a headless browser (Playwright — see §HOW-TO-TEST-LIVE below) and confirm the +new code's own console tag/log line actually appears, not just that the offline witness is green. +This was only caught because the user asked directly "have u tested?" — that question should be +asked of yourself before it's asked of you. + +## §HOW-TO-TEST-LIVE (added this session, use every time from now on) +```bash +cd ~/bim-ootb && python3 -m http.server 8000 & +# Playwright is installed under ~/bim-ootb/tests/node_modules — require it from there: +node -e "const {chromium} = require('/home/red1/bim-ootb/tests/node_modules/playwright'); ..." +``` +- Direct building URL only, never the Hub picker: `http://localhost:8000/viewer/viewer.html?db=buildings/{Name}_extracted.db&ghost=1` + (see memory `feedback_localhost_full_building_url_testing.md` — Hub can silently fetch production for some buildings). +- Find panel: press `f` (not a visible button by default). Axis toggle: `#find-axis-toggle` + (click cycles storey→disc→room→material→phase→parts). Sub-toggle (Storey/Type/Path): plain + `