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/benchmarks/*.gif
text-to-cad/models/ text-to-cad/models/
# DesignIR teacher data and runtime artifacts # Local distillation inputs and run artifacts. Only output/ is versioned.
designir-pipeline/workspace/teacher/inbox/* designir-pipeline/input/*
!designir-pipeline/workspace/teacher/inbox/.gitkeep !designir-pipeline/input/.gitkeep
designir-pipeline/workspace/teacher/evidence/* designir-pipeline/runs/
!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
+14 -20
View File
@@ -96,33 +96,27 @@ Schema 位于:
将待处理的 STEP/STP 文件放入: 将待处理的 STEP/STP 文件放入:
```text ```text
designir-pipeline/workspace/teacher/inbox/ designir-pipeline/input/
``` ```
完整运行目录: 蒸馏目录只保留三层:
| 阶段 | 路径 | | 路径 | 用途 | Git |
| --- | --- | | --- | --- | --- |
| 教师 STEP 输入 | `workspace/teacher/inbox/` | | `designir-pipeline/input/` | 原始教师 STEP/STP | 不提交 |
| 私有几何证据 | `workspace/teacher/evidence/` | | `designir-pipeline/runs/` | 证据、DesignIR、重建件、验收、草稿和失败记录 | 不提交 |
| Reconstruction Agent 输入 | `workspace/reconstruction/inbox/` | | `designir-pipeline/output/library.json` | 通过确定性校验的经验库 | 提交 |
| 参数化重建结果 | `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/` |
详细说明见 [`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 ```bash
designir-pipeline/scripts/cad-experience extract-folder \ designir-pipeline/scripts/cad-experience extract-folder
--input designir-pipeline/workspace/teacher/inbox \
--output designir-pipeline/workspace/teacher/evidence
``` ```
蒸馏得到的经验必须同时通过几何一致性、特征语义、参数可编辑性和修改稳定性验收。两个 Agent 都无权直接发布经验,最终晋升由确定性策略执行。 蒸馏得到的经验必须同时通过几何一致性、特征语义、参数可编辑性和修改稳定性验收。两个 Agent 都无权直接发布经验,最终晋升由确定性策略执行。
@@ -187,7 +181,7 @@ text-to-cad/.venv/bin/python -m unittest \
```bash ```bash
text-to-cad/.venv/bin/python \ text-to-cad/.venv/bin/python \
designir-pipeline/skills/cad-experience-builder/scripts/cad_experience.py \ 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 references, imported B-Reps, embedded meshes, topology references, and generator
source that calls STEP import APIs. source that calls STEP import APIs.
## Workspace ## Data layout
Runtime data is role-separated under `workspace/`: The operator-facing layout has only three directories:
For the exact input, process, acceptance, and output locations, see
[`workspace/README.md`](workspace/README.md).
```text ```text
workspace/ designir-pipeline/
teacher/inbox/ uploaded STEP/STP files input/ drop source STEP/STP files here
teacher/evidence/ private deterministic evidence runs/ all private process artifacts
reconstruction/inbox/ evidence packets for Agent A output/library.json verified experience consumed at runtime
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
``` ```
Only `.gitkeep` files are versioned in runtime directories. Teacher geometry, `input/` and `runs/` are ignored by Git. Only the deterministic result in
private evidence, generated parts, and reports stay local. `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 ## DesignIR 2.0
@@ -87,9 +89,16 @@ by deterministic code after several independent cases pass.
## Commands ## Commands
Use the CAD Python environment: Drop files into `input/`, then use the CAD Python environment:
```bash ```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 validate part.designir.json
../text-to-cad/.venv/bin/python scripts/designir_pipeline.py compile \ ../text-to-cad/.venv/bin/python scripts/designir_pipeline.py compile \
part.designir.json --output rebuilt.step part.designir.json --output rebuilt.step
+4 -5
View File
@@ -7,9 +7,9 @@ meaningfully editable.
## Allowed inputs ## Allowed inputs
- Teacher STEP from the protected teacher vault. - Teacher STEP from protected `input/`.
- Candidate DesignIR. - Candidate DesignIR from `runs/designir/`.
- Reconstructed STEP. - Reconstructed STEP from `runs/rebuilt/`.
- Deterministic compiler version. - Deterministic compiler version.
- Modification test contract. - Modification test contract.
@@ -36,10 +36,9 @@ Parametric behavior:
## Required output ## 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 feature-semantic, parameter-editability, and stability sections. Never collapse
the result into an unexplained similarity score. the result into an unexplained similarity score.
The agent may recommend pass, fail, or quarantine. Only the deterministic The agent may recommend pass, fail, or quarantine. Only the deterministic
publisher changes promotion state. publisher changes promotion state.
@@ -7,4 +7,7 @@ reconstruction_output_access: read
acceptance_report_access: write acceptance_report_access: write
publish_access: denied publish_access: denied
instructions: acceptance-agent.md 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 ## Allowed inputs
- One private evidence packet produced by the ingestion service. - One private evidence packet from `runs/evidence/`.
- The user modification request. - The user modification request.
- Promoted generalized experience. - Promoted generalized experience.
- The DesignIR schema and supported operation vocabulary. - The DesignIR schema and supported operation vocabulary.
@@ -24,7 +24,7 @@ geometry.
## Required output ## Required output
Write one `*.designir.json` containing: Write one `runs/designir/*.designir.json` containing:
- family and reconstruction status; - family and reconstruction status;
- coordinate system and datums; - 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 Request `isolated-rebuild`. Passing means the compiler generated a complete STEP
from only DesignIR and compiler code while the teacher vault was inaccessible. from only DesignIR and compiler code while the teacher vault was inaccessible.
The router may choose `build123d` or `simplecadapi`; the latter also produces a 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 acceptance_report_access: denied
publish_access: denied publish_access: denied
instructions: reconstruction-agent.md 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 ## Required workflow
1. Put source `.step` or `.stp` files in 1. Put source `.step` or `.stp` files directly in `input/`. Git ignores this
`workspace/teacher/inbox/`. Only deterministic ingestion and Agent B may directory; only deterministic ingestion and Agent B may access it.
access this vault. 2. Parse every new, content-unique STEP into private JSON under
2. Parse every new, content-unique STEP into visible private JSON under `runs/evidence/`:
`workspace/teacher/evidence/`:
```bash ```bash
../../scripts/cad-experience extract-folder ../../scripts/cad-experience extract-folder
@@ -26,11 +25,11 @@ published library and require repeated support before promoting any experience.
../../scripts/cad-experience prepare ../../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 distiller: compare families, parameter roles, datum roles, canonical
reconstruction stages, feature cardinality classes, relations, prior reconstruction stages, feature cardinality classes, relations, prior
vocabulary, failure rules, and validation targets; then author 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`. `references/llm-review-contract.md`.
Propose methods, invariants, and family-level `reconstruction_grammar` Propose methods, invariants, and family-level `reconstruction_grammar`
@@ -44,7 +43,7 @@ published library and require repeated support before promoting any experience.
```bash ```bash
../../scripts/cad-experience publish \ ../../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 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 ```bash
python scripts/cad_experience.py audit \ 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: 7. Query only the published library for creation, modification, or rebuilding:
```bash ```bash
python scripts/cad_experience.py query \ python scripts/cad_experience.py query \
../../workspace/promotion/promoted/library.json \ ../../output/library.json \
--family flanged_hub_adapter \ --family flanged_hub_adapter \
--features base_flange,hollow_sleeve,counterbore --features base_flange,hollow_sleeve,counterbore
``` ```
@@ -88,8 +87,10 @@ required.
family without publishing any teacher dimensions. family without publishing any teacher dimensions.
- Exact source paths, hashes, dimensions, coordinates, and face references are - Exact source paths, hashes, dimensions, coordinates, and face references are
permitted in private case JSON and forbidden in the published library. permitted in private case JSON and forbidden in the published library.
- Keep input/output as a human-visible staging area and induction corpus. Never - Keep `input/` and `runs/` as a local-only induction corpus. Never use them
use it directly during creation, modification, or reconstruction. 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, - A semantic candidate is not a trusted design method until repeated,
independently sourced cases satisfy the promotion policy. independently sourced cases satisfy the promotion policy.
@@ -2,8 +2,8 @@
Case JSON is private evidence used only during induction. It may contain exact Case JSON is private evidence used only during induction. It may contain exact
parameters, but the inducer never copies those fields into published experience. parameters, but the inducer never copies those fields into published experience.
Store source STEP files in `workspace/teacher/inbox` and generated private Store source STEP files in `input/` and generated private evidence in
evidence in `workspace/teacher/evidence`. `runs/evidence/`.
Do not pass either folder or a case JSON to cad-router or a CAD backend. 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 Creation, modification, and reconstruction may consume only the promoted public
@@ -1,10 +1,10 @@
# LLM semantic review contract # 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 Maintain stable proposal identifiers when an existing candidate expresses the
same method. same method.
Write `work/review/experience-draft.json` with this shape: Write `runs/review/experience-draft.json` with this shape:
```json ```json
{ {
@@ -618,17 +618,15 @@ def plugin_root() -> Path:
def default_library_path() -> Path: def default_library_path() -> Path:
return ( return plugin_root() / "output" / "library.json"
plugin_root()
/ "workspace"
/ "promotion"
/ "promoted"
/ "library.json"
)
def parser_root() -> Path: def input_root() -> Path:
return plugin_root() / "workspace" / "teacher" return plugin_root() / "input"
def runs_root() -> Path:
return plugin_root() / "runs"
def step_files(root: Path) -> list[Path]: 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: def default_review_dir() -> Path:
return plugin_root() / "workspace" / "promotion" / "candidates" return runs_root() / "review"
def prepare_semantic_batch( def prepare_semantic_batch(
@@ -1088,15 +1086,15 @@ def build_parser() -> argparse.ArgumentParser:
extract_folder_parser = subparsers.add_parser("extract-folder") extract_folder_parser = subparsers.add_parser("extract-folder")
extract_folder_parser.add_argument( extract_folder_parser.add_argument(
"--input", type=Path, default=parser_root() / "inbox" "--input", type=Path, default=input_root()
) )
extract_folder_parser.add_argument( 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 = subparsers.add_parser("prepare")
prepare_parser.add_argument( prepare_parser.add_argument(
"--input", type=Path, default=parser_root() / "evidence" "--input", type=Path, default=runs_root() / "evidence"
) )
prepare_parser.add_argument( prepare_parser.add_argument(
"--library", type=Path, default=default_library_path() "--library", type=Path, default=default_library_path()
@@ -1110,7 +1108,7 @@ def build_parser() -> argparse.ArgumentParser:
publish_parser = subparsers.add_parser("publish") publish_parser = subparsers.add_parser("publish")
publish_parser.add_argument("--draft", type=Path, required=True) publish_parser.add_argument("--draft", type=Path, required=True)
publish_parser.add_argument( publish_parser.add_argument(
"--input", type=Path, default=parser_root() / "evidence" "--input", type=Path, default=runs_root() / "evidence"
) )
publish_parser.add_argument( publish_parser.add_argument(
"--library", type=Path, default=default_library_path() "--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.", help="LLM-authored proposal draft. Without this, publishing is blocked.",
) )
distill_parser.add_argument( distill_parser.add_argument(
"--input", type=Path, default=parser_root() / "evidence" "--input", type=Path, default=runs_root() / "evidence"
) )
distill_parser.add_argument( distill_parser.add_argument(
"--library", type=Path, default=default_library_path() "--library", type=Path, default=default_library_path()
@@ -107,6 +107,15 @@ def make_case(index: int) -> dict:
class CadExperienceTest(unittest.TestCase): 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: def reviewed_proposal(self) -> dict:
return { return {
"id": "constraint.flanged_hub_adapter.shared_axis", "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": {}, "parameters": {},
"experience": { "experience": {
"library": "../../../designir-pipeline/workspace/promotion/promoted/library.json", "library": "../../../designir-pipeline/output/library.json",
"context_kind": "generalized_cad_experience_query", "context_kind": "generalized_cad_experience_query",
"methods_applied": [] "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 parameters, expressions, features, constraints, edit interface, and validation
contract. See `designir-2.0.md`. contract. See `designir-2.0.md`.
For an uploaded STEP, keep teacher geometry in the protected ingestion and For an uploaded STEP, keep teacher geometry in `designir-pipeline/input/` and
acceptance workspace. The reconstruction task contains private evidence, all private evidence, DesignIR candidates, rebuilds, and acceptance reports in
DesignIR, backend source, and rebuilt STEP but no teacher STEP or imported `designir-pipeline/runs/`. The reconstruction task receives evidence but no
B-Rep. teacher STEP or imported B-Rep.
## Backend adapter requirements ## 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() configured = os.environ.get("CAD_EXPERIENCE_LIBRARY", "").strip()
if configured: if configured:
return Path(configured).expanduser() return Path(configured).expanduser()
workspace_candidate = ( published_candidate = (
SKILL_ROOT.parents[2] SKILL_ROOT.parents[2]
/ "designir-pipeline" / "designir-pipeline"
/ "workspace" / "output"
/ "promotion"
/ "promoted"
/ "library.json" / "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( def classify_edit_context(
@@ -265,20 +263,18 @@ def classify_edit_context(
( (
SKILL_ROOT.parents[2] SKILL_ROOT.parents[2]
/ "designir-pipeline" / "designir-pipeline"
/ "workspace" / "input"
/ "teacher"
).resolve(), ).resolve(),
( (
SKILL_ROOT.parents[2] SKILL_ROOT.parents[2]
/ "designir-pipeline" / "designir-pipeline"
/ "workspace" / "runs"
/ "acceptance"
).resolve(), ).resolve(),
] ]
if any(source == root or root in source.parents for root in protected_roots): if any(source == root or root in source.parents for root in protected_roots):
raise ValueError( raise ValueError(
"cad-router reconstruction cannot read the teacher or acceptance " "cad-router reconstruction cannot read distillation input or run "
"vault; pass the ingestion job reference or an editable DesignIR" "artifacts; pass an editable DesignIR outside the private pipeline"
) )
suffix = source.suffix.lower() if source else "" suffix = source.suffix.lower() if source else ""
text = request.lower() text = request.lower()
@@ -363,12 +363,10 @@ class CadRouterTests(unittest.TestCase):
teacher_source = ( teacher_source = (
Path(__file__).resolve().parents[5] Path(__file__).resolve().parents[5]
/ "designir-pipeline" / "designir-pipeline"
/ "workspace" / "input"
/ "teacher"
/ "inbox"
/ "part.step" / "part.step"
) )
with self.assertRaisesRegex(ValueError, "cannot read the teacher"): with self.assertRaisesRegex(ValueError, "cannot read distillation input"):
route_module.route( route_module.route(
self.parse( 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__": if __name__ == "__main__":
unittest.main() unittest.main()