Constraint Engine Design
Cross-domain engineering constraint validation — separate from OPA business policies
Table of Contents
Overview
The Constraint Engine validates engineering and physics constraints against the graph. It is deliberately separate from OPA, which handles business/process policies (gate governance, approval rules, segregation of duties).
| Concern | Engine | Examples |
|---|---|---|
| Engineering constraints | Constraint Engine | Power budget, pin count, thermal limits, cross-domain consistency |
| Business policies | OPA | Gate progression guards, approval requirements, role permissions |
Architecture
digital_twin/thread/constraint_engine/
├── engine.py # Constraint evaluation orchestrator
├── registry.py # Loads and indexes constraint definitions
├── evaluators/
│ ├── arithmetic.py # SUM, MAX, MIN, comparison operators
│ ├── coverage.py # Percentage coverage checks
│ └── existence.py # "Must have" / "Must not have" checks
└── rules/
├── electrical.yaml # Power budget, pin count, EMC
├── mechanical.yaml # Thermal, tolerance, clearance
├── firmware.yaml # Flash size, RAM, timing
└── cross_domain.yaml # Pin assignment consistency, connector-PCB alignment
Constraint Definition Format
Constraints are defined as YAML files containing Cypher queries that run against the Neo4j graph. Each constraint specifies what constitutes a violation and how to remediate it.
Electrical Constraints
# rules/electrical.yaml
constraints:
- id: "elec-power-budget"
name: "Power budget must balance"
domain: "electrical"
severity: "error"
description: "Total component power draw must not exceed supply capacity"
query: |
MATCH (supply:DesignElement {type: 'power_supply'})-[:POWERS]->(rail:DesignElement {type: 'power_rail'})
MATCH (rail)-[:POWERS]->(consumer:BOMItem)
WITH rail, supply.max_current_a AS supply_current, SUM(consumer.current_draw_a) AS total_draw
WHERE total_draw > supply_current
RETURN rail.name AS rail, supply_current, total_draw
violation_message: "Rail {rail} draws {total_draw}A but supply provides {supply_current}A"
remediation: "Reduce load on rail or increase supply capacity"
- id: "elec-pin-count"
name: "MCU has sufficient I/O"
domain: "electrical"
severity: "error"
description: "MCU must have enough GPIO pins for all connected peripherals"
query: |
MATCH (mcu:BOMItem {category: 'MCU'})
MATCH (mcu)-[:CONNECTS_TO]->(peripheral:BOMItem)
WITH mcu, mcu.gpio_count AS available, COUNT(peripheral) AS required
WHERE required > available
RETURN mcu.mpn AS mcu, available, required
violation_message: "MCU {mcu} has {available} GPIOs but {required} are required"
remediation: "Select MCU with more I/O or reduce peripheral count"
Mechanical Constraints
# rules/mechanical.yaml
constraints:
- id: "mech-thermal-budget"
name: "Thermal budget within limits"
domain: "mechanical"
severity: "error"
description: "Total power dissipation must not exceed thermal capacity"
query: |
MATCH (enc:DesignElement {type: 'enclosure'})
MATCH (comp:BOMItem)-[:MOUNTED_IN]->(enc)
WITH enc, enc.thermal_capacity_w AS capacity, SUM(comp.power_dissipation_w) AS total_heat
WHERE total_heat > capacity
RETURN enc.name AS enclosure, capacity, total_heat
violation_message: "Enclosure {enclosure} dissipates {total_heat}W but capacity is {capacity}W"
remediation: "Add heatsinking, ventilation, or reduce power dissipation"
- id: "mech-clearance"
name: "Component clearance check"
domain: "mechanical"
severity: "warning"
description: "Components must maintain minimum clearance for assembly"
query: |
MATCH (a:BOMItem)-[:ADJACENT_TO]->(b:BOMItem)
WHERE a.clearance_mm < 1.0
RETURN a.mpn AS partA, b.mpn AS partB, a.clearance_mm AS clearance
violation_message: "Clearance between {partA} and {partB} is {clearance}mm (minimum 1.0mm)"
remediation: "Increase component spacing in PCB layout"
Firmware Constraints
# rules/firmware.yaml
constraints:
- id: "fw-flash-budget"
name: "Firmware fits in flash"
domain: "firmware"
severity: "error"
description: "Total firmware binary size must fit within MCU flash"
query: |
MATCH (mcu:BOMItem {category: 'MCU'})
MATCH (fw:DesignElement {domain: 'firmware', type: 'binary'})
WHERE fw.size_bytes > mcu.flash_bytes
RETURN mcu.mpn AS mcu, mcu.flash_bytes AS flash, fw.size_bytes AS binary_size
violation_message: "Firmware ({binary_size} bytes) exceeds MCU flash ({flash} bytes)"
remediation: "Optimize firmware size or select MCU with more flash"
- id: "fw-ram-budget"
name: "RAM allocation within limits"
domain: "firmware"
severity: "error"
description: "Total RAM usage must not exceed MCU SRAM"
query: |
MATCH (mcu:BOMItem {category: 'MCU'})
MATCH (alloc:DesignElement {domain: 'firmware', type: 'ram_allocation'})
WITH mcu, SUM(alloc.size_bytes) AS total_ram
WHERE total_ram > mcu.sram_bytes
RETURN mcu.mpn AS mcu, mcu.sram_bytes AS available, total_ram AS required
violation_message: "RAM usage ({required} bytes) exceeds MCU SRAM ({available} bytes)"
remediation: "Reduce buffer sizes, optimize data structures, or select MCU with more RAM"
Cross-Domain Constraints
# rules/cross_domain.yaml
constraints:
- id: "xd-pin-firmware-match"
name: "Pin assignments match firmware HAL"
domain: "cross-domain"
severity: "error"
description: "Hardware pin assignments must match firmware HAL configuration"
query: |
MATCH (pin:DesignElement {domain: 'electrical', type: 'pin_assignment'})
MATCH (hal:DesignElement {domain: 'firmware', type: 'hal_config'})
WHERE pin.signal = hal.signal AND pin.gpio != hal.gpio
RETURN pin.signal AS signal, pin.gpio AS schematic_pin, hal.gpio AS firmware_pin
violation_message: "Signal {signal}: schematic pin {schematic_pin} != firmware pin {firmware_pin}"
remediation: "Align pin assignments between schematic and firmware HAL configuration"
- id: "xd-connector-pcb-match"
name: "Connector pinout matches PCB footprint"
domain: "cross-domain"
severity: "error"
description: "Mechanical connector pin assignments must match PCB footprint"
query: |
MATCH (conn:DesignElement {domain: 'mechanical', type: 'connector'})
MATCH (fp:DesignElement {domain: 'electrical', type: 'footprint'})
WHERE conn.part_id = fp.part_id AND conn.pin_count != fp.pin_count
RETURN conn.name AS connector, conn.pin_count AS mech_pins, fp.pin_count AS pcb_pins
violation_message: "Connector {connector}: mechanical pins ({mech_pins}) != PCB pins ({pcb_pins})"
remediation: "Verify connector datasheet and update footprint or mechanical model"
- id: "xd-requirement-coverage"
name: "All requirements have test coverage"
domain: "cross-domain"
severity: "warning"
description: "Active requirements must have at least one linked test procedure"
query: |
MATCH (r:Requirement {status: 'active'})
WHERE NOT (r)-[:SATISFIED_BY]->()-[:TESTED_BY]->()
RETURN r.id AS requirement_id, r.title AS title
violation_message: "Requirement '{title}' ({requirement_id}) has no test coverage"
remediation: "Create a test procedure that validates this requirement"
Evaluation API
ConstraintEngine
class ConstraintEngine:
async def evaluate_all(self) -> list[ConstraintViolation]:
"""Evaluate all constraints against current graph state."""
async def evaluate_domain(self, domain: str) -> list[ConstraintViolation]:
"""Evaluate constraints for a specific domain."""
async def evaluate_change(
self, events: list[GraphEvent]
) -> list[ConstraintViolation]:
"""Pre-commit validation: evaluate constraints affected by proposed changes."""
ConstraintViolation
class ConstraintViolation(BaseModel):
constraint_id: str
severity: Literal["error", "warning", "info"]
message: str
affected_nodes: list[str]
remediation: Optional[str]
Integration Points
The constraint engine is invoked at three points:
-
Pre-commit validation — Before a
GraphEventis applied to Neo4j,evaluate_change()checks whether the proposed change would introduce violations. Events witherror-severity violations are rejected. -
Periodic full evaluation — A scheduled job runs
evaluate_all()and publishes results to the dashboard. This catches violations that emerge from combinations of individually-valid changes. -
Gate readiness — When checking gate readiness (EVT/DVT/PVT),
evaluate_domain()is called for all relevant domains. Gates cannot pass with unresolvederror-severity violations.
Related Documents
| Document | Description |
|---|---|
| Graph Schema | Node types and relationships that constraints query against |
| Event Sourcing | Event pipeline where pre-commit validation occurs |
| Digital Twin Evolution | Full twin architecture including constraint propagation |
| MVP Roadmap | Gate engine and OPA policy integration |
Document Version: v1.0 Last Updated: 2026-02-28 Status: Technical Architecture Document
| ← Event Sourcing | Digital Twin Evolution → |