# CADQL Tagging 与 Auto Semantics 重构设计 ## 文档状态 - 状态:Partially implemented (bounded transition) - 目标版本:Semantic Binding Schema 1.0 - 依赖:`design-docs/cadql-brep-query-language.md` - 目标项目:SimpleCADAPI 2.x - 主要影响模块:`tagging.py`、`autotag.py`、`core.py`、`topology.py`、`tracking.py`、`graph.py`、`serializer.py`、`cadql/`、各 translator backend - 非目标:继续扩展当前 `ql.py`、为每种 feature 增加专用 selector、用 tag 替代完整 topology history ### 当前实现边界 当前 2.0 graph/model schema 已实现 Semantic Binding Schema 1.0 的 bounded subset:source-preserving `TagBinding`、`local`/`inherited`/`effective`/`lineage` scope、显式 downward policy、evidence-gated tracking projection、semantic-only graph node、model-level `semantic_bindings` registry 与 strict replay。当前 `ql.py` 仅作为过渡层增加 scoped tag predicate、typed operation/origin/output-role predicate,以及 projected source-binding/source-topology predicate,不是完整 CADQL Query IR。 尚未实现完整 typed CADQL Query IR、完整 Topology Evolution Graph、GraphQL-like parser、所有 topology/use 类型以及全部 translator backend parity。当前实现继续使用 graph/model schema `2.0`;本文中依赖 schema `3.0` 或完整 provider contract 的部分仍为 proposed。 ### 已实现的 feature output role 子集 当前 `TopoDelta.roles: Tuple[TopoRoleEntry, ...]` 将 output role 与 `TopoEntry.event` 分开保存。每个 role entry 使用 geometry-node-owned durable `TopoRef`,并要求 operation-owned OCC witness、`coverage=complete`、 `status=proven` 与 exact result membership。以下是当前 public contract: | Operation | Role | Kind | Requested cardinality | | --- | --- | --- | --- | | Extrude | `extrusion.start` | Face | exactly one | | Extrude | `extrusion.end` | Face | exactly one | | Extrude | `extrusion.side` | Face | at least one, bind all | | Revolve | `revolution.start` | Face | exactly one | | Revolve | `revolution.end` | Face | exactly one | | Revolve | `revolution.side` | Face | at least one, bind all | | Fillet | `fillet.patch` | Face | at least one, bind all | | Chamfer | `chamfer.patch` | Face | at least one, bind all | | Shell | `shell.body_face` | Face | at least one, bind all | | Shell | `shell.offset_face` | Face | at least one, bind all | | Shell | `shell.closing_descendant` | Face | at least one, bind all | | Shell | `shell.wall` | Edge | at least one, bind all | | Loft | `loft.start` | Face | exactly one | | Loft | `loft.end` | Face | exactly one | | Loft | `loft.side` | Face | at least one, bind all | | Sweep | `sweep.start` | Face | exactly one | | Sweep | `sweep.end` | Face | exactly one | | Sweep | `sweep.side` | Face | at least one, bind all | 这些 role 由 `FirstShape`、`LastShape`、`Generated`、`Modified`、Fillet/Chamfer contour API 和 exact result membership 产生;不使用 center/normal、enumeration index 或 unmatched-to-generated fallback。Public feature 的 named tag arguments lower 为独立 `apply_tag_rselection` node,用户 binding 是 `USER_OPERATION + ASSERTED`,自动 role binding 是 `AUTO_RULE + PROVEN + RECOMPUTE`。Geometry node params 不保存这些 tag。 Strict replay 会重建 feature 后比较 serialized/recomputed role entries,并验证 role allowlist、feature node ownership、output slot、coverage/status、topology kind、binding ancestry、cardinality 和 exact selected refs。Full revolve 不具有 独立 start/end cap;OCC 未证明的 shell role 也保持 unavailable。调用方请求这些 role 时整个 operation 失败,不以 geometry guess 补齐。 当前 bounded QL 增加: ```python ql.output_role(role_name="extrusion.end") ql.source_binding(binding_id="tag_binding_...") ql.source_topology(topo_id="edge_...") ``` 后两个 predicate 只查询 canonical local `TagBinding.evidence`。Extrude 的 source Edge 到 generated side Face 投影只复制完整 witness 下的 local `USER_OPERATION` binding,并保存 source binding/topology、target topology、 operation、role 和 evidence method。Replay-safe authoring 必须把 `apply_tag_rselection(...)` 返回的 semantic view 作为 feature input;detached semantic branch 不会引入隐藏 graph coupling。Predicate payload deserialization 对 unknown kind、unknown/missing fields 与非法 child cardinality fail closed。 ## 1. 摘要 下一代 tagging 系统不再把所有语义压入 topology wrapper 上的 `_tags: set[str]`。Canonical truth 是可序列化、保留来源的 `TagBinding`;CADQL 将 tag 作为 `semantic.tags` 属性查询,并在执行时计算 `local`、`inherited`、`effective` 和 `lineage` 四种 scope。 Auto tagging 也不再表示“给所有结果实体补一些字符串”。它是一组 evidence-gated semantic rules:规则只能在所需事实可证明时产生 `TagBinding`,证据不足时产生 `UNKNOWN`/diagnostic 或不产生 assertion,不能把 unmatched entity 推断为 generated。 本设计遵循以下边界: 1. 用户或产品域赋予的 unary categorical meaning 使用 tag。 2. Current BRep 结构事实使用 topology property/relation。 3. 数值、枚举、几何分类和诊断使用 typed property/metadata。 4. Operation、source role、split/merge/generated/preserved 和多来源因果使用 Topology Evolution Graph。 5. Tag 可以作为稳定意图锚点,但不能成为 relation、history 或 evidence 的损失性副本。 ## 2. 当前问题 当前实现由四套机制叠加而成: | 机制 | 例子 | 当前问题 | | --- | --- | --- | | 用户显式 tag | `role.mounting_surface` | 与系统 tag 共用一个 set,无法识别作者和 assignment。 | | Wrapper 结构 tag | `wire.outer`、`edge.boundary` | 把可直接从 BRep graph 得到的事实 materialize 为字符串。 | | 几何分类 tag | `face.top`、`face.side` | frame、算法和置信度不明确;未知几何退化为 enumeration index。 | | Tracking tag | `op.cut.generated`、`origin.tool` | 把 Change event 和 source role 降级为 unary string,且依赖 Face-only matcher。 | 具体缺陷包括: - `_tags: set[str]` 不保存 assignment、producer、target query、attachment、propagation、evidence 和 lifecycle。 - Downward propagation 直接复制字符串,之后无法区分 local 与 inherited。 - `_carry_source_tags()` 直接复制 source Face 字符串,无法恢复 lineage witness。 - `apply_tracking_tags()` 只遍历 Face,不能消费 Edge/Wire/TopoUse history。 - Unmatched Face 默认被标记 `generated`,把 history 缺失伪装成确定事件。 - Boolean `section_edges` 是 Edge evidence,却用 Face ID 匹配,不能正确工作。 - Loft/sweep 提供 Edge history,但 Face-only auto tagger仍会给结果 Face生成看似完整的 operation tag。 - Transform 的 pure-preserve fallback 没有逐实体 correspondence,却对所有结果 Face声称 preserved。 - `face.face_0`、pattern index 和 sketch edge zip-order 依赖 backend enumeration,不是 durable semantic identity。 - Operation runtime 的 rich `delta_entries` 与 canonical `TopoDelta.entries` 分离,replay/translator 无法保证同一 semantic output。 当前测试中的“每个结果 Face 都有 operation tag”不是正确性证明,因为该覆盖率可以完全由 unmatched-to-generated fallback 产生。 ## 3. 与 CADQL 的关系 CADQL 在三种图的联合视图上执行: | 数据层 | 职责 | Tagging 是否拥有该事实 | | --- | --- | --- | | Operation Graph | 模型构建、assignment producer、query binding | 否;TagBinding 引用它。 | | Current BRep Graph | contains、boundary、incident、adjacent、TopoUse | 否;tag 只作为 entity 的 semantic property。 | | Topology Evolution Graph | Change input/output、role、event、derivation | 否;lineage scope 依赖它计算。 | | Semantic Binding Store | 用户/系统声明的稳定 unary meaning | 是。 | CADQL 只有一种 tag predicate: ```python cadql.tag("role.mounting_surface", scope="effective") ``` 它是对 `semantic.tags` 的 property predicate,不创建新的 selector、anchor 或 context 类型。Tag assignment 的 target 本身使用同一套 `SelectionQuery[T]`;不再引入另一套 tag selector DSL。 ## 4. 设计原则 ### 4.1 一项事实只有一个 canonical owner 以下映射是强制的: | 事实 | Canonical owner | 可否派生为 tag | | --- | --- | --- | | Face 的 outer/inner loop | `topology.loop_role` | 默认否。 | | Edge 是 Face boundary | `boundary_uses`/`use_entity` | 否。 | | Surface 是 plane/cylinder | `geometry.surface_type` | 默认否。 | | Face 当前在 world +Z 方向 | oriented geometry predicate + frame | 否。 | | Entity 由某 operation 产生 | `provenance.produced_by` | 否。 | | Entity 是 preserved/modified/generated | Change output event | 否。 | | Entity 来自 body/tool/profile/path | Change input role/path | 否。 | | 用户声明 mounting surface | TagBinding | 是,且这是 canonical truth。 | | 业务规则证明某面是 sealing surface | TagBinding + rule evidence | 是。 | | 长度、半径、pattern index | typed property/metadata | 否。 | Tag materialized cache 可以为性能复制 relation/property 的结果,但必须标记为 derived cache、可丢弃且不能成为 replay truth。Semantic Binding Schema 1.0 不定义此优化。 CADQL 中的 `semantic.role` 和 `semantic.group` 是 registry-backed TagBinding projection,例如从 `role.*` 和 `group.*` bindings 得到结构化视图,不建立第二份 writable storage。`semantic.material` 等具有独立 schema、互斥或结构化值的属性使用 typed semantic property,不同时编码为 tag。查询 provider必须从同一个 canonical owner求值这些属性。 ### 4.2 Tag 只表达 unary categorical semantics 适合 tag 的内容: ```text role.mounting_surface role.sealing_surface anchor.datum.primary group.fasteners process.inspect ``` 不适合 tag 的内容: ```text op.make_cut_rsolid.generated origin.tool face.area.245 edge.index.3 face.normal.+z wire.outer ``` 判断标准:如果完整含义需要 source、target、operation、frame、单位、置信度或多个参与者,它就不是单个 tag。 ### 4.3 Evidence 不足时保持未知 Auto semantic rule 的三值结果为: ```text PROVEN 所需 evidence 完整,允许发出 binding。 NOT_APPLICABLE 规则已完整求值,candidate 不满足。 UNKNOWN evidence 或 capability 不足,禁止发出 binding。 ``` `UNKNOWN` 不能被实现为 false、generated、preserved 或最佳猜测。Strict replay 中,如果 feature 依赖该 auto semantic,`UNKNOWN` 必须产生结构化错误;非依赖型装饰 semantic 可以只生成 diagnostic。 ### 4.4 Query intent 与 execution evidence 分离 Target query 描述要标记谁;assignment evidence 描述某次执行为何解析到这些 entities。Replay 先重新执行 query,再比较 evidence。`TopoRef.topo_id` 和 geometry fingerprint 只属于 evidence,不是 TagBinding identity。 ### 4.5 Semantic state 是版本化 shape view `apply_tag_rselection` 不修改 geometry,但产生新的 semantic state version。相同 BRep entity 在 assignment 前后可以有不同 effective tags。CADQL provider 必须相对当前 scope producer/version 读取 bindings,不能从全局 mutable wrapper set 读取历史无关的并集。 ## 5. Canonical 数据模型 ### 5.1 TagDefinition Tag token 继续使用 normalized dot-token 格式: ```text [a-z][a-z0-9_-]*(.[a-z][a-z0-9_-]*)* ``` 可选 registry 为已知 namespace 声明行为: ```json { "tag": "role.mounting_surface", "namespace": "role", "value_kind": "categorical", "allowed_target_types": ["face"], "default_topology_propagation": "local", "default_lineage_policy": "continuation_fragment" } ``` Registry 用于 validation 和文档,不拥有 assignment。未注册但格式合法的 custom tag 可以被允许,但必须显式提供 propagation 和 lineage policy,不能从字符串前缀静默猜测。 ### 5.2 TagBinding Canonical assignment: ```json { "schema_version": "1.0", "binding_id": "tag_binding_mounting_surface", "tag": "role.mounting_surface", "producer": { "kind": "user_operation", "node_id": "node_tag_mounting_surface", "rule_id": null, "rule_version": null }, "scope": { "node_id": "node_body", "output_slot": 0 }, "target": { "kind": "selection_query", "query_hash": "sha256:...", "binding_hash": "sha256:..." }, "attachment": "local", "propagation": { "topology": "local", "lineage": "continuation_fragment" }, "evidence": { "kind": "query_execution", "execution_hash": "sha256:...", "selected_refs": [] }, "certainty": "asserted", "lifecycle": "assertion" } ``` 字段语义: | 字段 | Contract | | --- | --- | | `binding_id` | Assignment identity,不由 selected topology ID 生成。 | | `tag` | Unary semantic token。 | | `producer` | 用户 operation 或 versioned auto rule。 | | `scope` | Target query 的 current snapshot/version boundary。 | | `target` | Canonical Query IR binding 或仅用于 legacy import 的 explicit refs。 | | `attachment` | 新 assignment 只允许 `local`;`inherited` 是计算结果。`effective_legacy` 仅用于迁移。 | | `propagation.topology` | `local` 或显式 `downward`;`local` 表示不沿 topology传播。 | | `propagation.lineage` | 允许沿哪些 derivation 计算 lineage visibility。 | | `evidence` | Assignment execution witness,不参与 query intent hash。 | | `certainty` | `asserted` 或 `proven`;猜测值不能进入 canonical bindings。 | | `lifecycle` | replay 时重新解析、重新计算或保持 legacy snapshot。 | ### 5.3 TargetBinding 新 assignment 的 target 必须是: ```text selection_query canonical Query IR + runtime bindings scope_root lower 为选择当前 scope root 的 canonical query ``` 以下 target 只用于迁移: ```text explicit_refs execution-local refs,无 semantic intent legacy_effective 只有旧 flat tag snapshot ``` Auto rule 可以直接在 producer operation 的已知 Change outputs 上产生 target,但 serialization 时仍 lower 为 canonical source-preserving target: - 引用 producer output variable 的 query;或 - 引用 versioned Change output set 的 internal binding。 不能只保存“当时第 3 个 Face”。 ### 5.4 Producer Producer kind: ```text user_operation auto_rule imported_sidecar legacy_import ``` Auto rule 必须保存稳定 `rule_id` 和 `rule_version`。修改 rule 语义必须提升 version;replay 可据此检测 semantic drift。 ### 5.5 Evidence Evidence 是 tagged union: | Kind | 必需内容 | 适用场景 | | --- | --- | --- | | `user_assertion` | assignment node、authoring source | 用户明确声明。 | | `query_execution` | query/binding/execution hash、selected refs | Query target assignment。 | | `topology_change` | Change IDs、input/output witnesses、coverage | 基于 lineage 的 auto rule。 | | `geometry_classification` | algorithm/version、frame、tolerance、measured properties | 几何分类规则。 | | `imported_sidecar` | source URI/hash/schema | 外部 semantic data。 | | `legacy_snapshot` | imported tags、migration diagnostic | 旧模型兼容。 | Evidence 可以证明 assignment,但不会把几何 fingerprint 升级为 durable target identity。 ## 6. Tag Scope 的正式语义 ### 6.1 Local `local` 返回当前 semantic state version 中,target query 直接匹配该 entity/use 的 bindings。 ```text local(e, v) = {b.tag | binding b is visible in semantic version v AND target(b, v) contains e} ``` 它不包含 topology parent、model entity 或 lineage ancestor 的 tag。 ### 6.2 Inherited `inherited` 只来自显式 topology propagation policy: ```text inherited(e, v) = bindings attached to ancestor a where topology propagation is downward and contains-path(a, e) is valid in snapshot v ``` 规则: - 不再根据 `role.*`、`anchor.*`、`group.*` 前缀自动决定传播。 - Propagation 在 assignment 时显式冻结并序列化。 - Downward 传播计算于 Current BRep containment,不复制字符串。 - 默认是 `local`,即不传播。 - 给 Solid 标记 `role.mounting_plate` 不应默认让每条 Edge 都成为 mounting plate。 - 若确实表达 assembly/model membership,优先使用 model relation 或显式 group binding;不要依靠 topology descendants 模拟产品结构。 ### 6.3 Effective ```text effective(e, v) = local(e, v) union inherited(e, v) ``` `effective` 不自动包含 lineage。这样 current snapshot 内的 attachment semantics 与跨 operation ancestry 保持正交。 ### 6.4 Lineage `lineage` 通过 TagBinding 与 Topology Evolution Graph 联合计算: ```text lineage(e, v) = source bindings whose tagged entities are reachable through allowed Change output derivations ``` 默认 policy: | Policy | 允许 derivation | 说明 | | --- | --- | --- | | `none` | 无 | 不跨 operation 可见。 | | `continuation` | `CONTINUATION` | 只接受同角色延续。 | | `continuation_fragment` | `CONTINUATION`、`FRAGMENT` | 允许 trim/split fragments。 | | `explicit` | assignment 指定集合 | 允许 `MERGE`/`REPLACEMENT` 等业务定义。 | `INTERSECTION` 和 `BOUNDARY` 默认不继承 source tag,因为新 entity 是由 source 参与生成,不等于 source 语义角色的延续。需要选择这类结果时使用 `generated_from`、`depends_on` 或 `cadql.change(...)`。`MERGE`/`REPLACEMENT` 也不默认继承,除非 assignment 显式声明。 `lineage` scope 只返回 lineage-derived tags;调用方若需要本地与 lineage 并集,应显式组合 predicates,不把 `effective` 的含义扩大。 History capability 为 `NONE` 或所需 path evidence 不完整时,请求 `lineage` 必须产生 `UnsupportedQueryCapabilityError`,不能返回空集冒充“没有 tag”。 ### 6.5 TopoUse TagBinding 可以 target entity 或 oriented use: - Entity tag 对同一 underlying entity 的所有 occurrences 可见。 - TopoUse tag 只对该 parent/orientation/occurrence 可见。 - TopoUse 的 inherited 与 lineage 依赖 use-level containment/evolution evidence。 - 缺少 use ancestry 时不能把 entity lineage tag 冒充 occurrence lineage tag。 ## 7. Public API 与 Operation Graph ### 7.1 用户赋值 保留简单入口: ```python apply_tag(body, "role.mounting_plate") ``` 新 schema 中该 convenience API 的固定默认值是: ```text topology_propagation = LOCAL lineage_policy = CONTINUATION_FRAGMENT ``` 因此它不会把 Solid 的 role 复制给所有 Face/Edge,但在 backend 提供完整 `CONTINUATION`/`FRAGMENT` evidence 时,该 assertion 可以通过 `scope="lineage"` 从后续版本查询。需要 topology downward inheritance 时必须调用带显式 policy 的 `apply_tag_rselection`。迁移期执行旧 model时可以保留旧 effective结果,但新 assignment不能继续按token prefix选择policy。 它 lower 为 scope-root query 和 canonical operation: ```text apply_tag_rselection( scope: Shape, targets: SelectionSet, tag: Tag, topology_propagation: LOCAL | DOWNWARD, lineage_policy: NONE | CONTINUATION | CONTINUATION_FRAGMENT | EXPLICIT, ) -> Shape ``` 推荐的精确赋值: ```python mounting_face = ( cadql.select(cadql.Face, in_=body) .where( cadql.surface_type.eq("plane") & cadql.normal.same_direction_as( (0, 0, 1), relative_to=body, angle_tolerance=cadql.deg(0.1), ) ) .expect_one() ) tagged_body = apply_tag_rselection( body, targets=mounting_face, tag="role.mounting_surface", topology_propagation="local", lineage_policy="continuation_fragment", ) ``` Active `GraphSession` 必须记录 target Query IR 和 binding,不能只记录 resolved Face refs。 ### 7.2 读取 CADQL 是 canonical query API: ```python cadql.select(cadql.Face, in_=body).where( cadql.tag("role.mounting_surface", scope="effective") ) ``` `list_tags(shape)` 可以保留为 current wrapper convenience,但默认返回当前 version 的 `effective` tags,并应允许显式 scope: ```python list_tags(shape, scope="local") list_tags(shape, scope="effective") list_tags(shape, scope="lineage") ``` 它不得绕过 provider 从 `_tags` 直接读取。迁移期旧签名等价于 `scope="effective"`。 ### 7.3 删除和替换 Tag 删除不是从 entity set 中 `discard()` 字符串,而是产生新的 semantic operation,撤销或 supersede 指定 binding: ```text remove_tag_rselection(scope, targets, tag, matching_producer?) -> Shape ``` 默认不能静默删除其他 producer 的同名 binding。例如移除 auto-rule binding 不应删除用户 assertion。 ### 7.4 Conflict semantics 同一个 entity 可同时存在多个 producer 的同名 tag,查询结果按 token 去重,但 evidence/projection 保留所有 bindings。 Tag 1.0 不定义基于命名约定的互斥,例如 `state.open` 与 `state.closed`。真正互斥的数据应使用 typed enum property。若产品域 registry 声明 tag group 互斥,assignment validation 必须报告 conflict,不能按“最后写入者获胜”处理。 ## 8. Auto Semantics Rule Contract ### 8.1 Rule schema 每条规则必须声明: ```json { "rule_id": "simplecad.primitive.box.face_roles", "rule_version": "1.0", "trigger": "make_box_rsolid", "candidate_type": "face", "required_capabilities": [ "operation_output_roles", "oriented_face_geometry" ], "required_evidence": { "history": "operation_native", "frame": "operation_context", "coverage": "complete" }, "output": { "kind": "tag_binding", "allowed_tags": ["role.cap.start", "role.cap.end", "role.side"] }, "unknown_policy": "diagnostic", "lifecycle": "recompute" } ``` Rule evaluator 返回: ```text bindings status: PROVEN | NOT_APPLICABLE | UNKNOWN evidence diagnostics coverage ``` ### 8.2 权威等级 | Level | Source | 可产生的结果 | | --- | --- | --- | | 1 | 用户显式 assertion/imported authoritative sidecar | `certainty=asserted` binding。 | | 2 | Kernel/operation-native topology history | Change graph;满足业务规则时可产生 `certainty=proven` binding。 | | 3 | Exact current topology fact | topology property/relation;通常不 materialize 为 tag。 | | 4 | Deterministic geometry classifier | typed geometry property;只有注册业务映射后才能产生 tag。 | | 5 | Heuristic/fingerprint | diagnostic/evidence only。 | 低等级 evidence 不能覆盖或伪装为高等级事实。尤其 geometry proximity 不能产生 preserved/generated lineage。 ### 8.3 Coverage 每个 rule evaluation 必须声明: ```text COMPLETE 候选 universe 与所需 evidence 完整。 PARTIAL 只有部分 candidates/relations 有 evidence。 NONE 无法求值。 ``` 只有 `COMPLETE` 且 rule 对具体 candidate 得到 `PROVEN` 时才发出需要全局排他性的 tag,例如“唯一 start cap”。`PARTIAL` 不能通过给其余 candidates 标记 opposite role 来补齐结果。 ### 8.4 禁止行为 以下规则在新系统中非法: - unmatched result entity => `generated`。 - pure-preserve delta => 所有 result entities preserved,而无逐实体 mapping。 - backend enumeration index => semantic tag。 - geometry look-alike => lineage continuation。 - 将 operation 名、event 和 origin role拼接为 tag作为唯一事实。 - 给所有 descendants 无条件复制 `role.*`。 - 多来源 Change => 只保留一个 `origin.*` 字符串。 - rule 失败后静默回退到 fingerprint 并仍标记 `proven`。 ## 9. Operation-by-Operation 策略 ### 9.1 Primitive Primitive operation 自己知道构造参数和 output roles,优先使用 operation-native evidence,而不是事后猜测几何。 | 当前输出 | 新 canonical 表达 | | --- | --- | | `geom.primitive.box` | `provenance.operation_type == make_box_rsolid` 或 typed semantic metadata,不需要 tag。 | | 裸 `box` | 删除。 | | `face.top/bottom/front/...` | 不自动作为 world-direction tag。使用 geometry predicate + explicit frame。 | | `face.surface` | `geometry.surface_type`。 | 如果产品 API 需要稳定 feature role,可定义 frame-aware operation role: ```text role.cap.start role.cap.end role.side ``` 这些角色必须绑定 primitive 的参数方向和 operation context,并保存 rule evidence。对于 box 的四个侧面,如果没有额外业务区别,不自动命名 front/back/left/right;数学对称性应保留为集合。 Cone 必须与 cylinder 使用同一 role contract,不再因为当前实现遗漏 `auto_tag_faces()` 而表现不同。 ### 9.2 Extrude 和 Revolve Feature provenance 和 topology evolution进入 Change graph: - Profile Face/Coedge 是 `subject` 或 `support` input。 - Start/end continuation、side/boundary outputs使用 event + derivation表示。 - `face.extrusion.start/side/end` 若保留为用户友好 semantic,必须由 operation-native role mapping产生 TagBinding,不使用 center exact equality或 normal exact equality。 - `solid.extrusion` 改为 `provenance.operation_type`,不作为 tag。 - Revolve 使用相同 Change contract;full/partial revolve 的 caps必须按实际 operation evidence处理。 推荐 query: ```python side_faces = ( cadql.select(cadql.Face, in_=extruded) .where( cadql.related( cadql.generated_from, from_=cadql.this, to=profile_boundary, derivations=("boundary",), depth=1, ) ) ) ``` 只有业务代码确实需要可命名的 feature role 时才额外发出 `role.extrusion.side`。 ### 9.3 Boolean Boolean 不再产生 canonical `op.*` 或 `origin.*` tags。 必须记录: - 每个 operand 的 named input port。 - 每个 source entity 的 role:`subject`、`tool`、`support` 或 `context`。 - 每个 output entity 的 event 和 derivation。 - Intersection Edge 的多 source Change witness。 交界 edge 使用: ```python cadql.change( inputs=( cadql.change_input(subject_faces, input_port="subjects", role="support"), cadql.change_input(tool_faces, input_port="tools", role="tool"), ), output=cadql.this, derivation="intersection", ) ``` 不能用 `role.section_face` 替代 Edge-level `INTERSECTION` Change。若产品域确实定义“section interface”业务角色,可在上述 query 上显式应用 tag;该 tag 的 target intent仍是 Change pattern。 多步 cut/intersect 必须保留每一步 Change,不能简单拼接 event buckets 后覆盖相同 `topo_id` 的 `delta_entries`。 ### 9.4 Fillet、Chamfer 和 Shell 这些 feature 的核心是 target selection 与 Change graph: - 用户选择的 Edge/Face 是 `target` input。 - 邻接 support Face 是 `support` input。 - Trimmed source entities 是 `CONTINUATION`/`FRAGMENT` output。 - Transition Face 和新 boundary Edge 是 `BOUNDARY` output。 - 完全消耗的 source entity保留为 historical input与 `DELETED` output event。 默认不产生 `op.fillet.generated` 等 tag。查询 transition geometry 应使用 `generated_from`、`depends_on` 和 `co_result_of`。 用户已有 `role.mounting_surface` 的 source Face,只沿 `CONTINUATION`/`FRAGMENT` 进入 lineage scope;fillet transition Face 不自动继承该角色。 ### 9.5 Transform 和 Mirror Rigid transform 不改变 topology role,但必须提供逐 entity/use `CONTINUATION` mapping。只有 mapping complete 时 lineage tag 才可查询。 - `solid.transform.mirrored` 改为 provenance/operation property。 - 不复制 source wrapper `_tags`。 - Mirror 的 orientation-sensitive TopoUse/normal 必须在新 parent occurrence和 frame中重新求值。 - 没有逐实体 mapping 时 history capability 为 `PARTIAL`,不能把所有 Face声明 preserved。 ### 9.6 Loft 和 Sweep Loft/sweep 必须首先修复 evidence kind mismatch: - Profile/path Edge history进入 Change graph。 - Result Face/Edge 与 profile/path source的关系必须由 operation-native adapter提供。 - Auto semantic evaluator按 candidate type消费对应 Change outputs,不允许 Face-only fallback。 - Profile 与 path 是不同 input ports/roles,不能把二者 tags union到 result Solid后丢失来源。 如果 backend只能提供 Edge-level partial history,Face lineage query与依赖它的 auto rule必须报告 unsupported/unknown;不能给全部 Faces标记 generated。 ### 9.7 Pattern Pattern 应有显式 Pattern operation 和 instance output relation: ```text source EntityVersion -> Change(instance identity) -> instance EntityVersion ``` 每个 instance 保存稳定 operation-local `instance_key`,index只作为 typed property: ```text provenance.pattern_instance_key provenance.pattern_ordinal ``` `solid.pattern.linear/radial` 改为 operation provenance,不是 semantic tag。Source tags通过 `CONTINUATION` lineage policy可见;不要复制到每个 instance wrapper。 对完全对称的 instances,若没有用户 tag或业务 key,CADQL返回集合或 ambiguity,不用 ordinal伪造 semantic identity。 ### 9.8 Sketch Promotion Sketch entity到 BRep Edge的映射是 source binding,不是 tag naming问题: - Sketch entity/profile使用稳定 semantic refs。 - Promotion adapter记录每个 output Edge对应的 source entity ref和 evidence。 - `sketch.*`、`sketch_profile.*`、`sketch_entity.*` 不再作为唯一 mapping。 - 禁止仅通过 `zip(edges, entity_ids)` 声称 correspondence。 - 用户附着在 sketch entity上的 semantic tag可通过 proven promotion Change在 `lineage` scope查询。 ## 10. Topology、Geometry、Provenance 与 Tag 的迁移表 | 现有 tag/数据 | 新位置 | 迁移行为 | | --- | --- | --- | | `wire.outer`/`wire.inner` | `topology.loop_role` on WireUse/Coedge | 不导入为 semantic tag。 | | `edge.boundary` | `boundary_uses` + `use_entity` | 不导入为 semantic tag。 | | `face.top/bottom/...` | Legacy effective tag;新模型使用 frame-aware geometry query或 explicit role binding | 导入并警告方向语义未声明。 | | `face.face_N` | Legacy enumeration annotation | 不生成新 binding;仅保留 migration evidence。 | | `geom.primitive.*` | `provenance.operation_type`/typed metadata | 可保留 legacy effective,不再新发出。 | | `op..` | Change output event + performed_by Operation | 不生成新 binding。 | | `origin.` | Change input role/path | 不生成新 binding。 | | `solid.boolean.*` | Operation provenance | 不生成新 binding。 | | `solid.pattern.*` | Pattern provenance/property | 不生成新 binding。 | | `role.*` 用户 tag | TagBinding | 尽可能恢复 assignment;否则 `effective_legacy`。 | | `anchor.*` 用户 tag | TagBinding | 同上。 | | `group.*` 用户 tag | TagBinding 或 model membership relation | 按 domain schema迁移。 | | Numeric/index tags | typed metadata/property | 不作为 semantic binding。 | ## 11. Legacy 兼容 旧模型只有 flat tags时,导入为: ```json { "producer": {"kind": "legacy_import"}, "target": {"kind": "legacy_effective", "refs": []}, "attachment": "effective_legacy", "propagation": {"topology": "local", "lineage": "none"}, "certainty": "asserted", "lifecycle": "snapshot", "evidence": {"kind": "legacy_snapshot"} } ``` Provider 只把 `effective_legacy` 暴露给 `scope="effective"`。它不能回答 local、inherited 或 lineage,并产生 migration diagnostic;该 attachment不是正常assignment可写入的值。 兼容规则: - 旧 public `list_tags()` 继续看到原 effective tokens。 - 旧 QL tag predicate可由 adapter lower 为 CADQL `effective` predicate。 - 不从 token prefix反推旧 propagation。 - 不从 `op.*`/`origin.*` 反向构造可信 Change graph。 - 如果 graph中同时有真实 TopoDelta evidence,可以独立迁移为 Change;仍不能仅依据旧 tag补齐缺失 parent mapping。 ## 12. Serialization 与 Provider Contract Model schema 3.0 增加: ```text semantic_bindings: TagBinding[] semantic_rules: AutoSemanticRuleRef[] semantic_diagnostics: SemanticDiagnostic[] ``` Operation node可以引用其产生的 binding IDs。TagBinding target query复用 model顶层 `query_objects`,不嵌入语义缩水的第二份 query。 Provider 增加: ```python class SemanticProvider(Protocol): def bindings(self, scope_version, entity_or_use) -> Sequence[TagBinding]: ... def tags(self, entity_or_use, scope: TagScope) -> Sequence[str]: ... def explain_tag(self, entity_or_use, tag: str, scope: TagScope) -> Sequence[TagWitness]: ... ``` `explain_tag()` 至少返回: ```text binding_id producer attachment path topology inheritance path, if any lineage Change path, if any evidence status diagnostics ``` OCP 和 FreeCAD provider必须运行同一 scope和rule conformance suite。Backend不支持某 rule所需 capability时报告 unknown/unsupported,不自行更换算法。 ## 13. Invalidation 与 Replay TagBinding lifecycle: | Lifecycle | Replay behavior | | --- | --- | | `recompute` | 重跑 target query或auto rule,生成新 evidence并比较 drift。 | | `assertion` | 重跑 target query;用户 semantic保留,但 query不再满足时失败。 | | `snapshot` | 仅 legacy/import sidecar;不能声称动态 lineage。 | 参数变化后: 1. 重建 geometry和 Change graph。 2. 重新解析 assignment target query。 3. 重新运行 versioned auto rules。 4. 撤销本次 replay中不再 proven的 derived bindings。 5. 比较旧/新 evidence并生成 drift diagnostic。 6. Feature依赖的 semantic target违反 cardinality时按 CADQL错误模型失败。 Derived auto binding不能因为曾经写入 wrapper set而永久残留。Semantic state由当前有效 bindings重新计算,不执行增量字符串清理猜测。 ## 14. 错误与诊断 新增结构化错误: | Error | 条件 | | --- | --- | | `TagValidationError` | token、target type、policy或registry不合法。 | | `TagTargetResolutionError` | Assignment target query无法满足 cardinality。 | | `SemanticConflictError` | Registry定义的互斥 semantic同时成立。 | | `AutoSemanticEvidenceError` | Strict dependency所需rule evidence为UNKNOWN/PARTIAL。 | | `SemanticCapabilityError` | Provider不能计算请求的tag scope或rule。 | | `SemanticDriftError` | Strict replay中assignment/rule结果发生不允许的漂移。 | Diagnostic 至少包含: ```text binding_id/rule_id scope version required and available capability coverage candidate count missing witnesses repair suggestions ``` ## 15. 测试策略 ### 15.1 Binding tests - TagBinding JSON round-trip和stable hash。 - 同名tag、不同producer不会互相覆盖。 - Target query intent与execution refs分离。 - Remove/supersede只影响指定binding。 ### 15.2 Scope tests - local不包含parent和lineage tags。 - inherited只沿显式downward policy计算。 - effective严格等于local union inherited。 - lineage只沿允许的derivations计算。 - `BOUNDARY`/`INTERSECTION` 默认不继承source role tag。 - history缺失时lineage产生capability error而不是空结果。 - entity tag和TopoUse tag保持不同visibility。 ### 15.3 Auto rule precision/recall - 每个emitted binding有required evidence witness。 - 可证明的semantic不会遗漏。 - Unmatched candidate不会得到generated/preserved tag。 - Partial coverage不会产生排他性role。 - Rule version变化产生semantic drift diagnostic。 ### 15.4 Operation fixtures - Primitive roles使用operation frame,不依赖world axis或Face枚举。 - Extrude/revolve caps和sides使用operation-native mapping。 - Boolean interface保留多input Change,Edge evidence不被当作Face。 - Fillet/chamfer source fragments、transition Faces和boundaries可通过evolution relation选择。 - Transform/mirror有逐entity continuation mapping。 - Loft/sweep在只有Edge evidence时拒绝Face lineage assertion。 - Pattern instances保留source lineage和instance key,不以ordinal作为semantic identity。 - Sketch promotion不依赖zip/enumeration order。 ### 15.5 Replay与translator - OCP和FreeCAD的tag scopes一致。 - Model JSON round-trip后producer/evidence/policies不丢失。 - 参数变化后过期auto bindings消失。 - Legacy effective tags仍可查询,但local/inherited/lineage明确unsupported。 ## 16. 实施阶段 ### Phase A:Characterization 与止损 - 锁定当前public tag行为和serialized fixtures。 - 给unmatched-to-generated、section Edge/Face mismatch、loft/sweep mismatch增加failing characterization tests。 - 停止新增`op.*`、`origin.*`和enumeration-derived tags。 - `apply_tracking_tags()`增加diagnostic mode,逐步取消unsound fallback。 退出标准:所有已知误报都有测试,不再扩大flat-tag contract。 ### Phase B:Semantic Binding Schema - 实现TagBinding、producer、target、policy、evidence和serialization。 - 实现semantic state version与binding store。 - `apply_tag_rselection`记录target Query IR。 - Flat `_tags` 暂时作为effective cache,不再是canonical truth。 退出标准:用户assignment可JSON round-trip并解释来源。 ### Phase C:CADQL Scope Provider - 实现local/inherited/effective。 - 移除prefix-driven runtime copying;迁移为binding-time explicit policy。 - 实现`explain_tag()`。 - Legacy tags仅通过effective adapter暴露。 退出标准:三个current-snapshot scopes通过OCP conformance tests。 ### Phase D:Topology Evolution Graph - 按CADQL设计建立完整Change inputs/outputs。 - 将`delta_entries`合并进canonical序列化schema。 - 修复Face-only consumer、section Edge、transform correspondence和多步Change。 - 实现lineage scope与history capability validation。 退出标准:continuation/fragment/boundary/intersection fixtures可查询且缺证据时失败。 ### Phase E:Auto Semantic Rules - 引入versioned rule registry和三值求值。 - 先迁移primitive与extrude roles,再迁移其他feature。 - 删除unmatched-generated和operation/origin tags的canonical生成。 - Geometry heuristic仅作为typed property或diagnostic。 退出标准:每个auto binding都有rule、evidence、coverage和replay lifecycle。 ### Phase F:Translator 与清理 - FreeCAD provider实现相同semantic binding/scope contract。 - 更新docs、skills和examples到CADQL tag query。 - Deprecate `Solid.auto_tag_faces()`和内部直接`_tags`复制。 - 删除flat tag作为truth的代码路径。 退出标准:OCP/FreeCAD semantic conformance一致,新model不依赖flat tags。 ## 17. 推荐决策 1. 采用`TagBinding`作为唯一canonical tag assignment;`_tags`只能是迁移期effective cache。 2. Tag仅表达用户/业务域的unary categorical semantics。 3. `wire.outer`、`edge.boundary`迁移到Current BRep properties/relations。 4. `op.*`、`origin.*`、preserved/modified/generated迁移到Operation/Topology Evolution Graph。 5. 几何类型、方向、数值和pattern ordinal迁移到typed properties,不再自动materialize为tag。 6. Downward propagation默认关闭并在每个binding中显式保存;不再按prefix复制字符串。 7. `effective = local union inherited`;lineage保持独立scope并依赖完整Change evidence。 8. Lineage默认只允许`CONTINUATION`/`FRAGMENT`;`BOUNDARY`、`INTERSECTION`、`MERGE`和`REPLACEMENT`不自动继承semantic role。 9. Auto semantics使用versioned、evidence-gated、三值规则;`UNKNOWN`是一等结果。 10. 立即禁止unmatched => generated、enumeration => semantic role、geometry guess => proven history。 11. 所有tag target复用`cadql.select(...)`和Query IR,不建设第二套selector或anchor API。 12. Replay和translator必须保存并解释assignment intent、producer、scope、evidence和drift,不能只搬运最终字符串集合。 这套策略的核心不是让auto tagging覆盖更多实体,而是让每个可查询semantic都有明确作者、目标意图、适用scope、证明依据和失效规则。CADQL负责查询这些事实;Current BRep Graph和Topology Evolution Graph负责证明结构与历史;TagBinding只负责承载真正的semantic assertion。