DocAction SRS — Document Lifecycle Engine¶
Foundation: BBC · DATA_MODEL · BIM_COBOL · MANIFESTO · TestArchitecture
Version: 1.5 (2026-03-26, session 79 — AD_Org_ID migration, work_output.db cleanup)
Depends on: BOMBasedCompilation.md §1.2, DISC_VALIDATE_SRS.md §9-10, DocValidate.md §9-§15, TE_MINING_RESULTS.md, BIM_Designer_SRS.md §19
Scope: The processIt() orchestration — iDempiere MOrder.processIt() mapped to
BIM compilation. How the BOM recipe pipeline and DocEvent Validation interact.
Activation rule (BBC.md §1.2): Each discipline in YAML has three states:
{prefix}_BOM(pipeline populates from named BOM),DocEvent(validation handles it), or absent (discipline does not exist). No ambiguity, no inference.Rules are DATA, not code. The engine evaluates AD_Val_Rule rows loaded from ERP.db. Adding a new rule = SQL INSERT. Adding a new jurisdiction = SQL INSERT. The Java code changes only when the ENGINE changes, not when RULES change.
Terminal as oracle: The SJTII_Terminal (48,428 elements, 8 disciplines, 7+ storeys) is the primary rule source. Every mined rule must pass Non-Disturbance against Terminal before activation. The building is ground truth; the rule adapts.
0. Discipline Routing — Three States¶
| YAML value | Meaning | Who acts |
|---|---|---|
{prefix}_BOM |
Populate from named BOM | BOM pipeline (concern #1) |
DocEvent |
Validation handles this discipline | DocEvent engine (concern #2) |
| Absent | Discipline does not exist | Nobody — building has no such discipline |
0.1 Discipline Routing Matrix¶
States are mutually exclusive per discipline per building. A discipline
cannot be both {prefix}_BOM and DocEvent simultaneously.
| Discipline | {prefix}_BOM support |
DocEvent support |
Notes |
|---|---|---|---|
| ARC | YES (all stones) | YES (generative) | Primary structure — always present |
| STR | YES (TE) | YES (generative) | Often paired with ARC in residential |
| FP | YES (TE) | YES | Seed: ad_fp_coverage, ad_space_type_mep_bom |
| ELEC | YES (TE) | YES | Seed: AD_Val_Rule 803, ad_space_type_mep_bom |
| CW | YES (TE) | YES (advisory Phase 1) | Topology-driven — density calibration only |
| SP | YES (TE) | YES (advisory Phase 1) | Same as CW |
| ACMV | YES (TE) | YES (advisory Phase 1) | Diffuser count only — duct routing Phase 2 |
| LPG | YES (TE) | Absent | Too few elements for rule mining (209 in TE) |
| REB | Absent | Absent | Removed from pipeline (Bonsai addon, not construction BOM) |
Execution order: Disciplines process sequentially in the order declared in
YAML disciplines: map. Each discipline completes (BOM walk or DocEvent placement)
before the next begins. This ensures Tier 2 cross-discipline clash detection has
a stable baseline from preceding disciplines.
Invalid state handling: If YAML declares a value other than a valid BOM
database name or DocEvent, processIt() logs BLOCK: invalid discipline routing
and aborts without modifying the compile DB. No partial state.
# Example: generative house — ARC from BOM, MEP via DocEvent
disciplines:
ARC: DM_BOM
FP: DocEvent # validation places + checks sprinklers
ELEC: DocEvent # validation places + checks electrical
CW: DocEvent # validation places + checks cold water
SP: DocEvent # validation places + checks sanitary
# ACMV, LPG absent — this house has no HVAC or gas
Two concerns, explicit routing:
| # | Concern | Trigger | What It Does |
|---|---|---|---|
| 1 | BOM recipe pipeline | Discipline = {prefix}_BOM |
Pipeline populates discipline sub-tree from the named BOM database. |
| 2 | DocEvent Validation | Discipline = DocEvent |
Discovers applicable AD_Val_Rule for this discipline, places elements from mined rules, validates against standards. |
Shared/common rules (non-clash, regulatory, AABB containment, vertical continuity) always fire via DocEvent regardless of per-discipline routing — these are cross-cutting. They apply to whatever exists in the BOM.
YAML.FP = TE_BOM → BOM pipeline populates FP from TE_BOM.db
Shared DocEvent rules still fire (non-clash, regulatory)
YAML.FP = DocEvent → DocEvent places FP elements from mined rules
DocEvent validates FP spacing, clearance
Shared rules also fire
YAML.FP absent → No FP in this building. Nothing fires for FP.
1. processIt() Lifecycle — MOrder.processIt() Mapping¶
1.1 The iDempiere Pattern¶
MOrder.processIt():
prepareIt() → validate header, create default lines, reserve inventory
approveIt() → authority/credit check
completeIt() → fire ModelValidator, create downstream docs (InOut, Invoice)
1.2 BIM processIt() — DocStatus Lifecycle¶
DocStatus: DR → IP → CO → AP
DR (Draft) User has created the order, not yet processed
IP (In Progress) processIt() — normal processing, writes to compile DB
CO (Complete) Compiled — written to output.db
AP (Approved) New {prefix}_BOM.db created — promoted to catalog
1.3 IP — processIt() (DR → IP)¶
Normal order processing. Reads YAML intent, populates compile DB.
processIt(compile DB): DocStatus: DR → IP
│
├── Read W_BuildingConfig (YAML embedded at spawn time)
│ → AABB, DocType/ST, jurisdiction, discipline routing
│
├── For each discipline in YAML:
│ │
│ ├── IF value = {prefix}_BOM:
│ │ │ BOM pipeline handles it (concern #1)
│ │ │
│ │ ├── SELECT m_bom tree from {prefix}_BOM.db
│ │ │ WHERE AD_Org_ID = discipline
│ │ │
│ │ ├── Walk BOM tree → INSERT C_OrderLine per node
│ │ │ (family_ref, host_type, dx/dy/dz, aabb_*_mm)
│ │ │
│ │ ├── Spatial slots derived from M_BOM_Line dx/dy/dz
│ │ │ (co_empty_space tables removed S74 — W008)
│ │ │
│ │ ├── For LEAF nodes: fetch LOD from component_library.db
│ │ │ → M_Product (dimensions) + component_definitions (attachment)
│ │ │ → component_geometries (mesh via geometry_hash)
│ │ │ → INSERT M_AttributeSetInstance (width, depth, height, material)
│ │ │
│ │ └── INSERT W_Verb_Node (verb execution record)
│ │
│ └── IF value = DocEvent:
│ │ DocEvent engine handles it (concern #2)
│ │
│ ├── SELECT AD_Val_Rule FROM ERP.db
│ │ WHERE discipline = ? AND jurisdiction IN (?, 'INTL')
│ │
│ ├── Read mined rule params:
│ │ typical_spacing_mm, max_spacing_mm, min_spacing_mm,
│ │ occupancy_class, check_method
│ │
│ ├── Compute placements from rules + room AABB:
│ │ pitch = min(max_spacing, dim / ceil(dim / typical_spacing))
│ │ → grid positions for FP heads, ELEC fixtures, etc.
│ │
│ ├── For each computed position:
│ │ ├── SELECT M_Product from component_library.db
│ │ │ WHERE ifc_class = rule.ifc_class
│ │ │
│ │ ├── Fetch LOD: component_definitions → component_geometries
│ │ │ (library mesh, NOT parametric — feedback_no_parametric.md)
│ │ │
│ │ ├── INSERT C_OrderLine
│ │ │ (family_ref=product_id, dx/dy/dz=computed tack)
│ │ │
│ │ ├── Spatial slot from M_BOM_Line dx/dy/dz
│ │ │ (co_empty_space tables removed S74 — W008)
│ │ │
│ │ └── INSERT M_AttributeSetInstance
│ │ (width, depth, height from M_Product)
│ │
│ └── Tier 1 validate each placement against same rules
│ → INSERT W_Validation_Result per C_OrderLine
│
├── Shared/common validation (always fires):
│ │
│ ├── Tier 2: Cross-discipline clash
│ │ → ClashDetector.checkFloor() per storey
│ │ → AD_Clash_Rule pairs (MEP×STR, PLB×ELC, FPR×ACMV...)
│ │ → ERP-maths clearance: centroid_dist - radius_a - radius_b
│ │ → INSERT W_Validation_Result (tier=2)
│ │
│ ├── Tier 3: Vertical continuity
│ │ → VerticalContinuityChecker.checkBuilding()
│ │ → Group by (discipline, ifc_class, ROUND(x), ROUND(y))
│ │ → Check X,Y drift across storeys ≤ max_xy_drift_mm
│ │ → INSERT W_Validation_Result (tier=3)
│ │
│ └── Regulatory compliance
│ → AD_Val_Rule WHERE rule_type='COMPLIANCE' (UBBL, IRC, NCC...)
│ → Room area, min dimension, ceiling height checks
│ → INSERT W_Validation_Result (tier=1, shared)
│
└── DocStatus → IP
Compile DB now has full C_OrderLine tree + validation results
User can edit in BIM Designer (slider, drag, add/remove rooms)
1.3a Error Handling and Idempotency¶
processIt() is atomic per discipline — if a discipline fails, all writes for that discipline are rolled back (SQLite transaction). Previously processed disciplines retain their state.
| Error Condition | Behaviour | DocStatus | Recovery |
|---|---|---|---|
| Missing AD_Val_Rule for DocEvent discipline | BLOCK, log RULE_NOT_FOUND:{discipline} |
Stays DR | Seed rules, retry processIt() |
| DB constraint violation (NOT NULL, FK) | BLOCK, automatic rollback for that discipline | Stays DR | Fix seed data or BOM template |
Empty BOM tree ({prefix}_BOM has 0 lines) |
BLOCK, log EMPTY_BOM:{bom_id} |
Stays DR | Populate BOM database |
| ad_space_type_mep_bom has 0 rows for room type | WARN (DocEvent places 0 elements for that room) | Proceeds to IP | Seed missing room/product row |
| Partial success (3/5 disciplines OK, 2 BLOCK) | Completed disciplines committed, blocked ones rolled back | IP (partial) | Fix blocked disciplines, re-run processIt() (idempotent — skips completed) |
Idempotency: processIt() can be re-run safely. It checks each discipline's C_OrderLine count — if already populated, it skips that discipline. This handles the partial-success case: fix the blocked discipline's seed data, re-run, and only the remaining disciplines execute.
Audit trail: Every processIt() invocation writes to W_Verb_Node:
- verb_ref = 'PROCESS_IT'
- description = 'FP: 45 placed, ELEC: BLOCK (RULE_NOT_FOUND)'
- node_status = 'COMPLETE' | 'PARTIAL' | 'BLOCKED'
1.3b Rotation Rule¶
m_bom_line.rotation_rule stores a single decimal angle in radians, applied
as 2D planar rotation around the Z-axis in the parent-local coordinate frame.
3D rotation is NOT supported in the current pipeline.
| Column | Type | Unit | Range | Used By |
|---|---|---|---|---|
rotation_rule |
REAL | radians | [0, 2π) | PlacementCollectorVisitor (line 140-149, 214-226) |
Compound rotation: When the BOM walker descends the tree (BUILDING → FLOOR
→ SET → LEAF), each node's rotation_rule is cumulative. The walker applies
parent_rotation + child_rotation to each child's offset before computing
world coordinates. This is a simple angle sum (2D), not quaternion composition.
Extraction vs generative: - Extracted: rotation_rule is NULL for most elements (no rotation in IFC data). Non-null only for MIRROR (DX: π radians) and angled placements. - Generative: rotation_rule set by placement rules or user in BIM Designer.
1.4 CO — completeIt() (IP → CO)¶
Compile to output.db. The order is now a built building.
completeIt(compile DB → output.db): DocStatus: IP → CO
│
├── CompilationPipeline.run() — same 12-stage pipeline
│ → BOM walker reads C_OrderLine tree from compile DB
│ → Resolves LODs from component_library.db
│ → Writes elements + spatial + geometry to output.db
│
├── INSERT W_Verb_Node records (execution audit trail)
│ → One row per verb invocation
│
├── ProveStage (Stage 9) gate
│ → G1-G6 gates run on output.db
│ → Generative: must PASS including validation
│ → Extracted: validation READONLY, gates still check geometry
│
└── DocStatus → CO
output.db exists. Building is compiled. Viewable in Bonsai.
1.5 AP — approveIt() (CO → AP)¶
Promote to catalog. Creates new {prefix}_BOM.db. Governance gate.
approveIt(compile DB → {prefix}_BOM.db): DocStatus: CO → AP
│
├── Pre-check: all W_Validation_Result = PASS (no BLOCK)
├── Pre-check: all dangles resolved (family_ref → M_Product or m_bom)
├── Pre-check: host tack tagging verified (W-TACK-1 invariant)
│
├── Walk C_OrderLine tree:
│ For each line:
│ → INSERT INTO m_bom in NEW {prefix}_BOM.db
│ → INSERT INTO m_bom_line for each child
│ → entity_type = 'U' (User-created)
│ → Provenance = 'GENERATIVE'
│
├── New {prefix}_BOM.db is now a reusable catalog entry
│ → Can be referenced by future YAML as a discipline source
│ → Other buildings can set YAML.ARC = this new BOM
│
└── DocStatus → AP
Master C_Order frozen. BOM created. Design is now catalog.
1.6 Database Writes per Phase¶
| Phase | DocStatus | Database | Tables Written | What |
|---|---|---|---|---|
| processIt | DR→IP | compile DB | C_OrderLine, spatial slots (M_BOM_Line dx/dy/dz), M_AttributeSetInstance, W_Validation_Result | Design decisions + all validation tiers |
| processIt | DR→IP | component_library.db | (read-only) | LOD fetch: M_Product → component_definitions → component_geometries |
| processIt | DR→IP | {prefix}_BOM.db | (read-only) | BOM tree source for {prefix}_BOM disciplines |
| processIt | DR→IP | ERP.db | (read-only) | AD_Val_Rule for DocEvent disciplines + shared rules |
| completeIt | IP→CO | output.db | elements_meta, element_transforms, element_instances, base_geometries, spatial_structure, c_order, c_orderline | Compiled output |
| completeIt | IP→CO | compile DB | W_Verb_Node, C_Order.DocStatus | Execution audit + status update |
| approveIt | CO→AP | NEW {prefix}_BOM.db | m_bom, m_bom_line | Promoted catalog (governance gate, not common) |
DR → IP → CO is the common path. Every building goes through process + compile. AP is advanced — governance gate with strict pre-checks (validation PASS, dangles resolved, host tack verified). Most buildings stay at CO. AP creates a new reusable BOM catalog entry.
1.4b LOD Assembly + Handlers¶
See DISC_VALIDATE_SRS.md for full detail:
- §9 — LOD resolution chain (5-table metadata lookup)
- §9.1-9.5 — DocEvent LOD resolution, quantity modes, placement rules, coverage tables
- §10 — Post-placement handlers H1-H6 (connectivity, non-clash, spacing, host, vertical, completeness)
- §10.2 — Common vs DocEvent-only handler classification
1.7 Compile DB — Tacking + Discipline Store¶
The compile DB (BomDropper output) stores all temporal, in-progress state: tacking (spatial positions) and discipline assignment (which trade owns each element). Both are editable during IP and frozen at CO. (Prior to S61, this role was served by work_output.db; see §1.10.)
| Table | Column | What it stores | Editable during IP |
|---|---|---|---|
C_OrderLine |
dx, dy, dz | LBD tack offset in parent | YES — drag/move |
C_OrderLine |
AD_Org_ID | Discipline assignment (FP, ELEC, CW...) | YES — reassign |
C_OrderLine |
family_ref | Product/BOM reference | YES — swap product |
C_OrderLine |
Parent_OrderLine_ID | Tree hierarchy | YES — reparent |
M_BOM_Line |
dx/dy/dz | Spatial offset (WHERE) | YES — follows OrderLine |
M_BOM_Line |
aabb_*_mm | Slot AABB capacity | YES — resize |
M_AttributeSetInstance |
width/depth/height | Instance sizing | YES — slider |
W_Validation_Result |
result | Tier 1/2/3 pass/warn/block | Recomputed on change |
W_Verb_Node |
verb_ref | Verb execution | Written at CO |
1.8 Discipline Verbs During IP¶
During IP, discipline assignment is fluid. The user or DocEvent engine can invoke these verbs on C_OrderLine.AD_Org_ID:
REASSIGN — Move element to different discipline¶
| Field | Value |
|---|---|
| Verb | REASSIGN |
| Input | C_OrderLine_ID, target_discipline (AD_Org_ID) |
| Precondition | Target discipline node exists under same storey. If not, auto-create. |
| Writes | C_OrderLine.AD_Org_ID, C_OrderLine.Parent_OrderLine_ID |
| Validation | Tier 1 re-run on element (new discipline rules). Tier 2 re-run on storey (new pair interactions). |
| W_Verb_Node | verb_ref='REASSIGN', description='pipe_42: CW→SP' |
SQL: UPDATE AD_Org_ID + Parent_OrderLine_ID, DELETE+re-INSERT W_Validation_Result.
SPLIT — Break combined discipline into sub-disciplines¶
| Field | Value |
|---|---|
| Verb | SPLIT |
| Input | source_discipline (e.g. 'MEP'), split_map (ifc_class → target_discipline) |
| Precondition | Source discipline node exists with mixed elements. |
| Writes | New C_OrderLine discipline nodes (host_type=DISCIPLINE). UPDATE AD_Org_ID + Parent_OrderLine_ID per element. |
| Validation | Full Tier 1+2+3 re-run (discipline landscape changed). |
| W_Verb_Node | verb_ref='SPLIT', description='MEP→CW(338)+SP(189)+FP(312)+ELEC(65)' |
Disambiguation heuristic: (1) unambiguous ifc_class → direct map, (2) ambiguous (IfcPipeSegment) → check element_ref string, (3) no match → stay in source, WARN. Source node → IsActive=0 (soft delete, audit trail).
MERGE — Combine disciplines into one¶
| Field | Value |
|---|---|
| Verb | MERGE |
| Input | source_disciplines[] (e.g. ['SP']), target_discipline (e.g. 'CW') |
| Precondition | All source + target discipline nodes exist under same storey. |
| Writes | UPDATE AD_Org_ID + Parent_OrderLine_ID. Source nodes → IsActive=0. |
| Validation | Tier 1 re-run (target rules apply). Tier 2 re-run (fewer pairs). |
| W_Verb_Node | verb_ref='MERGE', description='SP→CW (single plumbing contract)' |
REPARENT — Move element to different storey or room¶
| Field | Value |
|---|---|
| Verb | REPARENT |
| Input | C_OrderLine_ID, target_parent_id (storey/room/discipline node) |
| Precondition | Target parent exists. Same discipline (use REASSIGN to change discipline). |
| Writes | C_OrderLine.Parent_OrderLine_ID, C_OrderLine.dz (recomputed). Spatial slot cache updated. |
| Validation | Tier 1 on BOTH old and new parent (spacing recalc). Tier 3 re-check (vertical continuity may break). |
| W_Verb_Node | verb_ref='REPARENT', description='sprinkler_23: GF→L1' |
SWAP — Replace product, keep position¶
| Field | Value |
|---|---|
| Verb | SWAP |
| Input | C_OrderLine_ID, new_product_id |
| Precondition | New product exists in component_library.db. Same ifc_class family (don't swap a pipe for a light). |
| Writes | C_OrderLine.family_ref. New M_AttributeSetInstance (dimensions from new product). |
| Reads | component_library.db: M_Product → component_definitions → component_geometries (new LOD). |
| Validation | Tier 1 re-run (same spacing rules, different product dims/attachment). |
| W_Verb_Node | verb_ref='SWAP', description='sprinkler: pendant→upright' |
1.9 Discipline Lifecycle Summary¶
See §1.2-1.3 for the full processIt() lifecycle (DR → IP → CO → AP) and §1.8 for discipline verbs available during IP (REASSIGN, SPLIT, MERGE, REPARENT, SWAP).
1.10 Construction Standards Localization — iDempiere C_Country Pattern¶
In iDempiere, C_Tax is localized by C_Country_ID + ValidFrom. The tax
engine is generic — the data drives country-specific behaviour. BIM validation
follows the same pattern: AD_Val_Rule.jurisdiction + AD_Val_Rule.valid_from.
Localization Model¶
iDempiere: BIM Validation:
C_Country → C_Tax jurisdiction → AD_Val_Rule
C_Tax.Rate = 6% AD_Val_Rule_Param.value = '3000'
C_Tax.ValidFrom = 2024-01-01 AD_Val_Rule.valid_from = '2012'
C_Tax.C_TaxCategory_ID AD_Val_Rule.discipline + rule_type
W_BuildingConfig.jurisdiction ← set at project creation
→ SELECT AD_Val_Rule WHERE jurisdiction IN (?, 'INTL')
→ Jurisdiction-specific rules + international common rules
Jurisdiction Registry¶
| Code | Country | Primary Standards | Residential | Fire | Electrical | Plumbing |
|---|---|---|---|---|---|---|
| MY | Malaysia | UBBL 2012 | Room sizes §33, ceiling §36, corridor §40, door §41 | SS CP 52 | SS CP 5 | SS CP 48 |
| US | United States | IRC 2021 / IBC 2021 | R304 room, R305 ceiling, R311 egress | NFPA 13 | NEC (NFPA 70) | IPC |
| UK | United Kingdom | Building Regs / NDSS 2015 | NDSS room sizes, Part B fire | BS 9251 | BS 7671 | — |
| AU | Australia | NCC 2022 | F5/10.3 heights, door 820mm, corridor 1000mm | AS 2118.1 | AS/NZS 3000 | AS/NZS 3500 |
| SG | Singapore | BCA Approved Docs | Ceiling 2400mm, corridor 1200mm, door 850mm | SS CP 52 | SS CP 5 | SS CP 48 |
| INTL | International | — | — | NFPA 13 (baseline) | NEC 300.4 (baseline) | — |
Common Standards (INTL — always loaded)¶
Engineering practice rules that fire regardless of jurisdiction: NFPA 13 sprinkler spacing (3000-4600mm), NEC 300.4 conduit-pipe clearance (150mm), column vertical continuity (25mm drift), riser vertical continuity (50mm drift), sprinkler-duct obstruction (3x rule), opening host association.
Jurisdiction-Specific Standards¶
Detailed rule tables (MY rules 101-110, US 201-205, UK 301-304, AU 401-404,
SG 501-503) are seeded in V004_mined_rules.sql and documented in
DocValidate.md §9-§11 with per-jurisdiction thresholds.
Construction Verbs¶
Three verb categories — all jurisdiction-independent (same engine, different thresholds):
- Joining verbs (FIT, JOIN, ATTACH, SCREW, BOLT, WELD, CLAMP, MOUNT, EMBED, HANG) — how elements connect. Recorded on W_Verb_Node.
- Placement verbs (TILE, ROUTE, ARRAY, PLACE, SNAP) — how elements are positioned. See BIM_COBOL.md §4.6 for details.
- Validation verbs (CHECK PLACEMENT, VERIFY PLACEMENT, CONNECT FITTINGS) — how compliance is checked. Results in W_Validation_Result.
See DocValidate.md for jurisdiction-specific rule packs and BIM_COBOL.md §4.6 for joining verb details and W_Verb_Node recording.
Adding a New Jurisdiction¶
Same as adding a new country to iDempiere — data only, no code:
1. INSERT INTO AD_Val_Rule (jurisdiction='NZ', ...)
→ New Zealand Building Code rules
2. INSERT INTO AD_Val_Rule_Param (...)
→ Specific thresholds for NZ
3. User creates project: W_BuildingConfig.jurisdiction = 'NZ'
4. processIt() → SELECT AD_Val_Rule WHERE jurisdiction IN ('NZ', 'INTL')
→ NZ-specific + international common rules fire
→ Zero Java changes
Mined Rules — Terminal as Calibration¶
Mined rules (M1-M17) from Terminal carry jurisdiction='INTL' and
valid_from='MINED:SJTII_Terminal'. They represent observed engineering
practice from a real building — not a specific code. They serve as:
- Calibration — the Terminal values confirm what spacing/clearance real engineers used in practice (not just the code minimum)
- Typical values —
typical_spacing_mm(from mining) vsmax_spacing_mm(from code). DocEvent uses typical for placement, max for validation. - Non-Disturbance baseline — every mined rule must pass against the
Terminal it was mined from. See
TE_MINING_RESULTS.md.
1.11 Architectural Simplification: work_output.db Removal (S61)¶
Decision: work_output.db is removed from the architecture. The result is a
4-DB split: component_library / ERP (incl. compliance rules) / BOM / output.
Rationale: The BOM is read-only (EntityType D). The user cannot edit it — only promote it as a new BOM (approveIt). Save = Blender native save (.blend file). The viewport (Bonsai/Blender) holds all visual state. There is no intermediate design buffer to persist — work_output.db was a buffer between edits and compilation that has no edits to buffer.
Lifecycle: See §1.2-1.3 for the full DR → IP → CO → AP flow. Post-S61, all compile-time state lives in the compile DB (no work_output.db intermediary).
Output DB path: The output path is editable in the Web UI (Tab 1 — Order).
Defaults to output/{project_name}.db from C_DocType.OutputDbPath. User can
rename before each CompleteIt to version their builds (e.g. output/demo_v2.db).
What moves where:
| Was in work_output.db | Now lives in | Notes |
|---|---|---|
| C_Order + C_OrderLine | Compile DB | BomDropper already writes here |
| M_AttributeSetInstance | Compile DB | ASI overrides per C_OrderLine |
| ~~co_empty_space_line~~ | (removed S74 — W008) | Placement via M_BOM_Line dx/dy/dz |
| W_Validation_Result | Compile DB | Validation results |
| W_Verb_Node | Compile DB | Verb audit trail |
| W_Variant | Removed | No variants — BOM is read-only, no edit history |
| W_BuildingConfig | Removed | YAML config lives in C_DocType.DSLContent |
Promote conditions (approveIt → AP): A new BOM must declare:
- Parent category: M_Product_Category_ID (e.g. RESIDENTIAL, COMMERCIAL) — determines where it appears in the selection list
- Qualified name: Unique, descriptive (e.g. BUILDING_SH_V2_KITCHEN_SWAP) — prevents clutter
- Author / version: Metadata for traceability
- Children validated: All child BOM references must resolve (no dangling FKs)
Without these, promote is blocked. The selection list stays organized.
S61 completed. Sections §1.6-§1.9 already reflect post-removal compile DB architecture.
2. Validation Requirements¶
| Prefix | Domain | Count |
|---|---|---|
| DV-F | Functional (rule engine behaviour) | 20 |
| DV-N | Non-functional (performance, scale) | 6 |
| DV-E | Error/edge-case handling | 8 |
| DV-T | Testability (witness, Non-Disturbance) | 6 |
Priority: P0 = needed for generative path, P1 = needed for ambient compliance, P2 = future (clash resolution, auto-fix).
2.1 Current State — What Exists¶
| Component | Status | Location |
|---|---|---|
PlacementValidator interface |
DONE | validation/PlacementValidator.java |
PlacementValidatorImpl |
DONE — Tier 1 COMPLIANCE only | validation/PlacementValidatorImpl.java |
InferenceEngine |
DONE — Kahn's topo sort, proof tree | validation/InferenceEngine.java |
PlacementRequest / ValidationVerdict |
DONE | validation/ records |
| AD_Val_Rule schema | DONE — in ERP.db | migration/V002_validation_schema.sql |
| Mined rules M1-M17 seed data | DONE | migration/V004_mined_rules.sql |
| UBBL/IRC/UK/AU/SG residential rules | DONE — seeded | DocValidate.md §9-§11 |
| Non-Disturbance analysis | DONE — documented | TE_MINING_RESULTS.md |
| Tier 2 engine (clash/clearance) | PARTIAL — ClashDetector.java exists (bbox-intersection), but does not match the AD_Clash_Rule-driven engine in §4.1 | Spec: DocValidate.md §4, §13.2 |
| Tier 3 engine (vertical continuity) | NOT DONE | Spec: DocValidate.md §12, §13.3 |
| ERP-maths spatial predicates | IMPLEMENTED — SpatialPredicates.java: nnDistance(), centreClearance() | Spec: DocValidate.md §15.6 |
| ConstructionModelSpawner | NOT DONE | Spec: DocValidate.md §14-§15.2 |
| Non-Disturbance automated test | NOT DONE | Spec: DocValidate.md §7.2, §15.3 |
Note: Two separate rule ecosystems. ERP.db (63 rules, jurisdiction/discipline/rule_type schema) and ERP.db (415 rules, provenance/ifc_class/check_method schema) are separate ecosystems with incompatible schemas. ERP.db drives the DocEvent engine (this spec). ERP.db drives the extraction pipeline (see ConstructionAsERP.md).
2.2 Functional Requirements — Tier 1 Enhancements (DV-F)¶
3.1 check_method Dispatch¶
The current PlacementValidatorImpl.extractActual() only handles 4 param names
(min_area_m2, min_dim_mm, min_height_mm, min_width_mm). Mined rules use
richer check_method params that require dedicated evaluation logic.
| ID | Requirement | Acceptance Criteria | Priority | Rule(s) |
|---|---|---|---|---|
| DV-F-01 | check_method dispatch. When AD_Val_Rule_Param has a check_method row, the engine calls the corresponding evaluator instead of simple threshold comparison. |
evaluateRule() checks for check_method param first. If present, dispatches to named method. If absent, falls back to threshold comparison. |
P0 | M2, M3, M5, M7, M8, M16, M17 |
| DV-F-02 | ERP-maths NN distance. check_method=NN_CENTROID_DISTANCE computes planar (XY) nearest-neighbour distance between same-class elements on the same storey. |
Given N elements of class C on storey S, compute MIN(XY_dist) for each element. Compare to min_spacing_mm and max_spacing_mm params. Return BLOCK if outside range. |
P0 | M1, M4, M6 |
| DV-F-03 | ERP-maths clearance. check_method=CENTRELINE_CLEARANCE computes centreline-to-centreline distance minus half cross-section of each element. |
clearance = centroid_2D_dist - radius_a - radius_b where radius = MIN(width, depth) / 2. Same formula as TE_MINING_RESULTS.md M12 §Attempt 2. |
P0 | M12 |
| DV-F-04 | ROUTE segment sum. check_method=ROUTE_SEGMENT_SUM sums lengths of pipe segments sharing a verb_ref run from TEE to terminal. |
Query m_bom_line WHERE verb_ref LIKE 'ROUTE%', group by run_id, SUM allocated_width_mm (longest dim = run length). Compare to max_run_length_mm. |
P1 | M2 |
| DV-F-05 | M_Product cross-section. check_method=M_PRODUCT_CROSS_SECTION validates pipe/conduit diameter from product dimensions. |
cross_section = MIN(width, depth) from M_Product. Compare to min_main_diameter_mm / min_branch_diameter_mm. Distinguish main vs branch by parent BOM hierarchy. |
P1 | M3 |
| DV-F-06 | Per-storey Z consistency. check_method=PER_STOREY_Z_CONSISTENCY checks that same-class elements on a storey have consistent Z. |
Compute STDDEV(center_z) for elements of class C on storey S. Compare to max_z_deviation_mm. WARN if exceeded. |
P1 | M5 |
| DV-F-07 | TILE verb fidelity. check_method=TILE_VERB_FIDELITY verifies TILE step consistency against product dims. |
Already proven by pipeline (0.0mm fidelity). This check confirms post-compilation: TILE(rows×cols, step_mm) matches placed element spacing. | P2 | M8 |
| DV-F-08 | Beam span vs bay. check_method=BEAM_LENGTH_VS_BAY checks beam product width against nearest column pair spacing. |
For each IfcBeam, find two nearest IfcColumn on same storey. Beam width ≤ column_pair_dist × (1 + tolerance_pct/100). | P1 | M7 |
| DV-F-09 | Opening face-anchor. check_method=CENTROID_DEPTH_VS_HOST_CENTER validates door/window centering in host wall. |
depth_offset = |opening_centroid_depth - host_wall_center|. Compare to max_depth_offset_mm. Skip if ASI has face_anchor=INT|EXT. Skip if host wall thinner than min_host_wall_thickness_mm. |
P1 | M16 |
| DV-F-10 | Opening host association. check_method=AABB_PROXIMITY verifies every opening (IfcDoor/IfcWindow) has a nearby IfcWall. |
Find nearest IfcWall by centroid distance. If distance > proximity_tolerance_mm, WARN. Future: upgrade to FK check when host_element_ref column lands (R20). |
P1 | M17 |
3.2 Verdict Types¶
| ID | Requirement | Acceptance Criteria | Priority |
|---|---|---|---|
| DV-F-11 | Three verdicts. Engine returns PASS, WARN, or BLOCK per rule. WARN = advisory (logged, shown in ambient strip). BLOCK = hard stop (prevents compilation). | ValidationVerdict gains WARN level. Rules with verdict=WARN param never return BLOCK. Default = BLOCK if no verdict param. |
P0 |
| DV-F-12 | ADJUST verdict. When a BLOCK can be auto-corrected (e.g., snap dimension to minimum), return ADJUST with suggested values. | ValidationVerdict.adjust(suggestedWidth, suggestedDepth). BIM Designer slider auto-corrects to suggested value on ADJUST. |
P1 |
2.3 Functional Requirements — Tier 2: Cross-Discipline Clash (DV-F)¶
4.1 ClashDetector Engine¶
| ID | Requirement | Acceptance Criteria | Priority |
|---|---|---|---|
| DV-F-13 | ClashDetector class. Reads AD_Clash_Rule from ERP.db, evaluates element pairs across discipline boundaries on the same storey. | ClashDetector.checkFloor(Connection bomConn, Connection valConn, String storeyId) returns List<ClashResult>. Uses R-tree (elements_rtree) to narrow candidates, then applies AD_Clash_Rule filters. |
P1 |
| DV-F-14 | Clash types. HARD (intersection), SOFT (close proximity), MATERIAL (penetration through rated assembly), CLEARANCE (min distance). | AD_Clash_Rule.clash_type drives check logic: HARD = AABB overlap, SOFT = overlap margin, MATERIAL = overlap + fire_rating check, CLEARANCE = min_distance_mm. |
P1 |
| DV-F-15 | ALLOW_IF conditional verdicts. When clash verdict = ALLOW_IF, check condition column. If condition met (e.g., fire_stop_product_id IS NOT NULL), pass. |
ClashResult has conditionMet boolean. If ALLOW_IF and condition not met, return BLOCK with resolution_note from AD_Clash_Rule. |
P2 |
4.2 Terminal-Inferred Clash Rules¶
Source: DocValidate.md §4.1, TE_MINING_RESULTS.md M9-M12
| Rule ID | Disciplines | Type | Threshold | Evidence from Terminal |
|---|---|---|---|---|
| 701 | MEP × STR | HARD | 0mm (intersection) | Beams at z=7.7-25.9m, pipes route around |
| 702 | PLB × ELC | CLEARANCE | 150mm | M12: 11 true overlaps, 35 under 150mm |
| 703 | FPR × ACMV | CLEARANCE | 300mm (3× obstruction) | NFPA 13 obstruction rule |
| 7/8 | SP/ACMV × ARC (fire-rated) | MATERIAL | 0mm + fire_stop | M11: requires fire collar |
4.3 ERP-Maths for Cross-Discipline Distance¶
clearance(A, B) = centroid_2D_distance(A, B) - radius(A) - radius(B)
where:
centroid = (center_x, center_y) -- from element_transforms
radius(E) = MIN(width, depth) / 2 -- cross-section from M_Product
width/depth from AABB or M_Product dims -- same data BOM already carries
This is the proven M12 formula from TE_MINING_RESULTS.md: - Uses only M_Product dimensions + placement positions - No mesh, no Blender, no viewport - Works at compile time, design time, and batch time - 48,428-element Terminal validates in sub-second (SQL aggregation)
2.4 Functional Requirements — Tier 3: Vertical Continuity (DV-F)¶
| ID | Requirement | Acceptance Criteria | Priority |
|---|---|---|---|
| DV-F-16 | Vertical continuity checker. Groups elements by (discipline, ifc_class, ROUND(x,1), ROUND(y,1)) across storeys. Checks X,Y drift ≤ tolerance. | VerticalContinuityChecker.checkBuilding(conn, buildingId) returns List<ContinuityResult>. Each result: element_group, storeys_spanned, max_drift_mm, verdict. |
P1 |
| DV-F-17 | Storey span minimum. Only flag vertical groups spanning ≥ min_storey_span storeys. Single-storey elements skip. |
Rule param min_storey_span (default 3 for risers, 2 for columns). Groups spanning fewer storeys return PASS. |
P1 |
5.1 Terminal-Inferred Vertical Rules¶
Source: V004_mined_rules.sql rules 809-811
| Rule | Discipline | Elements | Max Drift | Min Storeys | Terminal Evidence |
|---|---|---|---|---|---|
| M13 (809) | CW | IfcPipeSegment | 50mm | 3 | CW risers at GF/L1/L2/L3/L4 — consistent x,y |
| M14 (810) | STR | IfcColumn | 25mm | 2 | 56 GF columns + 4 L2 columns — grid positions fixed |
| M15 (811) | FPR | IfcPipeSegment | 50mm | 3 | FP risers span all storeys, tapped per floor |
5.2 Query Pattern (from Terminal DB)¶
-- Elements with vertical continuity: same (x,y) across multiple storeys
SELECT em.discipline, em.ifc_class,
ROUND(et.center_x, 0) as grid_x,
ROUND(et.center_y, 0) as grid_y,
COUNT(DISTINCT ss.storey) as storey_count,
GROUP_CONCAT(DISTINCT ss.storey) as storeys,
MAX(et.center_x) - MIN(et.center_x) as x_drift,
MAX(et.center_y) - MIN(et.center_y) as y_drift
FROM elements_meta em
JOIN element_transforms et ON em.guid = et.guid
JOIN spatial_structure ss ON em.guid = ss.guid
WHERE em.ifc_class IN ('IfcPipeSegment', 'IfcColumn')
GROUP BY em.discipline, em.ifc_class,
ROUND(et.center_x, 0), ROUND(et.center_y, 0)
HAVING COUNT(DISTINCT ss.storey) >= 2
ORDER BY storey_count DESC;
Interpretation: Groups with x_drift or y_drift > tolerance = WARN/BLOCK.
Groups spanning all storeys with <25mm drift = genuine vertical continuity (columns).
Groups spanning 3+ storeys with <50mm drift = MEP risers.
3. Non-Functional Requirements (DV-N)¶
| ID | Requirement | Threshold | Rationale |
|---|---|---|---|
| DV-N-01 | Tier 1 latency. Single PlacementRequest validation. | < 5ms (in-memory rule lookup) | Real-time slider feedback in BIM Designer |
| DV-N-02 | Tier 2 latency. Cross-discipline clash check per floor. | < 200ms for 1000 elements per floor | Ambient compliance strip update |
| DV-N-03 | Tier 3 latency. Vertical continuity for entire building. | < 500ms for Terminal-scale (48K elements) | On-demand check, not real-time |
| DV-N-04 | Rule loading. activate(jurisdiction) loads all rules. | < 50ms for any jurisdiction | One-time cost on session start |
| DV-N-05 | Scale. Validation engine handles Terminal-scale data. | 48,428 elements, 8 disciplines, 7 storeys | Terminal is the largest Rosetta Stone |
| DV-N-06 | Memory. Cached rules fit in JVM heap. | < 1MB for 100 rules × 10 params each | Rules are small (ints, strings, doubles) |
4. Error and Edge-Case Handling (DV-E)¶
| ID | Scenario | Expected Behaviour | Priority |
|---|---|---|---|
| DV-E-01 | ERP.db missing or unreadable | activate() throws with clear message. BIM Designer shows "Validation rules unavailable" in ambient strip. Compilation proceeds without validation (DISABLED mode). |
P0 |
| DV-E-02 | Rule has unknown check_method |
Log WARN, skip rule. Do NOT block compilation for unimplemented check methods. Return SKIP in InferenceEngine results. | P0 |
| DV-E-03 | Circular rule dependency | InferenceEngine already handles (Kahn's cycle detection). All cycle members → SKIP. Logged. | DONE |
| DV-E-04 | Element has no M_Product match | ERP-maths checks require product dimensions. If product_id not in M_Product, skip element with WARN log. | P0 |
| DV-E-05 | Storey name mismatch | Terminal uses Malay names ("Aras Tanah", "Aras 01"). DX uses English ("Level 1"). Storey matching must use the storey column from spatial_structure, not hardcoded names. | P0 |
| DV-E-06 | Element has no element_transforms | Some elements may lack centre coordinates. Skip with WARN, do not crash. | P0 |
| DV-E-07 | ALLOW_IF condition references non-existent product | ALLOW_IF fire_stop_product_id check: if product doesn't exist in library, treat as condition-not-met → BLOCK with resolution note. | P1 |
| DV-E-08 | Jurisdiction mismatch | Rule has jurisdiction='MY', project has jurisdiction='US'. Rule not loaded. INTL rules always load. | P0 |
5. Testability Requirements — Non-Disturbance (DV-T)¶
8.1 Non-Disturbance Protocol¶
The building is ground truth. Rules must describe reality, not prescribe it.
| ID | Requirement | Acceptance Criteria | Priority |
|---|---|---|---|
| DV-T-01 | Non-Disturbance gate test. Automated test runs all active rules against SH, DX, and TE extracted data. | NonDisturbanceTest class. For each stone: load rules, run Tier 1+2+3, assert 0 BLOCK verdicts (excluding documented AD_Val_Rule_Exception). |
P0 |
| DV-T-02 | Exception accounting. Known exceptions (DX P23: 364, TE M12: 35) are counted and verified against AD_Val_Rule_Exception. | Test loads exceptions, verifies count matches expected. If new exceptions appear, test FAILS until documented. | P0 |
| DV-T-03 | Rule regression. Any rule parameter change re-triggers Non-Disturbance. | Test compares V004 migration checksum. If changed, full re-run. CI gate. | P1 |
8.2 Witness Claims¶
| Witness | What it Proves | Depends On |
|---|---|---|
| W-DV-ND-SH | Non-Disturbance PASS for SampleHouse (58 elements, ARC+STR+CW) | DV-T-01 |
| W-DV-ND-DX | Non-Disturbance PASS for Duplex (1099 elements, multi-discipline) | DV-T-01 |
| W-DV-ND-TE | Non-Disturbance PASS for Terminal (48428 elements, 8 disciplines) | DV-T-01 |
| W-DV-T2-CLR | Tier 2 clearance: M12 ELEC-SP detection matches TE_MINING_RESULTS | DV-F-03, DV-F-13 |
| W-DV-T3-COL | Tier 3 vertical: STR column continuity matches Terminal grid | DV-F-16 |
| W-DV-PERF | Tier 1+2+3 on Terminal completes within latency contracts | DV-N-01..03 |
8.3 Anti-Regression: Rule Decision Tree¶
When Non-Disturbance finds a violation on a Rosetta Stone:
Violation found:
├─ Is it in AD_Val_Rule_Exception?
│ YES → Expected. COUNT matches? PASS. Count changed? FAIL (investigate).
│ NO ──┐
│ ├─ Is this design intent? (engineer approved deviation)
│ │ YES → ADD AD_Val_Rule_Exception. Document reason. Re-run.
│ │ NO ──┐
│ │ ├─ Is the rule tolerance too tight?
│ │ │ YES → Widen param. Re-run. Document in V004 comment.
│ │ │ NO → Rule is wrong. Fix or remove. Re-derive.
6. Data Flow — The Three-Tier Cascade (Code-Level)¶
Three tiers fire during processIt() (see §1.3 for full pseudocode):
| Tier | Hook | Engine | Input → Output |
|---|---|---|---|
| 1 | ModelValidator.beforeSave | PlacementValidatorImpl.validate(PlacementRequest) |
PlacementRequest (category, dims, position) → ValidationVerdict (PASS/WARN/BLOCK + rule ref) |
| 2 | DocAction.prepareIt | ClashDetector.checkFloor(bomConn, valConn, storeyId) |
AD_Clash_Rule pairs per storey → List<ClashResult> (element pairs, clash type, verdict) |
| 3 | DocAction.completeIt | VerticalContinuityChecker.checkBuilding(bomConn, valConn, buildingId) |
CONTINUITY rules, element groups by (discipline, ifc_class, ROUND(x), ROUND(y)) → List<ContinuityResult> (drift per group, verdict) |
Tier 1 dispatches by check_method if present, else threshold comparison (see DV-F-01).
Tier 2 uses M12 clearance formula (see §4.3). Tier 3 checks x/y drift across storeys (see §5.1).
7. Integration Points¶
10.1 BIM Designer Ambient Compliance¶
DocValidate.md §9.5 + BIM_Designer_SRS.md §18.4:
Ambient strip shows live validation status:
[GREEN] 12/12 rules PASS | [YELLOW] 1 WARN: ELEC spacing | [RED] BLOCK: bedroom < 3000mm
Tier 1: fires on every slider change (< 5ms)
Tier 2: fires on floor completion or manual trigger (< 200ms)
Tier 3: fires on building compile or manual trigger (< 500ms)
10.2 Compile Bridge¶
When BIM Designer compiles (G-6 CompileBridge): 1. Tier 1 validation runs during BOM creation (beforeSave) 2. Tier 2 runs after all discipline BOMs populated (prepareIt) 3. Tier 3 runs after all floors compiled (completeIt) 4. If mode=ACTIVE and any BLOCK → compilation halted, user notified 5. If mode=READONLY → all results logged, compilation proceeds
10.3 ProveStage (Stage 9) Gate¶
For generative buildings (Provenance=GENERATIVE):
- ProveStage includes validation as a sub-gate
- All three tiers must PASS (excluding documented exceptions)
- Gate FAILS if any un-documented BLOCK verdict exists
For extracted buildings (Provenance=EXTRACTED): - Validation runs in READONLY mode - Gate PASSES regardless of validation results - Results logged for analytics/reporting only
8. Implementation Plan — Phased Delivery¶
Phase 1: check_method Dispatch (DV-F-01..10)¶
Files to modify:
- PlacementValidatorImpl.java — add check_method dispatch in evaluateRule()
- InferenceEngine.java — mirror check_method dispatch
- PlacementRequest.java — extend with spatial fields (centerX/Y/Z, product dims)
New file:
- SpatialPredicates.java — reusable ERP-maths functions:
- nnDistance(List<Element>, Element) → double (planar XY)
- centreClearance(Element, Element) → double (M12 formula)
- perStoreyZConsistency(List<Element>) → double (stddev)
Test: CheckMethodDispatchTest.java — one test per check_method
Phase 2: ClashDetector (DV-F-13..15)¶
New files:
- ClashDetector.java — loads AD_Clash_Rule, evaluates discipline pairs
- ClashResult.java — result record
Data: AD_Clash_Rule already seeded (rules 701-703, 7-8 in V004)
Test: ClashDetectorTest.java — use Terminal data, verify M12 counts match
Phase 3: VerticalContinuityChecker (DV-F-16..17)¶
New files:
- VerticalContinuityChecker.java — groups elements, checks drift
- ContinuityResult.java — result record
Test: VerticalContinuityTest.java — verify Terminal column grid
Phase 4: Non-Disturbance Automated Gate (DV-T-01..03)¶
New file:
- NonDisturbanceTest.java — runs all tiers against SH/DX/TE
- Loads AD_Val_Rule_Exception, accounts for documented deviations
- CI gate: fails build if any un-documented BLOCK
Phase 5: ConstructionModelSpawner (DocValidate.md §14)¶
Deferred until G-7 (Assembly Builder). Depends on: - C_Order / C_OrderLine schema in compile DB (G-4 DONE) - W_Verb_Node table (not yet created) - Full BOM tree walking (BOMWalker exists)
9. Traceability Matrix¶
| SRS Requirement | Spec Reference | Code Location | Test Witness |
|---|---|---|---|
| DV-F-01 | DocValidate.md §15.5 | PlacementValidatorImpl.evaluateRule() | W-DV-CHECK-DISPATCH |
| DV-F-02 | TE_MINING_RESULTS.md M1 | SpatialPredicates.nnDistance() | W-DV-ND-TE |
| DV-F-03 | TE_MINING_RESULTS.md M12 | SpatialPredicates.centreClearance() | W-DV-T2-CLR |
| DV-F-13 | DocValidate.md §4 | ClashDetector.checkFloor() | W-DV-T2-CLR |
| DV-F-16 | DocValidate.md §12 | VerticalContinuityChecker.checkBuilding() | W-DV-T3-COL |
| DV-T-01 | DocValidate.md §7.2 | NonDisturbanceTest | W-DV-ND-SH/DX/TE |
| DV-N-01..03 | BIM_Designer_SRS.md §18.4 | Performance assertions | W-DV-PERF |
10. Terminal DB as Rule Oracle — Spatial Evidence Summary¶
Terminal spatial footprints (48,428 elements, 8 disciplines, 7+ storeys) extracted
via mining pipeline. Position data from elements_meta, element_transforms,
spatial_structure. See TE_MINING_RESULTS.md for full
M1/M4/M5/M6/M12 distributions, discipline footprints, and implied placement rules
(structural grid, FP coverage, riser continuity, ceiling alignment, z-zone ordering).
11. Migration — V005_depends_on.sql¶
The depends_on column in AD_Val_Rule enables rule dependency chains
(InferenceEngine Kahn's sort). This migration adds the column if missing.
-- V005_add_depends_on.sql
-- Add depends_on FK for rule dependency chains (InferenceEngine)
ALTER TABLE AD_Val_Rule ADD COLUMN depends_on INTEGER REFERENCES AD_Val_Rule(ad_val_rule_id);
Already tracked as untracked file: migration/V005_add_depends_on.sql.
References: DocValidate.md (master validation spec) | TE_MINING_RESULTS.md (Terminal mining data) | BIM_Designer_SRS.md §18-19 (ambient compliance, inference) | BOMBasedCompilation.md §3-4 (tack, BUFFER) | TestArchitecture.md (G1-G6 gates, traceability) | V004_mined_rules.sql (seeded rules)