Imported from o2mandoo/CAD-file-defeaturing-and-meshing-automation (
AGENTS.md). Install upstream withnpx skills add o2mandoo/CAD-file-defeaturing-and-meshing-automation. Copyright stays with the author.
AGENTS.md - Mek-lab Defeaturing-Only Model + Data Generator (v0, submit-today)
- Single Source of Truth
This file defines the goal, scope, rules, outputs, and the Definition of Done. Codex (and humans) must read this file first before making changes. If any instruction conflicts with chat history, AGENTS.md wins.
- Mission (What we ship today)
Build a Defeaturing-Only pipeline for CAD preprocessing.
Given a STEP file (.stp/.step), the system runs a headless FreeCAD pipeline to:
- import geometry
- compute geometry metrics (before)
- run defeaturing (automatic or manual) as a sequence of operations (attempt-based)
- compute metrics (after, if available)
- export artifacts for dataset creation
Priority: generating defeaturing data (operations + success/failure logs) is more important than achieving perfect defeaturing success on all models.
Defeaturing-Only model scope:
- If/when an LLM is used, it is limited to defeaturing decisions (rules/thresholds, candidate grouping). It does not cover full CAE workflows.
- LLM output must be strict JSON and pass schema validation.
- Scope and Non-goals
In scope (v0)
- Input format: STEP only
- Headless execution: FreeCADCmd / freecadcmd on macOS
- Modes:
- Automatic: rule-based candidate detection + removal attempts
- Manual: user-provided selected items (e.g., faces) + removal attempts
- Always save dataset artifacts (even when defeaturing fails)
- Optional (later): defeaturing-only LLM that suggests rules/targets
Out of scope (v0)
- Full CAE workflows (simulation, meshing, solver, FEM)
- Node-graph UI / workflow editor UI
- General CAE LLM tasks (log diagnosis, solver scripts, report generation)
- Perfect semantic feature recognition ("wing", "body", etc.)
- Environment
- Host OS: macOS (Apple Silicon)
- FreeCAD: installed as an app bundle
- freecadcmd path (current): /Applications/FreeCAD.app/Contents/Resources/bin/freecadcmd
- Known CLI pitfall:
- freecadcmd may treat extra CLI args as files to open.
- Use env vars for inputs whenever possible.
- Inputs (env vars)
- STEP_FILE: path to input .stp/.step
- OUT_DIR: artifacts output directory (artifacts/<job_id>)
- RULES_JSON: path to rules JSON (optional; defaults apply if missing; mesh_size_mm required)
- MODE: automatic | manual (optional; default: automatic)
- SELECTED_ITEMS_JSON: path to manual selections JSON (optional)
- LLM_MODEL: Ollama model name for defeaturing-only decisions (default: gpt-oss:20b)
- LLM_URL: Ollama server URL (default: http://localhost:11434)
- LLM_TIMEOUT_SEC: Ollama request timeout in seconds (default: 120)
- LLM_TEMPERATURE: Ollama temperature (default: 0)
- LLM_SEED: Ollama seed for deterministic outputs (default: 42)
- Defeaturing Strategy (v0)
Core philosophy: Automation through Logic, not Hard-coding.
- Do not rely on hard-coded face indices like Faces[0].
- If referencing faces, use FreeCAD subelement names (e.g., "Face12") derived from geometry heuristics.
v0 focuses on data generation:
- Detect candidates based on mesh_size_mm (holes/fillets/chamfers/slots/pockets)
- Attempt removal (best-effort)
- Record success/failure and parameters in operations.json
- Manual selections are grouped by connectivity + surface type.
- Automatic candidates are optionally grouped by LLM or by connectivity + surface type.
- LLM policy proposes thresholds_used and selected_groups; invalid JSON falls back to rules.
It is acceptable if removal succeeds only on a subset of STEP files today, as long as artifacts are consistently produced.
- Coding Rules (Vibe Coding guardrails)
6.1 No GUI in headless
- Never import FreeCADGui, FemGui, or any GUI-only module.
- Use only headless-friendly modules (FreeCAD, Part, Mesh if needed).
6.2 Robust inputs
- Prefer env vars: STEP_FILE, OUT_DIR, MODE, RULES_JSON, SELECTED_ITEMS_JSON
- If env vars are missing, allow CLI fallbacks only if safe.
6.3 Always write artifacts Even on exceptions:
- Create out_dir
- Write summary.json
- Write operations.json (at least with failure info)
- Write before_metrics.json
6.4 Small, testable modules
- Keep files small (~200 lines max)
- Use type hints
- Log clearly with job_id
- Repo Layout (recommended)
mek-lab-project/ AGENTS.md README.md scripts/ defeature_runner.py src/ defeature/ domain/ application/ infrastructure/ artifacts/ input_files/ Makefile
For submit-today, it is OK to implement only:
- scripts/defeature_runner.py
- Makefile
- minimal docs
- Commands (must work)
Local run (preferred): use env vars.
Makefile targets (required):
- make defeature -> runs one sample and creates artifacts
- make run -> alias for make defeature
- make defeature-one RUN_STEP=... -> convenience target for single file
- make mesh RUN_OUT=artifacts/<job_id> -> runs gmsh mesh benchmark (defeatured.step if present, else input.step)
- make report -> regenerate reports/runs.csv from artifacts
- make label RUN_OUT=artifacts/<job_id> -> ensure labels.json exists for the job
- make label-llm RUN_OUT=artifacts/<job_id> -> generate labels.json via Ollama (teacher labels)
- optional: RUN_LABEL_OUT=... to save per-model labels without overwrite
- make clean-artifacts -> optional Optional:
- make batch -> runs all STEP files under input_files (batch artifacts)
- make batch-llm -> runs batch with LLM enabled (defaults to gpt-oss:20b)
- make batch_defeature -> alias for make batch
- make defeature-batch -> alias for make batch
- make mesh-batch -> runs mesh benchmark across artifacts/batch_* jobs
- make eval_models RUN_STEP=... -> runs defeature+mesh for multiple LLMs
- Output Contract (Dataset Artifacts)
Every run creates a unique job folder: artifacts/<job_id>/
Must include:
- input.step (copied from source)
- operations.json (most important)
- before_metrics.json
- summary.json (status/error/traceback/runtime)
- defeatured.step (only if export succeeds)
- after_metrics.json (only if after-shape exists)
- defeature_script.py (auto-generated FreeCAD replay script)
- labels.json (human review template)
- mesh_metrics.json
- mesh_summary.json
- datasets/defeature_train.jsonl (append-only training data)
- reports/runs.csv (append-only run log)
Labeling workflow:
- Do not edit operations.json manually.
- Human review goes into labels.json only.
- LLM teacher labels can be generated with make label-llm.
- make report regenerates reports/runs.csv from artifacts + labels.json.
Optional:
- log.txt (extra logs)
- preview_before.png, preview_after.png (later; not required today)
- mesh.geo, mesh.msh (gmsh outputs)
9.1 operations.json schema (v0 minimal) { "job_id": "20260112_173000_abcd", "mode": "automatic|manual", "rules": { "mesh_size_mm": 3.0, "small_hole_max_diameter_mm": 3.0, "small_fillet_max_radius_mm": 1.0 }, "thresholds_used": { "bbox_diag_mm": 120.0, "mesh_size_recommended_mm": 2.5, "mesh_size_used_mm": 3.0, "mesh_size_source": "input|recommended", "remove_hole_max_diameter_mm": 6.0, "remove_fillet_max_radius_mm": 2.25, "remove_chamfer_max_width_mm": 2.25, "remove_slot_max_width_mm": 4.5, "remove_pocket_max_depth_mm": 4.5, "preserve_hole_min_diameter_mm": 12.0, "preserve_large_planar_faces_top_n": 10 }, "scale_class": "small|medium|large", "shape_summary": { "bbox": { "x_len_mm": 10.0, "y_len_mm": 20.0, "z_len_mm": 30.0, "diag_mm": 40.0 }, "face_count": 120, "edge_count": 240, "vertex_count": 180, "planar_face_ratio": 0.6, "cylindrical_face_ratio": 0.2, "hole_candidates_count": 4, "cylindrical_diameter_min_mm": 2.0, "cylindrical_diameter_max_mm": 10.0 }, "timing": { "defeature_elapsed_sec": 1.234, "llm_elapsed_sec": 0.321, "mesh_h_reco_mm": 2.5 }, "llm": { "used": true, "model": "gpt-oss:20b", "url": "http://localhost:11434", "latency_ms": 250, "status": "ok|timeout|invalid_json|error" }, "generated_script": "defeature_script.py", "selected_items": [ { "type": "face", "ref": "Face12", "note": "manual selection" } ], "selected_groups": [ { "group_id": "g1", "candidate_refs": ["Face12", "Face13"], "type": "hole_candidate", "reason": "small feature" } ], "candidate_groups": [ { "group_id": "candidate_group_1", "type": "hole_candidate", "refs": ["Face7", "Face8"], "rule": "llm_grouping|connected+surface_type" } ], "candidates": [ { "type": "hole_candidate|fillet_candidate|chamfer_candidate|slot_candidate|pocket_candidate", "refs": ["Face7", "Face8"], "estimated_size_mm": 3.0, "confidence": 0.7, "preserve_reason": "" } ], "attempts": [ { "op": "remove_hole_candidate", "group_id": "hole_group_1", "target_refs": ["Face7", "Face8"], "params": { "remove_hole_max_diameter_mm": 6.0 }, "status": "success|fail", "error": "..." } ], "result": { "status": "completed|failed", "notes": ["..."] } }
9.2 summary.json schema (v0 minimal) { "job_id": "20260112_173000_abcd", "step_file": "path/to/input.stp", "out_dir": "artifacts/<job_id>", "rules_json": "path/to/rules.json", "mode": "automatic|manual", "status": "completed|failed", "runtime_sec": 1.234, "defeature_time_ms": 1234, "DEFEATURE_TIME_MS": 1234, "started_at": "2026-01-14T10:00:00", "finished_at": "2026-01-14T10:00:01", "generated_script": "defeature_script.py", "timing": { "defeature_elapsed_sec": 1.234, "llm_elapsed_sec": 0.321, "llm_status": "used|skipped|failed" }, "notes": ["..."], "error": "optional", "traceback": "optional" }
9.3 before_metrics.json / after_metrics.json (v0 minimal) { "status": "completed|failed", "face_count": 123, "solid_count": 1, "volume_mm3": 123.456, "bbox_diag_mm": 456.789, "notes": ["..."] }
9.4 mesh_metrics.json (v0 minimal) { "status": "completed|failed", "error": "optional", "input_step": "input.step|defeatured.step", "mesh_time_sec": 0.42, "mesh_nodes": 12345, "mesh_elements": 67890, "h_used_mm": 2.5, "h_recommended_mm": 2.5, "h_source": "override|rules|recommended|default", "gmsh_version": "4.11.1", "mesh_geo": "mesh.geo", "mesh_msh": "mesh.msh", "started_at": "2026-01-14T10:00:00", "finished_at": "2026-01-14T10:00:01" }
9.5 mesh_summary.json (v0 minimal) { "status": "completed|failed", "error": "optional", "traceback": "optional", "started_at": "2026-01-14T10:00:00", "finished_at": "2026-01-14T10:00:01" }
- Definition of Done (DoD) - Submit Today
A submission is acceptable if:
-
make run completes (even with defeaturing failure)
-
It creates: artifacts/<job_id>/operations.json and summary.json
-
The output clearly records:
- detected candidates
- attempted operations
- success/failure and errors
-
README explains:
- goal (defeaturing data generation)
- how to run
- what artifacts are produced
- next steps (v1 rules, higher success rate, UI integration later)
-
How Codex should work (mandatory protocol)
For every task, Codex must respond with:
- Goal
- Files changed
- Exact run command(s)
- Expected artifacts
- Notes / risks
If a change risks breaking DoD, stop and fix it first.