graph LR subgraph PIPE["Pipeline"] GEN["generator.js"] PRE["preprocess.js"] DC["divideConquer.js"] MW["mergeLine.js"] POLY["polygonizer.js"] end subgraph SUP["Support"] GEO["geometry.js"] BIS["bisector.js"] L1M["l1Metric.js"] end VOR["voronoi.js facade"] GEN --> PRE PRE --> DC DC --> MW MW --> POLY POLY --> VOR GEO -.-> BIS L1M -.-> BIS BIS -.-> DC BIS -.-> MW style VOR fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style GEN fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style L1M fill:#fff9c4,stroke:#f57f17,stroke-width:3px
graph LR P0["Phase 0\ncontract test"] --> P1["Phase 1\nStrategy + Context"] P1 --> P2["Phase 2\nFactory / Null / DRY"] P2 --> P3["Phase 3\nDTO + Adapter"] P3 --> P4["Phase 4\nPython parity"] P4 --> P5["Phase 5\noracle out of API"] style P0 fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style P1 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style P2 fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style P3 fill:#fff9c4,stroke:#f57f17,stroke-width:3px style P4 fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style P5 fill:#d1c4e9,stroke:#6a1b9a,stroke-width:3px
graph LR L1["l1Metric.js\ncreateL1Metric"] MW["mergeLine.js"] DC["divideConquer.js"] POLY["polygonizer.js"] BIS["bisector.js"] L1 -->|"metric.bisector"| MW L1 -->|"metric.isUpward"| MW L1 -->|"metric.distance"| BIS L1 -->|"metric.width/height"| POLY L1 -->|"metric.distance"| DC style L1 fill:#fff9c4,stroke:#f57f17,stroke-width:3px style MW fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style BIS fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px
graph LR subgraph CORE["Computational core (mutable graph)"] SITE["Site"] BIS["Bisector"] POLY["polygonizeSite\npolygonPoints + neighbors"] end subgraph EDGE["Presentation boundary"] CELL["cell.js\ntoCell + toSVGPath"] end OUT["public cell\nsite, bisectors,\npolygonPoints, d, neighbors"] POLY --> CELL CELL --> OUT style CELL fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style OUT fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style POLY fill:#fff9c4,stroke:#f57f17,stroke-width:3px
graph LR subgraph BEFORE["py_ai/voronoi.py"] MONO["869 lines\neverything"] end subgraph AFTER["py_ai/ package"] FAC["voronoi.py facade\n20 lines"] M1["geometry / bisector"] M2["l1_metric / merge_line"] M3["divide_conquer / polygonizer"] M4["cell / preprocess / generator"] end MONO --> FAC FAC -.-> M1 FAC -.-> M2 FAC -.-> M3 FAC -.-> M4 style MONO fill:#f8bbd0,stroke:#c2185b,stroke-width:3px style FAC fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px
graph TD C["42-case corpus\nseeded LCG"] C --> G["Golden master\nnpm test"] C --> D["JS vs Python diff\nnpm run test:diff"] PY["py_ai pytest"] --> D G --> R{"all green?"} D --> R P["pyflakes"] --> R R -->|yes| OK["behavior preserved"] style C fill:#e3f2fd,stroke:#1565c0,stroke-width:3px style OK fill:#c8e6c9,stroke:#2e7d32,stroke-width:3px style R fill:#fff9c4,stroke:#f57f17,stroke-width:3px