diff --git a/backend/agent/skills/cad-authoring/SKILL.md b/backend/agent/skills/cad-authoring/SKILL.md index b777186d..27d2a480 100644 --- a/backend/agent/skills/cad-authoring/SKILL.md +++ b/backend/agent/skills/cad-authoring/SKILL.md @@ -127,6 +127,62 @@ For `max_z`/`min_z` placement intent, use an owner role such as Use only supplied operations and their exact parameter schemas. Do not invent parameters, implicit booleans, or substitutes after a capability error. +## Semantic Annotation (meta and intent) + +Every document carries semantic annotations for downstream training. They are +purely descriptive: the compiler carries them through verbatim, geometry never +depends on them, and they are never acceptance targets. + +Document level — include `meta` with at least one of: + +```json +{ + "meta": { + "description": "R8 rounded square bushing, 100×100×12, central Ø45 bore", + "function": "Spacer sleeve over a Ø45 shaft; rounded corners for handling" + } +} +``` + +- `description`: one sentence naming the part with its key specifications + (≤60 characters). `function`: what the part does and where it fits + (≤200 characters). Narratives are written in the request language; keys and + controlled labels are English `snake_case`. + +Feature level — attach `intent` to every feature: + +```json +{ + "intent": { + "label": "shaft_passage", + "summary": "Ø45 central through bore, concentric with the outer contour", + "why": "The fitting face of the sleeve; diameter follows the mating shaft", + "provenance": "authored" + } +} +``` + +- `label`: one controlled vocabulary term in `snake_case` — for example + `housing_blank`, `shaft_passage`, `fastener_hole`, `bolt_circle`, + `tap_hole`, `counterbore_seat`, `countersink_seat`, `locating_pin_hole`, + `bearing_seat`, `press_fit_boss`, `mounting_boss`, `mounting_foot`, + `lifting_eye`, `slot_adjustment`, `coolant_channel`, `lubrication_gallery`, + `vent_hole`, `drain_port`, `fluid_inlet`, `process_corner_relief`, + `weld_prep`, `machining_setup_tab`, `inspection_access`, + `stress_relief_fillet`, `stiffening_rib`, `weight_relief`, + `mass_saving_pocket`, `load_path_flange`, `wall_thickness_transition`, + `gear_teeth`, `rack_teeth`, `thread_drive`, `cam_track`, `bend_wing`, + `cosmetic_surface`, `datum_plane_feature`, `datum_axis_feature`. The list + is open: an accurate new `snake_case` term is valid, but prefer vocabulary. +- `summary` (required, ≤80 characters): what it is plus the key parameters. + Use parameterized wording (`M8`, `Ø75`, `R8`), never a restatement of the + request prose. +- `why` (optional, ≤400 characters): the functional reason this feature exists. +- `provenance`: always `"authored"` when the model writes it. +- Do not invent fields inside `intent`, and never treat a mismatch between an + annotation and geometry as acceptable — the label must match the feature + actually constructed. + ## Sketches And Coordinates Keep a sketch to exactly `workplane` and `profile`. The workplane declares its @@ -228,6 +284,9 @@ Before returning the document, check every feature against these invariants: 5. Every requested removal intersects its intended material, every repeated feature has a valid seed reference, and every connected addition uses an operation whose contract explicitly supports the chosen result mode. +6. Every feature carries an `intent` with a truthful `label`, a parameterized + `summary` of at most 80 characters, and `provenance: "authored"`; the + document carries `meta` with at least one of `description`/`function`. If any invariant is false, revise the construction before emitting the single complete `cad.author.v1` document. diff --git a/backend/app/cad_agent/application/authoring_compiler.py b/backend/app/cad_agent/application/authoring_compiler.py index 6b2c36a5..c78c15df 100644 --- a/backend/app/cad_agent/application/authoring_compiler.py +++ b/backend/app/cad_agent/application/authoring_compiler.py @@ -106,10 +106,17 @@ class AuthoringCompiler: sketch_id = f"sketch_{source_positions[feature.name]:03d}" sketches.append({"id": sketch_id, **self._runtime_sketch(feature.sketch.model_dump(mode="json"))}) output["sketch_id"] = sketch_id + if feature.intent is not None: + # Semantic annotation only: carried through for training data, + # never consumed by runtime geometry or validation semantics. + output["intent"] = feature.intent.model_dump(mode="json", exclude_none=True) features.append(output) + document_meta: dict[str, Any] = {"unit": "mm"} + if doc.meta is not None: + document_meta.update(doc.meta.model_dump(mode="json", exclude_none=True)) runtime = { "schema": "cad.runtime.v1", "schema_version": "1.0.0", "kind": "part", - "part_id": "compiled", "meta": {"unit": "mm"}, + "part_id": "compiled", "meta": document_meta, "bodies": [ {"id": body_ids[body.name], "name": body.name} for body in doc.bodies diff --git a/backend/app/cad_agent/application/authoring_contract.py b/backend/app/cad_agent/application/authoring_contract.py index 677736ef..67d109be 100644 --- a/backend/app/cad_agent/application/authoring_contract.py +++ b/backend/app/cad_agent/application/authoring_contract.py @@ -108,6 +108,33 @@ class SelectorIntent(AuthorModel): return value +class FeatureIntent(AuthorModel): + """Feature-level semantic annotation for training data. + + Mirrors ``$defs/featureIntent`` in ``cdsl_schema.json``: purely + descriptive, never read by the compiler for geometry decisions. + """ + + label: str | None = Field(default=None, pattern=r"^[a-z][a-z0-9_]{2,63}$") + summary: str = Field(min_length=1, max_length=80) + why: str | None = Field(default=None, min_length=1, max_length=400) + ties_to_requirement: str | None = Field(default=None, pattern=r"^[A-Za-z0-9_.:-]{1,80}$") + provenance: Literal["authored", "annotated", "imported"] + + +class DocumentMeta(AuthorModel): + """Document-level semantic annotation (part description and function).""" + + description: str | None = Field(default=None, min_length=1, max_length=60) + function: str | None = Field(default=None, min_length=1, max_length=200) + + @model_validator(mode="after") + def require_at_least_one(self) -> "DocumentMeta": + if self.description is None and self.function is None: + raise ValueError("meta requires at least one of description or function") + return self + + class AuthorFeature(AuthorModel): name: str = Field(pattern=_NAME) operation: str = Field(pattern=r"^[a-z][a-z0-9_]{0,80}$") @@ -121,6 +148,10 @@ class AuthorFeature(AuthorModel): default=None, description="For sketch operations: exactly {workplane, profile}. Circle profiles use diameter_mm and center_mm.", ) + intent: FeatureIntent | None = Field( + default=None, + description="Optional semantic annotation for training; never consumed by geometry.", + ) class AuthorBody(AuthorModel): @@ -133,6 +164,10 @@ class AuthoringDocument(AuthorModel): units: str = Field(default="mm", pattern=r"^mm$") coordinate_system: str = Field(default="right_handed", pattern=r"^[a-z][a-z0-9_-]{0,40}$") assumptions: list[str] = Field(default_factory=list, max_length=64) + meta: DocumentMeta | None = Field( + default=None, + description="Optional document-level semantic annotation (description/function).", + ) bodies: list[AuthorBody] = Field(min_length=1, max_length=32) acceptance_targets: list[dict[str, Any]] = Field(default_factory=list, max_length=128) diff --git a/backend/engine/cdsl_engine/cdsl_schema.json b/backend/engine/cdsl_engine/cdsl_schema.json index 9c6d01c7..6f2cb533 100644 --- a/backend/engine/cdsl_engine/cdsl_schema.json +++ b/backend/engine/cdsl_engine/cdsl_schema.json @@ -24,6 +24,18 @@ "required": ["schema", "geometry", "features"], "additionalProperties": false, "$defs": { + "featureIntent": { + "type": "object", + "properties": { + "label": {"type": "string", "pattern": "^[a-z][a-z0-9_]{2,63}$"}, + "summary": {"type": "string", "minLength": 1, "maxLength": 80}, + "why": {"type": "string", "minLength": 1, "maxLength": 400}, + "ties_to_requirement": {"type": "string", "pattern": "^[A-Za-z0-9_.:-]{1,80}$"}, + "provenance": {"enum": ["authored", "annotated", "imported"]} + }, + "required": ["summary", "provenance"], + "additionalProperties": false + }, "number": {"type": "number"}, "positive": {"type": "number", "exclusiveMinimum": 0}, "positiveInteger": {"type": "integer", "minimum": 1}, @@ -1192,7 +1204,8 @@ "params": {"type": "object"}, "execution_status": {"enum": ["supported", "deferred"]}, "selectors": {"type": "array", "items": {"$ref": "#/$defs/selectorRef"}}, - "unresolved": {"type": "array", "items": {"type": "string", "minLength": 1}} + "unresolved": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "intent": {"$ref": "#/$defs/featureIntent"} }, "required": ["id", "atomic_id", "depends_on", "params"], "additionalProperties": false, diff --git a/backend/engine/cdsl_engine/semantic_vocabulary.json b/backend/engine/cdsl_engine/semantic_vocabulary.json new file mode 100644 index 00000000..f208fc9b --- /dev/null +++ b/backend/engine/cdsl_engine/semantic_vocabulary.json @@ -0,0 +1,443 @@ +{ + "_meta": { + "title": "CDSL 语义词表几何签名(初稿 v0.1)", + "protocol": "cad.cdsl.llm.v1", + "date": "2026-09-14", + "role": "三层关联机制的字典层:label ↔ 几何期望的机器可读绑定。签名是期望(expectation),不是模板(template)。", + "checking_semantics": "签名检查只产生警告与训练数据分级,永不阻塞建模与重建(与 AGENTS.md 一致:不把语义分歧当程序错误)。", + "input_dependency": "metric_expectations 依赖重建管线输出的逐特征指标(derived_metrics per_feature delta)。缺失 delta 时,退化检查为:metric_expectations 中 *_delta 键跳过,其余照查。", + "inheritance": "label 可带 parent,检查时先应用父签名再应用自身(并集)。composite=true 表示该语义典型地由特征组合实现(如打孔+阵列),检查时沿 depends_on 链聚合 delta。", + "out_of_vocabulary": "词表外 label 无签名,程序侧零校验(开放词表的设计使然);训练侧应引导收敛到词表内。", + "known_limitation": "纯用途差异在几何上不可分:coolant_channel 与 lubrication_gallery、cosmetic_surface 与 stress_relief_fillet 的几何签名几乎相同,其区分依赖 intent.why 与上下文,不依赖本签名。", + "cross_field_reference": "param_expectations 的值可以引用另一参数路径(如 {\">\": \"diameter_mm\"}),匹配器解析为跨字段比较。", + "signature_version": "0.1", + "vocabulary_count": 37 + }, + "signatures": { + "bolt_circle": { + "parent": "fastener_hole", + "composite": true, + "allowed_atomics": ["pattern_circular", "pattern_linear", "hole_wizard", "hole_blind"], + "param_expectations": { + "pattern_count": {">=": 3}, + "positions": {">=": 3} + }, + "metric_expectations": { + "hole_count_delta": {">=": 3}, + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "relations": ["instances_same_diameter", "instances_on_common_circle"], + "notes_zh": "一组等径紧固孔沿公共圆周分布。典型实现:hole_wizard(1孔)+pattern_circular 阵列,或一次多 positions。检查时沿 depends_on 聚合打孔与阵列两特征的 delta。" + }, + "fastener_hole": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through"], + "param_expectations": { + "end_condition.type": {"in": ["through_all", "through_all_both", "blind"]} + }, + "metric_expectations": { + "hole_count_delta": {">=": 1}, + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "紧固件过孔(光孔)。词表父节点:tap_hole / bolt_circle / 沉头类均为其子型或伴生型。" + }, + "tap_hole": { + "parent": "fastener_hole", + "allowed_atomics": ["hole_wizard", "thread_cut"], + "param_expectations": { + "thread": {"required_if_atomic": "hole_wizard"} + }, + "metric_expectations": { + "hole_count_delta": {">=": 1}, + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true, + "creates_thread_helix": false + }, + "notes_zh": "螺纹孔。注意:hole_wizard 的 thread 装饰当前被引擎降级为光孔(thread_decoration_ignored 诊断),creates_thread_helix 恒为 false,签名如实反映现状;thread_cut 才产生真实螺旋几何。" + }, + "counterbore_seat": { + "parent": "fastener_hole", + "allowed_atomics": ["hole_counterbore", "hole_wizard"], + "param_expectations": { + "counterbore_diameter_mm": {">": "diameter_mm"}, + "counterbore_depth_mm": {">": 0} + }, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true, + "creates_planar_shoulder": true + }, + "notes_zh": "沉头柱坑:主孔 + 同轴大直径浅坑,坑底形成环形平面肩。" + }, + "countersink_seat": { + "parent": "fastener_hole", + "allowed_atomics": ["hole_countersink", "hole_wizard"], + "param_expectations": { + "countersink_diameter_mm": {">": "diameter_mm"}, + "countersink_angle_rad": {">": 0, "<": 3.141592653589793} + }, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cone_face": true + }, + "notes_zh": "沉头锥坑:主孔 + 锥形扩口,锥面是与柱面区分的强拓扑指纹。" + }, + "locating_pin_hole": { + "allowed_atomics": ["hole_wizard", "hole_blind"], + "param_expectations": {}, + "metric_expectations": { + "hole_count_delta": {">=": 1}, + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "定位销孔。配合公差与双孔位置度是设计要点,但公差不在 CDSL 当前表达能力内,机器只能查到 存在性/数量;配合语义写 why。" + }, + "bearing_seat": { + "allowed_atomics": ["extrude_add_blind", "cylinder_add", "revolve_add", "hole_wizard"], + "param_expectations": {}, + "metric_expectations": {}, + "topology_expectations": { + "creates_cylindrical_fit_surface": true + }, + "notes_zh": "轴承位(轴颈或座孔)。功能型语义,几何签名天然宽:内核是圆柱配合面(外圆或内孔),方向(增/减材)与安装形式有关。尺寸配合关系写 why。" + }, + "shaft_passage": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through"], + "param_expectations": { + "end_condition.type": {"in": ["through_all", "through_all_both"]} + }, + "metric_expectations": { + "hole_count_delta": {">=": 1}, + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "relations": ["axis_aligned_with_body_center_or_axis"], + "notes_zh": "过轴通孔。贯穿是强约束(签名可查);同心是典型但非必然(签名标注为关系提示)。" + }, + "press_fit_boss": { + "allowed_atomics": ["extrude_add_blind", "cylinder_add"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_outer_cylindrical_face": true + }, + "notes_zh": "压配合凸台:增材圆柱特征。过盈量等配合信息写在 why,几何只可查 增材+圆柱面。" + }, + "alignment_datum": { + "allowed_atomics": ["reference_plane", "reference_axis", "extrude_add_blind", "hole_blind"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">=": 0} + }, + "topology_expectations": {}, + "notes_zh": "对中/对位基准结构。可以是参考几何(零材料变化)也可以是小凸台/销孔;签名宽,依赖 why。" + }, + "mounting_boss": { + "allowed_atomics": ["extrude_add_blind", "cylinder_add"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_outer_cylindrical_face": true + }, + "notes_zh": "安装凸台:增材圆柱特征,常带后续紧固孔(组合语义)。" + }, + "mounting_foot": { + "allowed_atomics": ["extrude_add_blind"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_planar_faces": true + }, + "notes_zh": "安装底脚:增材板状特征,形成安装平面。" + }, + "lifting_eye": { + "allowed_atomics": ["extrude_add_blind", "revolve_add", "sweep_add"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "吊环/吊耳:增材结构带贯穿吊孔(吊索穿过)。孔的存在是较强指纹。" + }, + "slot_adjustment": { + "allowed_atomics": ["extrude_cut_blind", "extrude_cut_through", "hole_wizard", "hole_blind"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_planar_walls": true + }, + "notes_zh": "调整长孔/滑槽:允许位置调节的细长切口。典型实现:长圆 polygon 切除,或两孔+直切。" + }, + "coolant_channel": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through", "sweep_add"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "冷却通道。与 lubrication_gallery 几何签名几乎相同(能力边界案例),区分靠 why 与介质上下文。" + }, + "lubrication_gallery": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through", "sweep_add"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "润滑油路。同上,几何上与冷却通道不可分。" + }, + "vent_hole": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through"], + "param_expectations": {}, + "metric_expectations": { + "hole_count_delta": {">=": 1}, + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "排气孔:小直径贯穿孔,通常贯穿(呼吸/排气的功能要求)。" + }, + "drain_port": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "排液口。位置在最低点是设计要点但不在几何签名能力内,写 why。" + }, + "fluid_inlet": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through", "extrude_add_blind"], + "param_expectations": {}, + "metric_expectations": {}, + "topology_expectations": {}, + "notes_zh": "进液口。可能是孔(减材)也可能是接管凸台+孔(增减组合),签名宽。" + }, + "process_corner_relief": { + "allowed_atomics": ["extrude_cut_blind", "extrude_cut_through", "chamfer", "fillet"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": {}, + "notes_zh": "工艺让位(避让刀具/相邻件干涉)。材料切除量小是典型特征。" + }, + "weld_prep": { + "allowed_atomics": ["chamfer"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_cone_face": true + }, + "notes_zh": "焊接坡口:倒角原子直接映射(坡口即倒角),锥面是强指纹。" + }, + "machining_setup_tab": { + "allowed_atomics": ["extrude_add_blind"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": {}, + "notes_zh": "装夹工艺台:临时增材,后续工序去除。生命周期语义写在 why(几何本身与普通小凸台不可分)。" + }, + "inspection_access": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_through"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_inner_cylindrical_face": true + }, + "notes_zh": "测量/探针可达孔。" + }, + "stress_relief_fillet": { + "allowed_atomics": ["fillet"], + "param_expectations": { + "radius_mm": {">": 0} + }, + "metric_expectations": { + "volume_delta": {"<": 0}, + "face_count_delta": {">": 0} + }, + "topology_expectations": { + "creates_torus_face": true + }, + "notes_zh": "应力缓解圆角。torus 面(圆角面)+面数增加是修饰操作生效的强指纹;与 cosmetic_surface 的区别是用途而非几何。" + }, + "stiffening_rib": { + "allowed_atomics": ["extrude_add_blind"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_planar_faces": true + }, + "notes_zh": "加强筋:增材薄壁特征。薄(相对壁厚)是定义的一部分但属跨字段相对约束,初稿不做机器检查。" + }, + "weight_relief": { + "allowed_atomics": ["hole_wizard", "hole_blind", "extrude_cut_blind", "extrude_cut_through"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": {}, + "notes_zh": "减重(孔/槽)。通常多孔或多腔(volume delta 显著为负)。" + }, + "mass_saving_pocket": { + "allowed_atomics": ["extrude_cut_blind"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_planar_faces": true + }, + "notes_zh": "减重腔:盲切腔体,不留穿。与 weight_relief 的区别是形式(腔 vs 孔阵)。" + }, + "load_path_flange": { + "allowed_atomics": ["extrude_add_blind", "revolve_add"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": {}, + "notes_zh": "承载法兰/接盘。功能型语义,签名宽。" + }, + "wall_thickness_transition": { + "allowed_atomics": ["shell", "extrude_add_blind"], + "param_expectations": {}, + "metric_expectations": {}, + "topology_expectations": {}, + "notes_zh": "壁厚过渡。实现多样(抽壳/变截面拉伸),签名宽。" + }, + "gear_teeth": { + "allowed_atomics": ["gear_add"], + "param_expectations": { + "module_mm": {">": 0}, + "teeth_count": {">=": 8} + }, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_involute_or_bspline_faces": true + }, + "notes_zh": "轮齿:gear_add 原子专属。渐开线/自由曲面齿面是强指纹(亦可用于检测 gear_add 是否真的生成几何)。" + }, + "rack_teeth": { + "allowed_atomics": ["rack_add"], + "param_expectations": { + "module_mm": {">": 0} + }, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_involute_or_bspline_faces": true + }, + "notes_zh": "齿条齿:rack_add 原子专属。" + }, + "thread_drive": { + "allowed_atomics": ["thread_add"], + "param_expectations": { + "pitch_mm": {">": 0} + }, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_thread_helix": true + }, + "notes_zh": "传动螺纹(外螺纹实体段):thread_add 专属,螺旋面是强指纹。" + }, + "cam_track": { + "allowed_atomics": ["sweep_add", "extrude_cut_through", "extrude_cut_blind"], + "param_expectations": {}, + "metric_expectations": {}, + "topology_expectations": { + "creates_bspline_curve_geometry": true + }, + "notes_zh": "凸轮轨道:曲线扫掠或曲线槽,B 样条路径/曲线是其指纹。" + }, + "bend_wing": { + "allowed_atomics": ["bend_add"], + "param_expectations": { + "chain": {">=": 1} + }, + "metric_expectations": { + "volume_delta": {">": 0} + }, + "topology_expectations": { + "creates_cylindrical_bend_face": true + }, + "notes_zh": "折弯翼:bend_add 原子专属,折弯内/外圆柱面是指纹。" + }, + "cosmetic_surface": { + "allowed_atomics": ["fillet", "chamfer"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"<": 0} + }, + "topology_expectations": { + "creates_torus_face": true + }, + "notes_zh": "外观修饰(倒圆/倒角)。与 stress_relief_fillet 几何签名几乎相同(能力边界案例),区别写 why。" + }, + "datum_plane_feature": { + "allowed_atomics": ["reference_plane"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"==": 0}, + "face_count_delta": {"==": 0} + }, + "topology_expectations": {}, + "notes_zh": "设计基准面:零材料变化 + 零拓扑变化 + 特定原子,是全部签名中最确定的一条(强指纹)。" + }, + "datum_axis_feature": { + "allowed_atomics": ["reference_axis"], + "param_expectations": {}, + "metric_expectations": { + "volume_delta": {"==": 0}, + "face_count_delta": {"==": 0} + }, + "topology_expectations": {}, + "notes_zh": "设计基准轴:同上,强指纹。" + } + } +}