refactor: simplify distillation data layout

This commit is contained in:
Jerry
2026-07-27 17:40:16 +08:00
parent 8895178844
commit c8dbc51549
34 changed files with 132 additions and 7791 deletions
+4 -19
View File
@@ -29,22 +29,7 @@ text-to-cad/assets/
text-to-cad/benchmarks/*.gif
text-to-cad/models/
# DesignIR teacher data and runtime artifacts
designir-pipeline/workspace/teacher/inbox/*
!designir-pipeline/workspace/teacher/inbox/.gitkeep
designir-pipeline/workspace/teacher/evidence/*
!designir-pipeline/workspace/teacher/evidence/.gitkeep
designir-pipeline/workspace/reconstruction/inbox/*
!designir-pipeline/workspace/reconstruction/inbox/.gitkeep
designir-pipeline/workspace/reconstruction/designir/*
!designir-pipeline/workspace/reconstruction/designir/.gitkeep
designir-pipeline/workspace/reconstruction/output/*
!designir-pipeline/workspace/reconstruction/output/.gitkeep
designir-pipeline/workspace/acceptance/inbox/*
!designir-pipeline/workspace/acceptance/inbox/.gitkeep
designir-pipeline/workspace/acceptance/reports/*
!designir-pipeline/workspace/acceptance/reports/.gitkeep
designir-pipeline/workspace/promotion/candidates/*
!designir-pipeline/workspace/promotion/candidates/.gitkeep
designir-pipeline/workspace/promotion/replay_validated/*
!designir-pipeline/workspace/promotion/replay_validated/.gitkeep
# Local distillation inputs and run artifacts. Only output/ is versioned.
designir-pipeline/input/*
!designir-pipeline/input/.gitkeep
designir-pipeline/runs/
+14 -20
View File
@@ -96,33 +96,27 @@ Schema 位于:
将待处理的 STEP/STP 文件放入:
```text
designir-pipeline/workspace/teacher/inbox/
designir-pipeline/input/
```
完整运行目录
蒸馏目录只保留三层
| 阶段 | 路径 |
| --- | --- |
| 教师 STEP 输入 | `workspace/teacher/inbox/` |
| 私有几何证据 | `workspace/teacher/evidence/` |
| Reconstruction Agent 输入 | `workspace/reconstruction/inbox/` |
| 参数化重建结果 | `workspace/reconstruction/designir/` |
| 独立重建 STEP | `workspace/reconstruction/output/` |
| Acceptance Agent 输入 | `workspace/acceptance/inbox/` |
| 验收报告 | `workspace/acceptance/reports/` |
| 经验候选 | `workspace/promotion/candidates/` |
| 跨案例回放验证 | `workspace/promotion/replay_validated/` |
| 正式经验库 | `workspace/promotion/promoted/library.json` |
| 失败与能力缺口 | `workspace/quarantine/` |
| 路径 | 用途 | Git |
| --- | --- | --- |
| `designir-pipeline/input/` | 原始教师 STEP/STP | 不提交 |
| `designir-pipeline/runs/` | 证据、DesignIR、重建件、验收、草稿和失败记录 | 不提交 |
| `designir-pipeline/output/library.json` | 通过确定性校验的经验库 | 提交 |
详细说明见 [`designir-pipeline/workspace/README.md`](designir-pipeline/workspace/README.md)。
`runs/` 内部由程序按需创建 `evidence``designir``rebuilt`
`acceptance``review``quarantine`。目录层级只负责整理文件;两个
Agent 的读写边界由程序和合同控制。
详细说明见 [`designir-pipeline/README.md`](designir-pipeline/README.md)。
批量提取教师证据:
```bash
designir-pipeline/scripts/cad-experience extract-folder \
--input designir-pipeline/workspace/teacher/inbox \
--output designir-pipeline/workspace/teacher/evidence
designir-pipeline/scripts/cad-experience extract-folder
```
蒸馏得到的经验必须同时通过几何一致性、特征语义、参数可编辑性和修改稳定性验收。两个 Agent 都无权直接发布经验,最终晋升由确定性策略执行。
@@ -187,7 +181,7 @@ text-to-cad/.venv/bin/python -m unittest \
```bash
text-to-cad/.venv/bin/python \
designir-pipeline/skills/cad-experience-builder/scripts/cad_experience.py \
audit designir-pipeline/workspace/promotion/promoted/library.json
audit designir-pipeline/output/library.json
```
## 当前边界
+29 -20
View File
@@ -26,30 +26,32 @@ removed. `scripts/designir_pipeline.py anti-cheat` rejects source STEP
references, imported B-Reps, embedded meshes, topology references, and generator
source that calls STEP import APIs.
## Workspace
## Data layout
Runtime data is role-separated under `workspace/`:
For the exact input, process, acceptance, and output locations, see
[`workspace/README.md`](workspace/README.md).
The operator-facing layout has only three directories:
```text
workspace/
teacher/inbox/ uploaded STEP/STP files
teacher/evidence/ private deterministic evidence
reconstruction/inbox/ evidence packets for Agent A
reconstruction/designir/ authored DesignIR 2.0
reconstruction/output/ independently rebuilt STEP
acceptance/inbox/ manifests visible to Agent B
acceptance/reports/ geometry and editability reports
promotion/candidates/ untrusted generalized methods
promotion/replay_validated/
promotion/promoted/ deterministic published experience
quarantine/ unsupported vocabulary and failed cases
designir-pipeline/
input/ drop source STEP/STP files here
runs/ all private process artifacts
output/library.json verified experience consumed at runtime
```
Only `.gitkeep` files are versioned in runtime directories. Teacher geometry,
private evidence, generated parts, and reports stay local.
`input/` and `runs/` are ignored by Git. Only the deterministic result in
`output/` is versioned. The pipeline creates these private run directories as
needed:
| Local path | Purpose |
| --- | --- |
| `runs/evidence/` | Deterministic evidence extracted from source STEP. |
| `runs/designir/` | DesignIR authored by Reconstruction Agent. |
| `runs/rebuilt/` | STEP rebuilt without access to source STEP. |
| `runs/acceptance/` | Independent geometry and editability reports. |
| `runs/review/` | Sanitized semantic batches and untrusted drafts. |
| `runs/quarantine/` | Failures and unsupported feature vocabulary. |
The shallow directory structure is for usability, not authorization. Agent and
compiler access is enforced by their contracts and by anti-cheat checks.
## DesignIR 2.0
@@ -87,9 +89,16 @@ by deterministic code after several independent cases pass.
## Commands
Use the CAD Python environment:
Drop files into `input/`, then use the CAD Python environment:
```bash
scripts/cad-experience extract-folder
scripts/cad-experience prepare
# The semantic distiller writes runs/review/experience-draft.json.
scripts/cad-experience publish \
--draft runs/review/experience-draft.json
scripts/cad-experience audit output/library.json
../text-to-cad/.venv/bin/python scripts/designir_pipeline.py validate part.designir.json
../text-to-cad/.venv/bin/python scripts/designir_pipeline.py compile \
part.designir.json --output rebuilt.step
+4 -5
View File
@@ -7,9 +7,9 @@ meaningfully editable.
## Allowed inputs
- Teacher STEP from the protected teacher vault.
- Candidate DesignIR.
- Reconstructed STEP.
- Teacher STEP from protected `input/`.
- Candidate DesignIR from `runs/designir/`.
- Reconstructed STEP from `runs/rebuilt/`.
- Deterministic compiler version.
- Modification test contract.
@@ -36,10 +36,9 @@ Parametric behavior:
## Required output
Write one machine-readable acceptance report with separate geometry,
Write one machine-readable report under `runs/acceptance/` with separate geometry,
feature-semantic, parameter-editability, and stability sections. Never collapse
the result into an unexplained similarity score.
The agent may recommend pass, fail, or quarantine. Only the deterministic
publisher changes promotion state.
@@ -7,4 +7,7 @@ reconstruction_output_access: read
acceptance_report_access: write
publish_access: denied
instructions: acceptance-agent.md
teacher_input_path: input
designir_input_path: runs/designir
rebuild_input_path: runs/rebuilt
report_output_path: runs/acceptance
@@ -8,7 +8,7 @@ geometry.
## Allowed inputs
- One private evidence packet produced by the ingestion service.
- One private evidence packet from `runs/evidence/`.
- The user modification request.
- Promoted generalized experience.
- The DesignIR schema and supported operation vocabulary.
@@ -24,7 +24,7 @@ geometry.
## Required output
Write one `*.designir.json` containing:
Write one `runs/designir/*.designir.json` containing:
- family and reconstruction status;
- coordinate system and datums;
@@ -46,4 +46,5 @@ If the vocabulary is insufficient, emit
Request `isolated-rebuild`. Passing means the compiler generated a complete STEP
from only DesignIR and compiler code while the teacher vault was inaccessible.
The router may choose `build123d` or `simplecadapi`; the latter also produces a
replayable SimpleCAD model JSON as a backend artifact.
replayable SimpleCAD model JSON as a backend artifact. Put rebuild artifacts in
`runs/rebuilt/`.
@@ -8,3 +8,6 @@ reconstruction_output_access: request_only
acceptance_report_access: denied
publish_access: denied
instructions: reconstruction-agent.md
input_path: runs/evidence
designir_output_path: runs/designir
rebuild_output_path: runs/rebuilt
@@ -1,10 +0,0 @@
# STEP reconstruction benchmark
Each case stores only a protected teacher STEP in Git LFS. DesignIR,
reconstructed STEP, acceptance reports, and perturbation artifacts are produced
at runtime outside the case directory.
These two teachers were migrated from the retired Exact Base fixtures. Their old
copied-B-Rep specifications and generated outputs were intentionally removed.
They are now held-out truth for independent DesignIR reconstruction.
@@ -1,6 +0,0 @@
{
"schema_version": "1.0",
"case_id": "015133",
"teacher": "teacher.step",
"purpose": "held_out_designir_reconstruction"
}
File diff suppressed because it is too large Load Diff
@@ -1,6 +0,0 @@
{
"schema_version": "1.0",
"case_id": "015240",
"teacher": "teacher.step",
"purpose": "held_out_designir_reconstruction"
}
File diff suppressed because it is too large Load Diff
@@ -10,11 +10,10 @@ published library and require repeated support before promoting any experience.
## Required workflow
1. Put source `.step` or `.stp` files in
`workspace/teacher/inbox/`. Only deterministic ingestion and Agent B may
access this vault.
2. Parse every new, content-unique STEP into visible private JSON under
`workspace/teacher/evidence/`:
1. Put source `.step` or `.stp` files directly in `input/`. Git ignores this
directory; only deterministic ingestion and Agent B may access it.
2. Parse every new, content-unique STEP into private JSON under
`runs/evidence/`:
```bash
../../scripts/cad-experience extract-folder
@@ -26,11 +25,11 @@ published library and require repeated support before promoting any experience.
../../scripts/cad-experience prepare
```
4. Read `workspace/promotion/candidates/semantic-batch.json` completely. Act as the semantic
4. Read `runs/review/semantic-batch.json` completely. Act as the semantic
distiller: compare families, parameter roles, datum roles, canonical
reconstruction stages, feature cardinality classes, relations, prior
vocabulary, failure rules, and validation targets; then author
`workspace/promotion/candidates/experience-draft.json` using
`runs/review/experience-draft.json` using
`references/llm-review-contract.md`.
Propose methods, invariants, and family-level `reconstruction_grammar`
@@ -44,7 +43,7 @@ published library and require repeated support before promoting any experience.
```bash
../../scripts/cad-experience publish \
--draft ../../workspace/promotion/candidates/experience-draft.json
--draft ../../runs/review/experience-draft.json
```
The publisher recomputes support from the private corpus, rejects numeric
@@ -55,14 +54,14 @@ published library and require repeated support before promoting any experience.
```bash
python scripts/cad_experience.py audit \
../../workspace/promotion/promoted/library.json
../../output/library.json
```
7. Query only the published library for creation, modification, or rebuilding:
```bash
python scripts/cad_experience.py query \
../../workspace/promotion/promoted/library.json \
../../output/library.json \
--family flanged_hub_adapter \
--features base_flange,hollow_sleeve,counterbore
```
@@ -88,8 +87,10 @@ required.
family without publishing any teacher dimensions.
- Exact source paths, hashes, dimensions, coordinates, and face references are
permitted in private case JSON and forbidden in the published library.
- Keep input/output as a human-visible staging area and induction corpus. Never
use it directly during creation, modification, or reconstruction.
- Keep `input/` and `runs/` as a local-only induction corpus. Never use them
directly during ordinary creation, modification, or reconstruction.
- Commit only the verified runtime library in `output/`; never commit source
STEP files, extracted evidence, review drafts, reports, or rebuilt parts.
- A semantic candidate is not a trusted design method until repeated,
independently sourced cases satisfy the promotion policy.
@@ -2,8 +2,8 @@
Case JSON is private evidence used only during induction. It may contain exact
parameters, but the inducer never copies those fields into published experience.
Store source STEP files in `workspace/teacher/inbox` and generated private
evidence in `workspace/teacher/evidence`.
Store source STEP files in `input/` and generated private evidence in
`runs/evidence/`.
Do not pass either folder or a case JSON to cad-router or a CAD backend.
Creation, modification, and reconstruction may consume only the promoted public
@@ -1,10 +1,10 @@
# LLM semantic review contract
Read the sanitized `work/review/semantic-batch.json`, not raw STEP geometry.
Read the sanitized `runs/review/semantic-batch.json`, not raw STEP geometry.
Maintain stable proposal identifiers when an existing candidate expresses the
same method.
Write `work/review/experience-draft.json` with this shape:
Write `runs/review/experience-draft.json` with this shape:
```json
{
@@ -618,17 +618,15 @@ def plugin_root() -> Path:
def default_library_path() -> Path:
return (
plugin_root()
/ "workspace"
/ "promotion"
/ "promoted"
/ "library.json"
)
return plugin_root() / "output" / "library.json"
def parser_root() -> Path:
return plugin_root() / "workspace" / "teacher"
def input_root() -> Path:
return plugin_root() / "input"
def runs_root() -> Path:
return plugin_root() / "runs"
def step_files(root: Path) -> list[Path]:
@@ -684,7 +682,7 @@ def extract_folder(input_dir: Path, output_dir: Path) -> dict[str, Any]:
def default_review_dir() -> Path:
return plugin_root() / "workspace" / "promotion" / "candidates"
return runs_root() / "review"
def prepare_semantic_batch(
@@ -1088,15 +1086,15 @@ def build_parser() -> argparse.ArgumentParser:
extract_folder_parser = subparsers.add_parser("extract-folder")
extract_folder_parser.add_argument(
"--input", type=Path, default=parser_root() / "inbox"
"--input", type=Path, default=input_root()
)
extract_folder_parser.add_argument(
"--output", type=Path, default=parser_root() / "evidence"
"--output", type=Path, default=runs_root() / "evidence"
)
prepare_parser = subparsers.add_parser("prepare")
prepare_parser.add_argument(
"--input", type=Path, default=parser_root() / "evidence"
"--input", type=Path, default=runs_root() / "evidence"
)
prepare_parser.add_argument(
"--library", type=Path, default=default_library_path()
@@ -1110,7 +1108,7 @@ def build_parser() -> argparse.ArgumentParser:
publish_parser = subparsers.add_parser("publish")
publish_parser.add_argument("--draft", type=Path, required=True)
publish_parser.add_argument(
"--input", type=Path, default=parser_root() / "evidence"
"--input", type=Path, default=runs_root() / "evidence"
)
publish_parser.add_argument(
"--library", type=Path, default=default_library_path()
@@ -1125,7 +1123,7 @@ def build_parser() -> argparse.ArgumentParser:
help="LLM-authored proposal draft. Without this, publishing is blocked.",
)
distill_parser.add_argument(
"--input", type=Path, default=parser_root() / "evidence"
"--input", type=Path, default=runs_root() / "evidence"
)
distill_parser.add_argument(
"--library", type=Path, default=default_library_path()
@@ -107,6 +107,15 @@ def make_case(index: int) -> dict:
class CadExperienceTest(unittest.TestCase):
def test_default_data_paths_use_shallow_local_layout(self):
pipeline_root = Path(__file__).parents[1].resolve()
self.assertEqual(pipeline_root / "input", MODULE.input_root())
self.assertEqual(pipeline_root / "runs", MODULE.runs_root())
self.assertEqual(
pipeline_root / "output" / "library.json",
MODULE.default_library_path(),
)
def reviewed_proposal(self) -> dict:
return {
"id": "constraint.flanged_hub_adapter.shared_axis",
-53
View File
@@ -1,53 +0,0 @@
# Distillation Workspace
This directory is the runtime boundary for uploaded STEP reconstruction and
experience distillation.
## Where to put target STEP files
Place STEP or STP files to be reconstructed in:
```text
teacher/inbox/
```
Use unique, stable filenames. Batch inputs are intentionally ignored by Git so
large private corpora do not inflate the repository.
## Where each stage is stored
| Stage | Directory | Contents |
| --- | --- | --- |
| Source input | `teacher/inbox/` | Original target STEP/STP files. |
| Extracted evidence | `teacher/evidence/` | Private geometry facts extracted from each source. |
| Agent A input | `reconstruction/inbox/` | Sanitized evidence packets and reconstruction requests. |
| Agent A result | `reconstruction/designir/` | Executable `*.designir.json` models. |
| Independent rebuild | `reconstruction/output/` | STEP rebuilt without teacher-file access; SimpleCAD jobs may also emit model JSON. |
| Agent B input | `acceptance/inbox/` | Acceptance manifests linking teacher, DesignIR, rebuild, and edit contract. |
| Acceptance result | `acceptance/reports/` | Geometry, topology, semantic, and parameter-perturbation reports. |
| Failed/unsupported | `quarantine/` | Unsupported vocabulary, invalid cases, and failure clusters. |
| Experience candidates | `promotion/candidates/` | Generalized methods proposed from validated cases. |
| Replay gate | `promotion/replay_validated/` | Methods repeated successfully across independent cases. |
| Final distilled result | `promotion/promoted/library.json` | Only deterministic, promoted experience consumed by CAD Router. |
## Data flow
```text
teacher/inbox/*.step
-> teacher/evidence/
-> reconstruction/inbox/
-> reconstruction/designir/
-> reconstruction/output/*.step
-> acceptance/inbox/
-> acceptance/reports/
-> promotion/candidates/
-> promotion/replay_validated/
-> promotion/promoted/library.json
```
Agent A cannot read `teacher/inbox/`. Agent B may read the teacher STEP and the
rebuilt result, but neither agent can directly publish experience. Promotion is
performed only by the deterministic policy gate.
The `.gitkeep` files preserve this layout in Git. Runtime STEP files, evidence,
rebuilds, and reports remain local by default.
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -1,494 +0,0 @@
{
"schema_version": "2.0",
"library_kind": "generalized_cad_experience",
"generated_at": "2026-07-23T08:16:40+00:00",
"induction_mode": "llm_proposals_with_deterministic_evidence_verification",
"status": "ready",
"policy": {
"instance_parameters_allowed": false,
"absolute_coordinates_allowed": false,
"single_case_promotion_allowed": false,
"llm_semantic_review_required": true,
"draft_evidence_verified": true,
"router_consumable": true,
"minimum_support": 20,
"minimum_confidence": 0.8
},
"corpus_summary": {
"accepted_case_count": 2,
"duplicate_case_count": 0,
"rejected_case_count": 0,
"family_count": 11
},
"experience_summary": {
"llm_proposal_count": 4,
"promoted_experience_count": 0,
"candidate_experience_count": 4,
"by_kind": [],
"by_scope": [],
"candidate_by_kind": [
{
"kind": "constraint",
"count": 1
},
{
"kind": "feature_motif",
"count": 2
},
{
"kind": "validation_rule",
"count": 1
}
]
},
"experiences": [
{
"id": "constraint.rotational_part.shared_dominant_axis",
"kind": "constraint",
"scope": [
"rotational_part"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"rotational_body"
],
"relations": [
"dominant_axis_alignment"
]
},
"guidance": "Preserve the dominant axis as the organizing datum for rotational feature roles.",
"semantic_rationale": "The datum transfers because axis alignment is independent of the individual component dimensions.",
"support": 2,
"confidence": 1.0,
"check": "Confirm that rotational feature roles remain aligned to the same organizing datum.",
"promotion_state": "candidate",
"required_support": 20,
"remaining_support": 18,
"required_confidence": 0.8,
"consumer_policy": "visible_for_review_but_not_available_to_cad_router"
},
{
"id": "motif.rotational_part.coaxial_core_with_center_passage",
"kind": "feature_motif",
"scope": [
"rotational_part"
],
"when": {
"features": [
"circular_equal_radius_pattern"
],
"relations": [
"shared_passage_axis"
]
},
"guidance": "Treat the rotational body, coaxial cylindrical stack, and central passage as one shared-axis core.",
"semantic_rationale": "The relationship transfers because the participating roles describe axis organization rather than a particular size.",
"support": 2,
"confidence": 1.0,
"check": "Confirm that the rotational body, stacked cylindrical roles, and central passage retain their intended common axis.",
"promotion_state": "candidate",
"required_support": 20,
"remaining_support": 18,
"required_confidence": 0.8,
"consumer_policy": "visible_for_review_but_not_available_to_cad_router"
},
{
"id": "motif.rotational_part.patterned_axial_mounting",
"kind": "feature_motif",
"scope": [
"rotational_part"
],
"when": {
"features": [
"repeated_axial_hole_pattern",
"rotational_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 55,
"confidence": 1.0
},
{
"id": "motif.hollow_or_stepped_shaft.coaxial_cylindrical_stack+stepped_cylindrical_passage",
"kind": "feature_motif",
"scope": [
"hollow_or_stepped_shaft"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"stepped_cylindrical_passage"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 46,
"confidence": 1.0
},
{
"id": "motif.hollow_or_stepped_shaft.rotational_body+coaxial_cylindrical_stack",
"kind": "feature_motif",
"scope": [
"hollow_or_stepped_shaft"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"rotational_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 46,
"confidence": 1.0
},
{
"id": "motif.hollow_or_stepped_shaft.shaft_like_body+elongated_body",
"kind": "feature_motif",
"scope": [
"hollow_or_stepped_shaft"
],
"when": {
"features": [
"elongated_body",
"shaft_like_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 46,
"confidence": 1.0
},
{
"id": "motif.multi_axis_manifold.cylindrical_feature_network+multi_axis_passage",
"kind": "feature_motif",
"scope": [
"multi_axis_manifold"
],
"when": {
"features": [
"cylindrical_feature_network",
"multi_axis_passage"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 55,
"confidence": 1.0
},
{
"id": "motif.multi_axis_manifold.planar_dominant_body+multi_level_planar_profile",
"kind": "feature_motif",
"scope": [
"multi_axis_manifold"
],
"when": {
"features": [
"multi_level_planar_profile",
"planar_dominant_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 44,
"confidence": 1.0
},
{
"id": "motif.patterned_plate.cylindrical_feature_network+multi_axis_passage",
"kind": "feature_motif",
"scope": [
"patterned_plate"
],
"when": {
"features": [
"cylindrical_feature_network",
"multi_axis_passage"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 76,
"confidence": 1.0
},
{
"id": "motif.patterned_plate.planar_dominant_body+multi_level_planar_profile",
"kind": "feature_motif",
"scope": [
"patterned_plate"
],
"when": {
"features": [
"multi_level_planar_profile",
"planar_dominant_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 202,
"confidence": 1.0
},
{
"id": "motif.patterned_plate.repeated_axial_hole_pattern+circular_equal_radius_pattern",
"kind": "feature_motif",
"scope": [
"patterned_plate"
],
"when": {
"features": [
"circular_equal_radius_pattern",
"repeated_axial_hole_pattern"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 66,
"confidence": 1.0
},
{
"id": "motif.patterned_prismatic_part.cylindrical_feature_network+multi_axis_passage",
"kind": "feature_motif",
"scope": [
"patterned_prismatic_part"
],
"when": {
"features": [
"cylindrical_feature_network",
"multi_axis_passage"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 156,
"confidence": 1.0
},
{
"id": "motif.patterned_prismatic_part.planar_dominant_body+multi_level_planar_profile",
"kind": "feature_motif",
"scope": [
"patterned_prismatic_part"
],
"when": {
"features": [
"multi_level_planar_profile",
"planar_dominant_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 323,
"confidence": 1.0
},
{
"id": "motif.patterned_prismatic_part.repeated_axial_hole_pattern+circular_equal_radius_pattern",
"kind": "feature_motif",
"scope": [
"patterned_prismatic_part"
],
"when": {
"features": [
"circular_equal_radius_pattern",
"repeated_axial_hole_pattern"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 159,
"confidence": 1.0
},
{
"id": "motif.plate_or_bracket.planar_dominant_body+multi_level_planar_profile",
"kind": "feature_motif",
"scope": [
"plate_or_bracket"
],
"when": {
"features": [
"multi_level_planar_profile",
"planar_dominant_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 35,
"confidence": 1.0
},
{
"id": "motif.prismatic_block.planar_dominant_body+multi_level_planar_profile",
"kind": "feature_motif",
"scope": [
"prismatic_block"
],
"when": {
"features": [
"multi_level_planar_profile",
"planar_dominant_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 67,
"confidence": 1.0
},
{
"id": "motif.rotational_part.coaxial_cylindrical_stack+stepped_cylindrical_passage",
"kind": "feature_motif",
"scope": [
"rotational_part"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"stepped_cylindrical_passage"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 123,
"confidence": 1.0
},
{
"id": "motif.rotational_part.rotational_body+coaxial_cylindrical_stack",
"kind": "feature_motif",
"scope": [
"rotational_part"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"rotational_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 123,
"confidence": 1.0
},
{
"id": "motif.shaft.coaxial_cylindrical_stack+stepped_cylindrical_passage",
"kind": "feature_motif",
"scope": [
"shaft"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"stepped_cylindrical_passage"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 40,
"confidence": 1.0
},
{
"id": "motif.shaft.rotational_body+coaxial_cylindrical_stack",
"kind": "feature_motif",
"scope": [
"shaft"
],
"when": {
"features": [
"coaxial_cylindrical_stack",
"rotational_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 40,
"confidence": 1.0
},
{
"id": "motif.shaft.shaft_like_body+elongated_body",
"kind": "feature_motif",
"scope": [
"shaft"
],
"when": {
"features": [
"elongated_body",
"shaft_like_body"
],
"relations": []
},
"guidance": "Treat the participating feature roles as one coordinated composition rather than unrelated primitives.",
"semantic_rationale": "The composition recurs across independent dimensional variants in the same family.",
"support": 40,
"confidence": 1.0
},
{
"id": "reconstruction.flanged_rotational_part.primary",
"kind": "reconstruction_grammar",
"scope": [
"flanged_rotational_part"
],
"when": {
"features": [
"circular_equal_radius_pattern",
"coaxial_cylindrical_stack",
"cylindrical_feature_network",
"multi_level_planar_profile",
"repeated_axial_hole_pattern",
"rotational_body",
"stepped_cylindrical_passage"
],
"relations": [
"repeated_radius_group"
]
},
"guidance": "Represent repeated axial holes as a symmetric mounting pattern tied to the rotational body.",
"semantic_rationale": "The motif remains useful across variants because it captures repeated functional placement around a rotational form.",
"support": 2,
"confidence": 1.0,
"check": "Confirm that repeated axial holes remain grouped consistently around the rotational body.",
"promotion_state": "candidate",
"required_support": 20,
"remaining_support": 18,
"required_confidence": 0.8,
"consumer_policy": "visible_for_review_but_not_available_to_cad_router"
},
{
"id": "validation.rotational_part.axis_and_pattern_consistency",
"kind": "validation_rule",
"scope": [
"rotational_part"
],
"when": {
"features": [
"central_passage",
"repeated_axial_hole_pattern"
],
"relations": []
},
"guidance": "Validate both the shared rotational axis and the grouped repeated-hole relationship before handoff.",
"semantic_rationale": "These checks protect transferable intent without relying on instance geometry.",
"support": 2,
"confidence": 1.0,
"check": "Confirm shared-axis alignment and repeated-hole grouping before treating the model as ready.",
"promotion_state": "candidate",
"required_support": 20,
"remaining_support": 18,
"required_confidence": 0.8,
"consumer_policy": "visible_for_review_but_not_available_to_cad_router"
}
]
}
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -1 +0,0 @@
@@ -58,7 +58,7 @@ Write `cad-task.json` beside the task artifacts with this minimum shape:
],
"parameters": {},
"experience": {
"library": "../../../designir-pipeline/workspace/promotion/promoted/library.json",
"library": "../../../designir-pipeline/output/library.json",
"context_kind": "generalized_cad_experience_query",
"methods_applied": []
},
@@ -74,10 +74,10 @@ have a sibling `*.designir.json` that owns its coordinate system, datums,
parameters, expressions, features, constraints, edit interface, and validation
contract. See `designir-2.0.md`.
For an uploaded STEP, keep teacher geometry in the protected ingestion and
acceptance workspace. The reconstruction task contains private evidence,
DesignIR, backend source, and rebuilt STEP but no teacher STEP or imported
B-Rep.
For an uploaded STEP, keep teacher geometry in `designir-pipeline/input/` and
all private evidence, DesignIR candidates, rebuilds, and acceptance reports in
`designir-pipeline/runs/`. The reconstruction task receives evidence but no
teacher STEP or imported B-Rep.
## Backend adapter requirements
+7 -11
View File
@@ -241,15 +241,13 @@ def default_experience_library() -> Path | None:
configured = os.environ.get("CAD_EXPERIENCE_LIBRARY", "").strip()
if configured:
return Path(configured).expanduser()
workspace_candidate = (
published_candidate = (
SKILL_ROOT.parents[2]
/ "designir-pipeline"
/ "workspace"
/ "promotion"
/ "promoted"
/ "output"
/ "library.json"
)
return workspace_candidate if workspace_candidate.is_file() else None
return published_candidate if published_candidate.is_file() else None
def classify_edit_context(
@@ -265,20 +263,18 @@ def classify_edit_context(
(
SKILL_ROOT.parents[2]
/ "designir-pipeline"
/ "workspace"
/ "teacher"
/ "input"
).resolve(),
(
SKILL_ROOT.parents[2]
/ "designir-pipeline"
/ "workspace"
/ "acceptance"
/ "runs"
).resolve(),
]
if any(source == root or root in source.parents for root in protected_roots):
raise ValueError(
"cad-router reconstruction cannot read the teacher or acceptance "
"vault; pass the ingestion job reference or an editable DesignIR"
"cad-router reconstruction cannot read distillation input or run "
"artifacts; pass an editable DesignIR outside the private pipeline"
)
suffix = source.suffix.lower() if source else ""
text = request.lower()
@@ -363,12 +363,10 @@ class CadRouterTests(unittest.TestCase):
teacher_source = (
Path(__file__).resolve().parents[5]
/ "designir-pipeline"
/ "workspace"
/ "teacher"
/ "inbox"
/ "input"
/ "part.step"
)
with self.assertRaisesRegex(ValueError, "cannot read the teacher"):
with self.assertRaisesRegex(ValueError, "cannot read distillation input"):
route_module.route(
self.parse(
"修改这个零件",
@@ -377,6 +375,23 @@ class CadRouterTests(unittest.TestCase):
)
)
def test_private_run_artifact_is_rejected_as_edit_source(self) -> None:
run_source = (
Path(__file__).resolve().parents[5]
/ "designir-pipeline"
/ "runs"
/ "designir"
/ "part.designir.json"
)
with self.assertRaisesRegex(ValueError, "cannot read distillation input"):
route_module.route(
self.parse(
"修改这个零件",
"--edit-source",
str(run_source),
)
)
if __name__ == "__main__":
unittest.main()