This commit is contained in:
xsxx03-art
2026-06-03 10:06:48 +08:00
parent cf28676756
commit 9b4f43f0b1
202 changed files with 14111 additions and 10107 deletions
@@ -0,0 +1,330 @@
# Required mxpic_router Stack Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make `mxpic_router` plus its Nazca/gdstk/route backend stack mandatory, and route all Build Layout and Build GDS generation through it.
**Architecture:** Add a focused backend dependency gate module and make server startup fail before Flask launches when the router stack is incomplete. Keep `routed_layout_preview.py` as the single preview builder and simplify `gds_builder.py` so the production GDS path delegates to `mxpic_router` without silent fallback.
**Tech Stack:** Python Flask backend, sibling `mxpic_router`, Nazca, gdstk, `mxpic_forge` route backend, Node static regression tests.
---
## File Structure
- Create `backend/router_dependency.py`: one responsibility, validate and expose the required router stack.
- Modify `backend/server.py`: import dependency gate, run it before `app.run`, and always call `create_routed_layout_svg` for previews.
- Modify `backend/gds_builder.py`: make `_build_with_mxpic_router` the only production builder path.
- Modify `tests/layout-backend-static.test.js`: update static assertions to the new required-router contract.
- Modify `GDS_SVG_GENERATION_LOGIC.md`: replace the two Build Layout branches with one unified router path.
- Modify `README.md` and `INTRANET_DEPLOYMENT.md`: document simultaneous EDA/router deployment.
## Task 1: Static Tests For Required Router Contract
**Files:**
- Modify: `tests/layout-backend-static.test.js`
- [ ] **Step 1: Write failing assertions**
Replace the old `server.py` assertions around `create_layout_svg_from_gds` and `create_routed_layout_svg` with assertions that:
```javascript
assert(
fs.existsSync(path.join(backendDir, 'router_dependency.py')),
'backend/router_dependency.py should validate required mxpic_router runtime dependencies'
);
assert(
serverPy.includes('from router_dependency import require_router_stack') &&
serverPy.includes('require_router_stack()'),
'server startup should check the full mxpic_router stack before launching Flask'
);
assert(
!serverPy.includes('create_layout_svg_from_gds'),
'save-layout route should not use the legacy gdstk-only preview builder'
);
assert(
serverPy.includes('create_routed_layout_svg') &&
!serverPy.includes('if layout_has_links(content):'),
'save-layout route should always use routed preview generation through mxpic_router'
);
```
Add assertions for `router_dependency.py`:
```javascript
const routerDependencyPy = fs.readFileSync(path.join(backendDir, 'router_dependency.py'), 'utf8');
assert(
routerDependencyPy.includes('def require_router_stack') &&
routerDependencyPy.includes('importlib.import_module("gdstk")') &&
routerDependencyPy.includes('importlib.import_module("nazca")') &&
routerDependencyPy.includes('mxpic_router.builder') &&
routerDependencyPy.includes('_import_mxpic_forge_route'),
'router dependency gate should validate mxpic_router, Nazca, gdstk, and route backend imports'
);
```
Replace the old GDS fallback assertions with:
```javascript
assert(
gdsBuilderPy.includes('return _build_with_mxpic_router(') &&
!gdsBuilderPy.includes('return _build_with_gdstk') &&
!gdsBuilderPy.includes('return _build_with_nazca'),
'Build GDS should use mxpic_router as the only production builder'
);
```
- [ ] **Step 2: Run the focused test to verify RED**
Run: `node tests/layout-backend-static.test.js`
Expected: FAIL because `router_dependency.py` does not exist, `server.py` still imports `create_layout_svg_from_gds`, and `gds_builder.py` still contains fallback returns.
## Task 2: Add Router Dependency Gate
**Files:**
- Create: `backend/router_dependency.py`
- Modify: `backend/server.py`
- [ ] **Step 1: Implement `backend/router_dependency.py`**
Create a small module with:
```python
import importlib
import os
import sys
from dataclasses import dataclass, field
from typing import List
@dataclass
class RouterStackStatus:
ok: bool
router_root: str
checked: List[str] = field(default_factory=list)
def ensure_router_path() -> str:
router_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "mxpic_router"))
if os.path.isdir(router_root) and router_root not in sys.path:
sys.path.insert(0, router_root)
return router_root
def require_router_stack() -> RouterStackStatus:
router_root = ensure_router_path()
checked = []
missing = []
for module_name, label in (
("mxpic_router", "mxpic_router"),
("nazca", "nazca"),
("gdstk", "gdstk"),
):
try:
importlib.import_module(module_name)
checked.append(label)
except Exception as exc:
missing.append(f"{label}: {exc}")
try:
router_builder = importlib.import_module("mxpic_router.builder")
route_factory = getattr(router_builder, "_import_mxpic_forge_route")
route_factory()
checked.append("mxpic_forge Route")
except Exception as exc:
missing.append(f"mxpic_forge Route: {exc}")
if missing:
details = "; ".join(missing)
raise RuntimeError(
"Required mxpic_router runtime stack is unavailable. "
"Deploy mxpic_EDA together with the matched mxpic_router and mxpic_forge checkouts, "
f"then install nazca and gdstk. Details: {details}"
)
return RouterStackStatus(ok=True, router_root=router_root, checked=checked)
```
- [ ] **Step 2: Wire startup check in `backend/server.py`**
Import:
```python
from router_dependency import require_router_stack
```
Before `app.run(...)` in the `if __name__ == '__main__':` block:
```python
router_status = require_router_stack()
print(f"Verified mxpic_router stack: {', '.join(router_status.checked)}")
```
- [ ] **Step 3: Run focused test to verify progress**
Run: `node tests/layout-backend-static.test.js`
Expected: still FAIL because preview and GDS builder branches have not been unified yet.
## Task 3: Unify Build Layout Preview Path
**Files:**
- Modify: `backend/server.py`
- [ ] **Step 1: Remove legacy preview imports**
Change imports from:
```python
from layout_preview import create_layout_svg_from_gds
from routed_layout_preview import create_routed_layout_svg, layout_has_links
```
to:
```python
from routed_layout_preview import create_routed_layout_svg
```
- [ ] **Step 2: Replace preview branch**
Replace the `if layout_has_links(content): ... else: ...` block inside
`save_layout` with one unconditional call:
```python
create_routed_layout_svg(
content,
svg_path,
pdk_root=current_pdk_root(),
project_dir=project_root(project),
technology_manifest_path=technology_manifest_path_for_project(project),
prefer_full_gds=prefer_full_gds_for_session(session),
)
```
- [ ] **Step 3: Run focused test**
Run: `node tests/layout-backend-static.test.js`
Expected: still FAIL only on GDS builder fallback assertions.
## Task 4: Unify Build GDS Builder Path
**Files:**
- Modify: `backend/gds_builder.py`
- [ ] **Step 1: Simplify production builder**
Change `build_project_gds` so after `_load_project_cells` validation it returns
only `_build_with_mxpic_router(...)`:
```python
cells = _load_project_cells(project_dir)
if not cells:
raise ValueError("No saved cell YAML files found for this project")
return _build_with_mxpic_router(
project_dir,
output_path,
pdk_public_root,
technology_manifest_path,
prefer_full_gds,
)
```
- [ ] **Step 2: Remove unreachable fallback code**
Delete `_cells_have_links`, `_cells_have_elements`, `_build_with_gdstk`,
`_import_public_gds`, `_build_with_nazca`, `_build_nazca_element_cells`,
`_build_nazca_element_cell`, `_element_port_offset`, `_element_pin_names`,
`_safe_pin_name`, `_safe_cell_name`, `_library_cell_by_name`, `_int`, and
`_number` if no remaining code references them.
Remove unused imports such as `math` and `PdkRegistry`.
- [ ] **Step 3: Run focused test**
Run: `node tests/layout-backend-static.test.js`
Expected: PASS.
## Task 5: Update Logic And Release Documentation
**Files:**
- Modify: `GDS_SVG_GENERATION_LOGIC.md`
- Modify: `README.md`
- Modify: `INTRANET_DEPLOYMENT.md`
- [ ] **Step 1: Update logic path markdown**
Rewrite Build Layout to describe:
```text
Click Build Layout -> handleBuildLayout -> /api/save-layout -> save_layout
writes .yml -> create_routed_layout_svg -> mxpic_router.build_project_gds
creates Nazca GDS -> gdstk reads temporary GDS and writes .svg
```
Rewrite Build GDS to describe:
```text
Click Build GDS -> /api/build-gds -> build_gds -> gds_builder.build_project_gds
loads saved .yml files -> _build_with_mxpic_router -> mxpic_router creates
nd.Cell objects and exports .gds
```
- [ ] **Step 2: Update release docs**
Add a short note to `README.md` and `INTRANET_DEPLOYMENT.md`:
```markdown
### Required Router Stack
`mxpic_EDA` must be deployed with the matched `mxpic_router` and `mxpic_forge`
checkouts. The backend validates `mxpic_router`, Nazca, gdstk, and the route
backend before launching the Flask server, so release and deploy these
repositories together.
```
- [ ] **Step 3: Run docs/static test**
Run: `node tests/layout-backend-static.test.js`
Expected: PASS.
## Task 6: Final Verification
**Files:**
- Verify only.
- [ ] **Step 1: Run focused backend contract test**
Run: `node tests/layout-backend-static.test.js`
Expected: PASS.
- [ ] **Step 2: Run all Node static tests**
Run each test script:
```powershell
node tests\account-pdk-access-static.test.js
node tests\canvas-generation-wiring.test.js
node tests\canvas-helpers.test.js
node tests\canvas-static-route.test.js
node tests\gds-download-static.test.js
node tests\layout-backend-static.test.js
node tests\layout-ui-wiring.test.js
node tests\project-load-static.test.js
```
Expected: all scripts exit code 0.
- [ ] **Step 3: Check git status**
Run: `git status --short`
Expected: modified/added files are limited to the router-stack implementation,
the plan, docs, and existing pre-task changes.
@@ -0,0 +1,93 @@
# PDK Root Technology Migration Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make `opt_pdk_public/foundries` and `opt_pdk_atlas/foundries` the single source of truth for both component PDK assets and `technology.yml` manifests, then remove the redundant `mxpic/` folder from `mxpic_EDA`.
**Architecture:** The backend resolves a role-scoped PDK root from the current Flask session. Technology listing, manifest reads, project-scoped library browsing, SVG preview, and GDS export all use that same root. A directory directly under `<role_pdk_root>/<foundry>` is treated as a technology only when it contains `technology.yml`.
**Tech Stack:** Flask backend, local YAML manifests, PowerShell/Node static tests, `mxpic_router`/Nazca build path.
---
### Task 1: Regression Tests for New PDK Source of Truth
**Files:**
- Modify: `tests/layout-backend-static.test.js`
- [ ] **Step 1: Write the failing test**
Add assertions that `backend/server.py` no longer defines `EDA_PDK_ROOT`/`YML_PATH`, does not reference `mxpic/PDKs`, lists technologies from `current_pdk_root()`, checks for `technology.yml`, and reads manifests from the active role PDK root.
- [ ] **Step 2: Run test to verify it fails**
Run: `node tests/layout-backend-static.test.js`
Expected before implementation: FAIL because the backend still scans `EDA_PDK_ROOT`.
### Task 2: Backend Migration
**Files:**
- Modify: `backend/server.py`
- [ ] **Step 1: Update root helpers**
Remove `PDK_PUBLIC_ROOT`, `EDA_PDK_ROOT`, and `YML_PATH` from active path configuration. Add a helper that safely builds `<current_pdk_root>/<foundry>/<technology>/technology.yml`.
- [ ] **Step 2: Update technology APIs**
Change `/api/technologies` and `/api/technologies/<foundry>/<technology>/manifest` to use `current_pdk_root()`. Only return technology folders containing `technology.yml`.
- [ ] **Step 3: Update build-time manifest lookup**
Change `technology_manifest_path_for_project(project)` to read the project `foundry/technology` ID and return the manifest path under the active role PDK root.
### Task 3: Documentation and Static Contract Updates
**Files:**
- Modify: `README.md`
- Modify: `INTRANET_DEPLOYMENT.md`
- Modify: `PDK_TECHNOLOGY_XSECTION_LOADING.md`
- Modify: `GDS_SVG_GENERATION_LOGIC.md`
- Modify: `tests/layout-backend-static.test.js`
- [ ] **Step 1: Update docs**
Replace old `mxpic/PDKs` source-of-truth language with role PDK root language.
- [ ] **Step 2: Keep tests aligned**
Remove assertions that require `mxpic/PDKs/Silterra/.../technology.yml` to exist inside `mxpic_EDA`.
### Task 4: Remove Redundant EDA PDK Folder
**Files:**
- Delete: `mxpic/`
- [ ] **Step 1: Verify target path**
Run: `Resolve-Path mxpic` and confirm it resolves inside `D:\mxpic\mxpic_EDA`.
- [ ] **Step 2: Delete folder**
Run: `Remove-Item -LiteralPath D:\mxpic\mxpic_EDA\mxpic -Recurse -Force`.
### Task 5: Verification
**Files:**
- Test: `tests/*.test.js`
- [ ] **Step 1: Run focused tests**
Run: `node tests/layout-backend-static.test.js`
- [ ] **Step 2: Run full static suite**
Run each `tests/*.test.js` file with Node.
- [ ] **Step 3: Check old references**
Run: `rg -n "EDA_PDK_ROOT|YML_PATH|mxpic/PDKs|mxpic\\\\PDKs" backend frontend tests README.md INTRANET_DEPLOYMENT.md GDS_SVG_GENERATION_LOGIC.md PDK_TECHNOLOGY_XSECTION_LOADING.md`
Expected: no active-code references to the deleted internal PDK root.
@@ -0,0 +1,143 @@
# Required mxpic_router Stack Design
Date: 2026-05-31
## Goal
Unify layout preview and GDS generation behind the same Nazca/mxpic_router
pipeline. `mxpic_router` is no longer an optional routed-only helper. It is a
required runtime package for `mxpic_EDA`, and both repositories should be
released together as a matched pair.
## Current Problem
`Build Layout` currently has two backend preview paths:
- No `bundles.*.links`: `server.py` calls `create_layout_svg_from_gds`, which
directly places PDK GDS with gdstk.
- With `bundles.*.links`: `server.py` calls `create_routed_layout_svg`, which
delegates to `mxpic_router`, builds Nazca GDS, then converts that GDS to SVG.
This duplicates layout semantics. Placement-only cells and routed cells can
drift because they are built by different engines. Maintenance is harder
because every future layout feature must be considered in two paths.
## Decision
Use one canonical build path for both preview SVG and project GDS:
`saved YAML -> mxpic_router -> Nazca GDS -> gdstk SVG conversion when needed`
`mxpic_router` and its required build stack are hard dependencies. The Flask
server should verify the whole stack before startup, fail early if anything is
missing, and give an actionable message.
## Dependency Gate
Add `backend/router_dependency.py` as the single backend module responsible for
router dependency validation.
Responsibilities:
- Add the sibling checkout path `../mxpic_router` to `sys.path` when it exists,
preserving the current local-development pattern.
- Import `mxpic_router.build_project_gds`.
- Import `nazca`, because router output is built as Nazca cells.
- Import `gdstk`, because preview SVG still converts router-generated GDS with
`gdstk.read_gds(...).top_level()[0].write_svg(...)`.
- Probe the route backend by importing
`mxpic_router.builder._import_mxpic_forge_route` and calling it. This uses the
same `mxpic_forge`/`mxpic.Route` lookup that actual router builds use.
- Return a small structured result for startup logging and tests.
- Raise a clear `RuntimeError` when the stack is incomplete.
Server startup should call this check before `app.run`. In script mode, startup
should exit before Flask launches if the dependency gate fails.
## Build Layout Flow
`Build Layout` should always use the router-backed preview path:
`frontend/canvas.html handleBuildLayout` -> POST `/api/save-layout` ->
`backend/server.py save_layout` writes `<cell>.yml` and route sidecar ->
`create_routed_layout_svg(...)` -> `mxpic_router.build_project_gds(...)` with
`target_cell_name=<cell>` -> temporary `.gds` -> `gdstk.write_svg(...)` ->
saved `<cell>.svg`.
There should be no `layout_has_links` branch in `save_layout`. A no-link layout
is still a valid router input; the router builds placed instances and exports
the requested top cell without drawing routes.
`layout_preview.py` can remain in the tree only if it is still useful as a
manual diagnostic module. It should not be used by production preview requests.
## Build GDS Flow
`Build GDS` should always delegate to `mxpic_router`.
`backend/gds_builder.py build_project_gds` should:
- Load saved project YAML files only if needed for validation/error messages.
- Call `_build_with_mxpic_router(...)` as the canonical builder.
- Remove silent fallback to `_build_with_gdstk` and `_build_with_nazca`.
- Raise a clear error if the required router stack is unavailable, though this
should normally be caught at server startup.
The old fallback functions may be removed if tests and imports no longer need
them. If kept temporarily, they must be clearly marked as unused diagnostics
and not reachable from the production API.
## Error Handling
Dependency failures should be early and explicit:
- Missing `mxpic_router`: tell the operator to install or place the matched
sibling checkout.
- Missing `nazca`: tell the operator the router runtime cannot build GDS.
- Missing `gdstk`: tell the operator previews cannot convert GDS to SVG.
- Missing route backend: tell the operator routing primitives are unavailable.
Request-time failures should remain JSON API errors, but normal deployment
should fail at startup before users can reach these routes.
## Testing
Update static/backend tests to assert the new contract:
- `server.py` imports and calls the router dependency gate before startup.
- `/api/save-layout` calls `create_routed_layout_svg` unconditionally for
previews.
- `/api/save-layout` no longer branches on `layout_has_links` to choose
between gdstk and router preview builders.
- `gds_builder.py` delegates to `_build_with_mxpic_router` without gdstk/Nazca
production fallback.
- The router dependency module checks `mxpic_router`, `nazca`, `gdstk`, and the
route backend.
- `GDS_SVG_GENERATION_LOGIC.md` documents the unified path.
Add a runtime smoke test for the local development environment when the required
router stack is installed: a no-link saved YAML cell should generate SVG through
`create_routed_layout_svg`. If CI does not install Nazca/router dependencies,
keep that test explicitly skipped unless the dependency gate passes.
## Release Policy
`mxpic_EDA` and `mxpic_router` should be released simultaneously.
Recommended lightweight policy:
- Update deployment notes to state that both repositories must be deployed as a
matched pair.
- Keep a visible compatibility note in `README.md` or deployment docs.
- Version compatibility checks are out of scope until both repositories expose
explicit version constants; until then, deployment docs carry the matched-pair
release requirement.
## Non-Goals
- Do not redesign the frontend YAML format in this change.
- Do not vendor/copy `mxpic_router` source into `mxpic_EDA`.
- Do not replace gdstk SVG conversion in this change; it remains the converter
after router-generated GDS.
- Do not add fallback behavior that hides a missing or incompatible router
stack.