feat: add wire duct and din rail model assets
parent
d16dcc3fed
commit
61fa285472
@ -0,0 +1,46 @@
|
|||||||
|
# QET Cabinet Assets
|
||||||
|
|
||||||
|
This directory contains reusable cabinet placement assets for QET / FreeCAD exchange examples.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `qet_wire_duct.FCStd`: FreeCAD native wire duct asset.
|
||||||
|
- `qet_wire_duct.step`: geometry-only wire duct exchange export.
|
||||||
|
- `qet_din_rail.FCStd`: FreeCAD native DIN rail asset.
|
||||||
|
- `qet_din_rail.step`: geometry-only DIN rail exchange export.
|
||||||
|
- `qet_cabinet_assets_report.json`: generated metadata for verification.
|
||||||
|
- `create_qet_cabinet_assets.py`: FreeCAD Python generator used to recreate the assets.
|
||||||
|
|
||||||
|
## Wire Duct
|
||||||
|
|
||||||
|
The wire duct is a gray open duct for cabinet routing:
|
||||||
|
|
||||||
|
- Length: `200 mm`
|
||||||
|
- Width: `40 mm`
|
||||||
|
- Height: `40 mm`
|
||||||
|
|
||||||
|
It includes a base plate, two side walls, comb-style side slots, and mounting hole markers.
|
||||||
|
|
||||||
|
## DIN Rail
|
||||||
|
|
||||||
|
The DIN rail is a metal-gray DIN 35mm rail:
|
||||||
|
|
||||||
|
- Length: `200 mm`
|
||||||
|
- Width: `35 mm`
|
||||||
|
- Height: `7.5 mm`
|
||||||
|
|
||||||
|
It includes a hat-shaped rail profile, return lips, and elongated mounting slot markers.
|
||||||
|
|
||||||
|
## Semantics
|
||||||
|
|
||||||
|
These are placement and routing support assets. They intentionally do not contain terminal LCS objects or engineering binding fields such as `QetProjectUuid`, `QetElementUuid`, `QetTerminalUuid`, or `QetInstanceId`.
|
||||||
|
|
||||||
|
## Regenerate
|
||||||
|
|
||||||
|
On this Windows workstation, use the registered FreeCAD runtime:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$runtime = Get-Content -LiteralPath 'C:\Users\ng123\AppData\Local\QETDeps\runtime.json' -Raw | ConvertFrom-Json
|
||||||
|
$env:QET_FREECAD_RUNTIME_JSON = 'C:\Users\ng123\AppData\Local\QETDeps\runtime.json'
|
||||||
|
& $runtime.freecad_python 'D:\LightWork3D\data\examples\qet_cabinet_assets\create_qet_cabinet_assets.py'
|
||||||
|
```
|
||||||
@ -0,0 +1,233 @@
|
|||||||
|
# Generate reusable QET cabinet assets: wire duct and DIN rail.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def _bootstrap_windows_freecad_runtime():
|
||||||
|
if os.name != "nt":
|
||||||
|
return
|
||||||
|
|
||||||
|
runtime_json = os.environ.get("QET_FREECAD_RUNTIME_JSON")
|
||||||
|
if not runtime_json:
|
||||||
|
runtime_json = os.path.join(os.environ.get("LOCALAPPDATA", ""), "QETDeps", "runtime.json")
|
||||||
|
if not runtime_json or not os.path.exists(runtime_json):
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(runtime_json, "r", encoding="utf-8-sig") as handle:
|
||||||
|
runtime = json.load(handle)
|
||||||
|
|
||||||
|
roots = [str(item) for item in runtime.get("path_prefix", []) if item]
|
||||||
|
freecad_root = runtime.get("freecad_root", "")
|
||||||
|
if freecad_root:
|
||||||
|
roots.extend(
|
||||||
|
[
|
||||||
|
os.path.join(freecad_root, "build", "Mod", "Material"),
|
||||||
|
os.path.join(freecad_root, "build", "Mod", "Part"),
|
||||||
|
os.path.join(freecad_root, "build", "Mod", "Import"),
|
||||||
|
os.path.join(freecad_root, "build", "Mod"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
roots.append(os.path.join(os.environ.get("SystemRoot", r"C:\Windows"), "System32", "downlevel"))
|
||||||
|
|
||||||
|
for root in roots:
|
||||||
|
if root and os.path.isdir(root):
|
||||||
|
try:
|
||||||
|
os.add_dll_directory(root)
|
||||||
|
except (AttributeError, OSError):
|
||||||
|
pass
|
||||||
|
if root not in sys.path:
|
||||||
|
sys.path.append(root)
|
||||||
|
|
||||||
|
|
||||||
|
_bootstrap_windows_freecad_runtime()
|
||||||
|
|
||||||
|
import FreeCAD as App
|
||||||
|
import Part
|
||||||
|
|
||||||
|
|
||||||
|
OUT_DIR = Path(__file__).resolve().parent
|
||||||
|
REPORT_PATH = OUT_DIR / "qet_cabinet_assets_report.json"
|
||||||
|
|
||||||
|
ENGINEERING_BINDING_PROPERTIES = {
|
||||||
|
"QetProjectUuid",
|
||||||
|
"QetElementUuid",
|
||||||
|
"QetTerminalUuid",
|
||||||
|
"QetInstanceId",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _style(obj, color, transparency=0):
|
||||||
|
if not hasattr(obj, "ViewObject") or obj.ViewObject is None:
|
||||||
|
return
|
||||||
|
obj.ViewObject.ShapeColor = color
|
||||||
|
obj.ViewObject.Transparency = transparency
|
||||||
|
|
||||||
|
|
||||||
|
def _box(doc, name, dx, dy, dz, x, y, z, color, transparency=0):
|
||||||
|
obj = doc.addObject("Part::Feature", name)
|
||||||
|
obj.Shape = Part.makeBox(dx, dy, dz, App.Vector(x, y, z))
|
||||||
|
_style(obj, color, transparency)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _cylinder_z(doc, name, radius, height, x, y, z, color):
|
||||||
|
obj = doc.addObject("Part::Feature", name)
|
||||||
|
obj.Shape = Part.makeCylinder(radius, height, App.Vector(x, y, z), App.Vector(0, 0, 1))
|
||||||
|
_style(obj, color, 0)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _rounded_slot(doc, name, length, radius, height, x, y, z, color):
|
||||||
|
left = Part.makeCylinder(radius, height, App.Vector(x - length / 2.0 + radius, y, z), App.Vector(0, 0, 1))
|
||||||
|
right = Part.makeCylinder(radius, height, App.Vector(x + length / 2.0 - radius, y, z), App.Vector(0, 0, 1))
|
||||||
|
center = Part.makeBox(length - 2.0 * radius, radius * 2.0, height, App.Vector(x - length / 2.0 + radius, y - radius, z))
|
||||||
|
obj = doc.addObject("Part::Feature", name)
|
||||||
|
obj.Shape = left.fuse(center).fuse(right)
|
||||||
|
_style(obj, color, 0)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _export_step(objects, path):
|
||||||
|
try:
|
||||||
|
import Import
|
||||||
|
|
||||||
|
Import.export(objects, str(path))
|
||||||
|
except Exception:
|
||||||
|
import ImportGui
|
||||||
|
|
||||||
|
ImportGui.export(objects, str(path))
|
||||||
|
|
||||||
|
|
||||||
|
def _create_wire_duct():
|
||||||
|
doc = App.newDocument("QETWireDuct")
|
||||||
|
length = 200.0
|
||||||
|
width = 40.0
|
||||||
|
height = 40.0
|
||||||
|
wall = 2.0
|
||||||
|
x0 = -length / 2.0
|
||||||
|
y0 = -width / 2.0
|
||||||
|
|
||||||
|
light_gray = (0.72, 0.74, 0.74)
|
||||||
|
dark_gray = (0.18, 0.2, 0.22)
|
||||||
|
|
||||||
|
objects = [
|
||||||
|
_box(doc, "WireDuct_BasePlate", length, width, wall, x0, y0, 0.0, light_gray),
|
||||||
|
_box(doc, "WireDuct_LeftWall", length, wall, height, x0, y0, 0.0, light_gray),
|
||||||
|
_box(doc, "WireDuct_RightWall", length, wall, height, x0, width / 2.0 - wall, 0.0, light_gray),
|
||||||
|
]
|
||||||
|
|
||||||
|
slot_count = 18
|
||||||
|
slot_pitch = length / slot_count
|
||||||
|
finger_width = slot_pitch * 0.45
|
||||||
|
for index in range(slot_count):
|
||||||
|
center_x = x0 + slot_pitch * (index + 0.5)
|
||||||
|
objects.append(
|
||||||
|
_box(
|
||||||
|
doc,
|
||||||
|
"WireDuct_LeftCombSlot_{0:02d}".format(index + 1),
|
||||||
|
finger_width,
|
||||||
|
wall + 0.2,
|
||||||
|
height - 8.0,
|
||||||
|
center_x - finger_width / 2.0,
|
||||||
|
y0 - 0.1,
|
||||||
|
8.0,
|
||||||
|
dark_gray,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
objects.append(
|
||||||
|
_box(
|
||||||
|
doc,
|
||||||
|
"WireDuct_RightCombSlot_{0:02d}".format(index + 1),
|
||||||
|
finger_width,
|
||||||
|
wall + 0.2,
|
||||||
|
height - 8.0,
|
||||||
|
center_x - finger_width / 2.0,
|
||||||
|
width / 2.0 - wall - 0.1,
|
||||||
|
8.0,
|
||||||
|
dark_gray,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for center_x in (-60.0, 0.0, 60.0):
|
||||||
|
objects.append(_cylinder_z(doc, "WireDuct_MountHole_{0:g}".format(center_x), 2.2, wall + 0.2, center_x, 0.0, 0.0, dark_gray))
|
||||||
|
|
||||||
|
doc.recompute()
|
||||||
|
fcstd = OUT_DIR / "qet_wire_duct.FCStd"
|
||||||
|
step = OUT_DIR / "qet_wire_duct.step"
|
||||||
|
doc.saveAs(str(fcstd))
|
||||||
|
_export_step(objects, step)
|
||||||
|
return {
|
||||||
|
"name": "wire_duct",
|
||||||
|
"fcstd": str(fcstd),
|
||||||
|
"step": str(step),
|
||||||
|
"dimensions_mm": {"length": length, "width": width, "height": height},
|
||||||
|
"objects": [obj.Name for obj in objects],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _create_din_rail():
|
||||||
|
doc = App.newDocument("QETDINRail")
|
||||||
|
length = 200.0
|
||||||
|
width = 35.0
|
||||||
|
height = 7.5
|
||||||
|
x0 = -length / 2.0
|
||||||
|
y0 = -width / 2.0
|
||||||
|
|
||||||
|
metal = (0.55, 0.57, 0.58)
|
||||||
|
dark = (0.16, 0.17, 0.18)
|
||||||
|
|
||||||
|
objects = [
|
||||||
|
_box(doc, "DINRail_CenterTop", length, 15.0, 2.0, x0, -7.5, height - 2.0, metal),
|
||||||
|
_box(doc, "DINRail_LeftWeb", length, 2.0, height, x0, -9.5, 0.0, metal),
|
||||||
|
_box(doc, "DINRail_RightWeb", length, 2.0, height, x0, 7.5, 0.0, metal),
|
||||||
|
_box(doc, "DINRail_LeftFlange", length, 8.0, 1.6, x0, y0, 0.0, metal),
|
||||||
|
_box(doc, "DINRail_RightFlange", length, 8.0, 1.6, x0, width / 2.0 - 8.0, 0.0, metal),
|
||||||
|
_box(doc, "DINRail_LeftReturnLip", length, 2.2, 2.0, x0, y0, 1.4, metal),
|
||||||
|
_box(doc, "DINRail_RightReturnLip", length, 2.2, 2.0, x0, width / 2.0 - 2.2, 1.4, metal),
|
||||||
|
]
|
||||||
|
|
||||||
|
for center_x in (-60.0, 0.0, 60.0):
|
||||||
|
objects.append(_rounded_slot(doc, "DINRail_MountSlot_{0:g}".format(center_x), 12.0, 2.2, 0.35, center_x, 0.0, height - 1.95, dark))
|
||||||
|
|
||||||
|
doc.recompute()
|
||||||
|
fcstd = OUT_DIR / "qet_din_rail.FCStd"
|
||||||
|
step = OUT_DIR / "qet_din_rail.step"
|
||||||
|
doc.saveAs(str(fcstd))
|
||||||
|
_export_step(objects, step)
|
||||||
|
return {
|
||||||
|
"name": "din_rail",
|
||||||
|
"fcstd": str(fcstd),
|
||||||
|
"step": str(step),
|
||||||
|
"dimensions_mm": {"length": length, "width": width, "height": height},
|
||||||
|
"objects": [obj.Name for obj in objects],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _binding_properties_present(doc):
|
||||||
|
found = {}
|
||||||
|
for obj in doc.Objects:
|
||||||
|
props = [prop for prop in sorted(ENGINEERING_BINDING_PROPERTIES) if prop in obj.PropertiesList]
|
||||||
|
if props:
|
||||||
|
found[obj.Name] = props
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
assets = [_create_wire_duct(), _create_din_rail()]
|
||||||
|
report = {"assets": assets}
|
||||||
|
REPORT_PATH.write_text(json.dumps(report, indent=2), encoding="utf-8")
|
||||||
|
|
||||||
|
for asset in assets:
|
||||||
|
print("Generated {0} FCStd: {1}".format(asset["name"], asset["fcstd"]))
|
||||||
|
print("Generated {0} STEP: {1}".format(asset["name"], asset["step"]))
|
||||||
|
print("Generated report: {0}".format(REPORT_PATH))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"name": "wire_duct",
|
||||||
|
"fcstd": "D:\\LightWork3D\\data\\examples\\qet_cabinet_assets\\qet_wire_duct.FCStd",
|
||||||
|
"step": "D:\\LightWork3D\\data\\examples\\qet_cabinet_assets\\qet_wire_duct.step",
|
||||||
|
"dimensions_mm": {
|
||||||
|
"length": 200.0,
|
||||||
|
"width": 40.0,
|
||||||
|
"height": 40.0
|
||||||
|
},
|
||||||
|
"objects": [
|
||||||
|
"WireDuct_BasePlate",
|
||||||
|
"WireDuct_LeftWall",
|
||||||
|
"WireDuct_RightWall",
|
||||||
|
"WireDuct_LeftCombSlot_01",
|
||||||
|
"WireDuct_RightCombSlot_01",
|
||||||
|
"WireDuct_LeftCombSlot_02",
|
||||||
|
"WireDuct_RightCombSlot_02",
|
||||||
|
"WireDuct_LeftCombSlot_03",
|
||||||
|
"WireDuct_RightCombSlot_03",
|
||||||
|
"WireDuct_LeftCombSlot_04",
|
||||||
|
"WireDuct_RightCombSlot_04",
|
||||||
|
"WireDuct_LeftCombSlot_05",
|
||||||
|
"WireDuct_RightCombSlot_05",
|
||||||
|
"WireDuct_LeftCombSlot_06",
|
||||||
|
"WireDuct_RightCombSlot_06",
|
||||||
|
"WireDuct_LeftCombSlot_07",
|
||||||
|
"WireDuct_RightCombSlot_07",
|
||||||
|
"WireDuct_LeftCombSlot_08",
|
||||||
|
"WireDuct_RightCombSlot_08",
|
||||||
|
"WireDuct_LeftCombSlot_09",
|
||||||
|
"WireDuct_RightCombSlot_09",
|
||||||
|
"WireDuct_LeftCombSlot_10",
|
||||||
|
"WireDuct_RightCombSlot_10",
|
||||||
|
"WireDuct_LeftCombSlot_11",
|
||||||
|
"WireDuct_RightCombSlot_11",
|
||||||
|
"WireDuct_LeftCombSlot_12",
|
||||||
|
"WireDuct_RightCombSlot_12",
|
||||||
|
"WireDuct_LeftCombSlot_13",
|
||||||
|
"WireDuct_RightCombSlot_13",
|
||||||
|
"WireDuct_LeftCombSlot_14",
|
||||||
|
"WireDuct_RightCombSlot_14",
|
||||||
|
"WireDuct_LeftCombSlot_15",
|
||||||
|
"WireDuct_RightCombSlot_15",
|
||||||
|
"WireDuct_LeftCombSlot_16",
|
||||||
|
"WireDuct_RightCombSlot_16",
|
||||||
|
"WireDuct_LeftCombSlot_17",
|
||||||
|
"WireDuct_RightCombSlot_17",
|
||||||
|
"WireDuct_LeftCombSlot_18",
|
||||||
|
"WireDuct_RightCombSlot_18",
|
||||||
|
"WireDuct_MountHole__60",
|
||||||
|
"WireDuct_MountHole_0",
|
||||||
|
"WireDuct_MountHole_60"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "din_rail",
|
||||||
|
"fcstd": "D:\\LightWork3D\\data\\examples\\qet_cabinet_assets\\qet_din_rail.FCStd",
|
||||||
|
"step": "D:\\LightWork3D\\data\\examples\\qet_cabinet_assets\\qet_din_rail.step",
|
||||||
|
"dimensions_mm": {
|
||||||
|
"length": 200.0,
|
||||||
|
"width": 35.0,
|
||||||
|
"height": 7.5
|
||||||
|
},
|
||||||
|
"objects": [
|
||||||
|
"DINRail_CenterTop",
|
||||||
|
"DINRail_LeftWeb",
|
||||||
|
"DINRail_RightWeb",
|
||||||
|
"DINRail_LeftFlange",
|
||||||
|
"DINRail_RightFlange",
|
||||||
|
"DINRail_LeftReturnLip",
|
||||||
|
"DINRail_RightReturnLip",
|
||||||
|
"DINRail_MountSlot__60",
|
||||||
|
"DINRail_MountSlot_0",
|
||||||
|
"DINRail_MountSlot_60"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,100 @@
|
|||||||
|
# Wire Duct and DIN Rail Assets 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:** Generate reusable FreeCAD and STEP assets for one wire duct and one DIN 35mm rail.
|
||||||
|
|
||||||
|
**Architecture:** Add one focused FreeCAD Python generator under `data/examples/qet_cabinet_assets/`. The script bootstraps the registered Windows FreeCAD runtime, creates separate FreeCAD documents for the wire duct and DIN rail, saves `.FCStd`, exports `.step`, and writes a JSON report for verification.
|
||||||
|
|
||||||
|
**Tech Stack:** FreeCAD Python API, Part workbench primitives, registered QETDeps FreeCAD Python runtime, Markdown documentation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Generator and README
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `data/examples/qet_cabinet_assets/create_qet_cabinet_assets.py`
|
||||||
|
- Create: `data/examples/qet_cabinet_assets/README.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the generator**
|
||||||
|
|
||||||
|
Create `create_qet_cabinet_assets.py`. It must:
|
||||||
|
|
||||||
|
- Bootstrap Windows FreeCAD DLL paths from `QET_FREECAD_RUNTIME_JSON` or `%LOCALAPPDATA%\QETDeps\runtime.json`.
|
||||||
|
- Generate `qet_wire_duct.FCStd` and `qet_wire_duct.step`.
|
||||||
|
- Generate `qet_din_rail.FCStd` and `qet_din_rail.step`.
|
||||||
|
- Write `qet_cabinet_assets_report.json`.
|
||||||
|
- Use dimensions from the approved spec.
|
||||||
|
- Avoid creating terminal LCS objects or engineering binding properties.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Create the README**
|
||||||
|
|
||||||
|
Create `README.md` describing the two assets, dimensions, file roles, regeneration command, and the fact that neither model contains terminal semantics.
|
||||||
|
|
||||||
|
### Task 2: Generate and Verify
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Generate: `data/examples/qet_cabinet_assets/qet_wire_duct.FCStd`
|
||||||
|
- Generate: `data/examples/qet_cabinet_assets/qet_wire_duct.step`
|
||||||
|
- Generate: `data/examples/qet_cabinet_assets/qet_din_rail.FCStd`
|
||||||
|
- Generate: `data/examples/qet_cabinet_assets/qet_din_rail.step`
|
||||||
|
- Generate: `data/examples/qet_cabinet_assets/qet_cabinet_assets_report.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Run generator**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$runtime = Get-Content -LiteralPath 'C:\Users\ng123\AppData\Local\QETDeps\runtime.json' -Raw | ConvertFrom-Json
|
||||||
|
$env:QET_FREECAD_RUNTIME_JSON = 'C:\Users\ng123\AppData\Local\QETDeps\runtime.json'
|
||||||
|
& $runtime.freecad_python 'D:\LightWork3D\data\examples\qet_cabinet_assets\create_qet_cabinet_assets.py'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: command exits `0` and prints all generated output paths.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify files**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ChildItem -LiteralPath 'D:\LightWork3D\data\examples\qet_cabinet_assets' | Select-Object Name,Length
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: four model files and one report exist with non-zero sizes.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify FCStd documents and STEP headers**
|
||||||
|
|
||||||
|
Open both FCStd documents with the registered FreeCAD Python runtime and assert that objects exist, no object has `Role="Terminal"`, and no engineering binding properties exist. Read the first line of both STEP files and assert it is `ISO-10303-21;`.
|
||||||
|
|
||||||
|
### Task 3: Commit
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Add: `docs/superpowers/plans/2026-05-26-wire-duct-din-rail-assets-implementation.md`
|
||||||
|
- Add: `data/examples/qet_cabinet_assets/`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Stage intended files only**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git add -- docs/superpowers/plans/2026-05-26-wire-duct-din-rail-assets-implementation.md data/examples/qet_cabinet_assets
|
||||||
|
git diff --cached --name-only
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: only the plan and `qet_cabinet_assets` files are staged.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git commit -m "feat: add wire duct and din rail model assets"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: a commit containing only the generated assets, generator, report, README, and plan.
|
||||||
|
|
||||||
|
## Self-Review
|
||||||
|
|
||||||
|
- Spec coverage: implements both FCStd and STEP outputs for the line duct and DIN rail, report, README, and validation.
|
||||||
|
- Placeholder scan: no TBD/TODO/fill-later language is present.
|
||||||
|
- Type consistency: output file names match the approved spec.
|
||||||
Loading…
Reference in New Issue