feat: 接入 SCDM 优先编辑闭环并支持阵列局部间距
接入 SCDM probe/edit/cache/校验链路,增强孔组、阵列、关系式和参数表交互。 支持阵列相邻段间距、移动意图切换、结果回滚校验,并补充对应回归脚本。
This commit is contained in:
@@ -44,6 +44,29 @@ def _hole(object_id: str, face_id: int, *, diameter: float, center: tuple[float,
|
||||
}
|
||||
|
||||
|
||||
def _feature(
|
||||
object_id: str,
|
||||
object_type: str,
|
||||
face_id: int,
|
||||
*,
|
||||
center: tuple[float, float, float],
|
||||
capability_key: str,
|
||||
) -> dict[str, object]:
|
||||
return {
|
||||
"objectId": object_id,
|
||||
"objectType": object_type,
|
||||
"geometrySignature": {
|
||||
"objectType": object_type,
|
||||
"faceIds": [face_id],
|
||||
"center": list(center),
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
},
|
||||
"capabilities": [
|
||||
{"key": capability_key, "currentValue": list(center)},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _cache(*objects: dict[str, object]) -> dict[str, object]:
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
@@ -128,6 +151,419 @@ def main() -> int:
|
||||
position_check = check_scdm_target(position_after["objects"][0], capability_key="hole.position", expected_target=[2.0, 1.0, 6.0])
|
||||
_assert(position_check.get("ok") is True, f"position target should pass: {position_check}")
|
||||
|
||||
before_slot = _cache(_feature("slot:30", "slot", 30, center=(1.0, 2.0, 3.0), capability_key="slot.position"))
|
||||
after_slot = _cache(_feature("slot:40", "slot", 40, center=(1.0, 2.0, 6.0), capability_key="slot.position"))
|
||||
slot_before_signature = before_slot["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
slot_match = match_scdm_object_by_signature(slot_before_signature, after_slot, capability_key="slot.position")
|
||||
_assert(slot_match.get("status") == "unique", f"moved slot should match without old center lock: {slot_match}")
|
||||
slot_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=slot_before_signature,
|
||||
before_cache=before_slot,
|
||||
after_cache=after_slot,
|
||||
capability_key="slot.position",
|
||||
expected_target=[1.0, 2.0, 6.0],
|
||||
edited_object_id="slot:30",
|
||||
)
|
||||
_assert(slot_ok.get("ok") is True, f"slot.position result should validate target center: {slot_ok}")
|
||||
_assert(slot_ok.get("targetCheck", {}).get("ok") is True, f"slot target should be checked: {slot_ok}")
|
||||
before_slot_width = _cache(
|
||||
{
|
||||
"objectId": "slot:30",
|
||||
"objectType": "slot",
|
||||
"geometrySignature": {
|
||||
"objectType": "slot",
|
||||
"faceIds": [30],
|
||||
"center": [1.0, 2.0, 3.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"width": 2.0,
|
||||
},
|
||||
"capabilities": [{"key": "slot.width", "currentValue": 2.0}],
|
||||
}
|
||||
)
|
||||
after_slot_width = _cache(
|
||||
{
|
||||
"objectId": "slot:40",
|
||||
"objectType": "slot",
|
||||
"geometrySignature": {
|
||||
"objectType": "slot",
|
||||
"faceIds": [40],
|
||||
"center": [1.0, 2.0, 3.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"width": 2.5,
|
||||
},
|
||||
"capabilities": [{"key": "slot.width", "currentValue": 2.5}],
|
||||
}
|
||||
)
|
||||
slot_width_before_signature = before_slot_width["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
slot_width_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=slot_width_before_signature,
|
||||
before_cache=before_slot_width,
|
||||
after_cache=after_slot_width,
|
||||
capability_key="slot.width",
|
||||
expected_target=2.5,
|
||||
edited_object_id="slot:30",
|
||||
)
|
||||
_assert(slot_width_ok.get("ok") is True, f"slot.width result should validate target width: {slot_width_ok}")
|
||||
slot_width_mismatch = check_scdm_target(after_slot_width["objects"][0], capability_key="slot.width", expected_target=2.1) # type: ignore[index]
|
||||
_assert(slot_width_mismatch.get("ok") is False and slot_width_mismatch.get("reason") == "target-mismatch", f"slot.width mismatch should fail: {slot_width_mismatch}")
|
||||
before_slot_depth = _cache(
|
||||
{
|
||||
"objectId": "slot:31",
|
||||
"objectType": "slot",
|
||||
"geometrySignature": {
|
||||
"objectType": "slot",
|
||||
"faceIds": [31],
|
||||
"center": [1.0, 2.0, 3.0],
|
||||
"depth": 1.5,
|
||||
"depthAxis": [0.0, 0.0, -1.0],
|
||||
},
|
||||
"capabilities": [{"key": "slot.depth", "currentValue": 1.5}],
|
||||
}
|
||||
)
|
||||
after_slot_depth = _cache(
|
||||
{
|
||||
"objectId": "slot:41",
|
||||
"objectType": "slot",
|
||||
"geometrySignature": {
|
||||
"objectType": "slot",
|
||||
"faceIds": [41],
|
||||
"center": [1.0, 2.0, 3.0],
|
||||
"depth": 2.0,
|
||||
"depthAxis": [0.0, 0.0, -1.0],
|
||||
},
|
||||
"capabilities": [{"key": "slot.depth", "currentValue": 2.0}],
|
||||
}
|
||||
)
|
||||
slot_depth_before_signature = before_slot_depth["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
slot_depth_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=slot_depth_before_signature,
|
||||
before_cache=before_slot_depth,
|
||||
after_cache=after_slot_depth,
|
||||
capability_key="slot.depth",
|
||||
expected_target=2.0,
|
||||
edited_object_id="slot:31",
|
||||
)
|
||||
_assert(slot_depth_ok.get("ok") is True, f"slot.depth result should validate target depth: {slot_depth_ok}")
|
||||
slot_depth_mismatch = check_scdm_target(after_slot_depth["objects"][0], capability_key="slot.depth", expected_target=1.5) # type: ignore[index]
|
||||
_assert(slot_depth_mismatch.get("ok") is False and slot_depth_mismatch.get("reason") == "target-mismatch", f"slot.depth mismatch should fail: {slot_depth_mismatch}")
|
||||
|
||||
boss_after = _feature("boss:50", "cylindrical_boss", 50, center=(3.0, 0.0, 2.0), capability_key="boss.position")
|
||||
boss_check = check_scdm_target(boss_after, capability_key="boss.position", expected_target=[3.0, 0.0, 2.0])
|
||||
_assert(boss_check.get("ok") is True, f"boss.position target should pass: {boss_check}")
|
||||
boss_mismatch = check_scdm_target(boss_after, capability_key="boss.position", expected_target=[4.0, 0.0, 2.0])
|
||||
_assert(boss_mismatch.get("ok") is False and boss_mismatch.get("reason") == "target-mismatch", f"boss.position mismatch should fail: {boss_mismatch}")
|
||||
before_boss_height = _cache(
|
||||
{
|
||||
"objectId": "boss:50",
|
||||
"objectType": "cylindrical_boss",
|
||||
"geometrySignature": {
|
||||
"objectType": "cylindrical_boss",
|
||||
"faceIds": [50, 51, 52],
|
||||
"center": [0.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"diameter": 3.0,
|
||||
"height": 4.0,
|
||||
},
|
||||
"capabilities": [{"key": "boss.height", "currentValue": 4.0}],
|
||||
}
|
||||
)
|
||||
after_boss_height = _cache(
|
||||
{
|
||||
"objectId": "boss:55",
|
||||
"objectType": "cylindrical_boss",
|
||||
"geometrySignature": {
|
||||
"objectType": "cylindrical_boss",
|
||||
"faceIds": [55, 56, 57],
|
||||
"center": [0.0, 0.0, 2.75],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"diameter": 3.0,
|
||||
"height": 5.5,
|
||||
},
|
||||
"capabilities": [{"key": "boss.height", "currentValue": 5.5}],
|
||||
}
|
||||
)
|
||||
boss_height_signature = before_boss_height["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
boss_height_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=boss_height_signature,
|
||||
before_cache=before_boss_height,
|
||||
after_cache=after_boss_height,
|
||||
capability_key="boss.height",
|
||||
expected_target=5.5,
|
||||
edited_object_id="boss:50",
|
||||
)
|
||||
_assert(boss_height_ok.get("ok") is True, f"boss.height result should validate target height: {boss_height_ok}")
|
||||
boss_height_mismatch = check_scdm_target(after_boss_height["objects"][0], capability_key="boss.height", expected_target=4.5) # type: ignore[index]
|
||||
_assert(boss_height_mismatch.get("ok") is False and boss_height_mismatch.get("reason") == "target-mismatch", f"boss.height mismatch should fail: {boss_height_mismatch}")
|
||||
before_boss_diameter = _cache(
|
||||
{
|
||||
"objectId": "boss:60",
|
||||
"objectType": "cylindrical_boss",
|
||||
"geometrySignature": {
|
||||
"objectType": "cylindrical_boss",
|
||||
"faceIds": [60, 61, 62],
|
||||
"center": [0.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"diameter": 3.0,
|
||||
"height": 4.0,
|
||||
},
|
||||
"capabilities": [{"key": "boss.diameter", "currentValue": 3.0}],
|
||||
}
|
||||
)
|
||||
after_boss_diameter = _cache(
|
||||
{
|
||||
"objectId": "boss:63",
|
||||
"objectType": "cylindrical_boss",
|
||||
"geometrySignature": {
|
||||
"objectType": "cylindrical_boss",
|
||||
"faceIds": [63, 64, 65],
|
||||
"center": [0.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"diameter": 4.5,
|
||||
"height": 4.0,
|
||||
},
|
||||
"capabilities": [{"key": "boss.diameter", "currentValue": 4.5}],
|
||||
}
|
||||
)
|
||||
boss_diameter_signature = before_boss_diameter["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
boss_diameter_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=boss_diameter_signature,
|
||||
before_cache=before_boss_diameter,
|
||||
after_cache=after_boss_diameter,
|
||||
capability_key="boss.diameter",
|
||||
expected_target=4.5,
|
||||
edited_object_id="boss:60",
|
||||
)
|
||||
_assert(boss_diameter_ok.get("ok") is True, f"boss.diameter result should validate target diameter: {boss_diameter_ok}")
|
||||
boss_diameter_mismatch = check_scdm_target(after_boss_diameter["objects"][0], capability_key="boss.diameter", expected_target=3.5) # type: ignore[index]
|
||||
_assert(boss_diameter_mismatch.get("ok") is False and boss_diameter_mismatch.get("reason") == "target-mismatch", f"boss.diameter mismatch should fail: {boss_diameter_mismatch}")
|
||||
|
||||
before_round_radius = _cache(
|
||||
{
|
||||
"objectId": "round:70",
|
||||
"objectType": "round",
|
||||
"geometrySignature": {
|
||||
"objectType": "round",
|
||||
"faceIds": [70],
|
||||
"center": [1.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"radius": 0.5,
|
||||
"isConstantRound": True,
|
||||
},
|
||||
"capabilities": [{"key": "round.radius", "currentValue": 0.5}],
|
||||
}
|
||||
)
|
||||
after_round_radius = _cache(
|
||||
{
|
||||
"objectId": "round:71",
|
||||
"objectType": "round",
|
||||
"geometrySignature": {
|
||||
"objectType": "round",
|
||||
"faceIds": [71],
|
||||
"center": [1.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"radius": 0.75,
|
||||
"isConstantRound": True,
|
||||
},
|
||||
"capabilities": [{"key": "round.radius", "currentValue": 0.75}],
|
||||
}
|
||||
)
|
||||
round_radius_signature = before_round_radius["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
round_radius_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=round_radius_signature,
|
||||
before_cache=before_round_radius,
|
||||
after_cache=after_round_radius,
|
||||
capability_key="round.radius",
|
||||
expected_target=0.75,
|
||||
edited_object_id="round:70",
|
||||
)
|
||||
_assert(round_radius_ok.get("ok") is True, f"round.radius result should validate target radius: {round_radius_ok}")
|
||||
round_radius_mismatch = check_scdm_target(after_round_radius["objects"][0], capability_key="round.radius", expected_target=0.5) # type: ignore[index]
|
||||
_assert(round_radius_mismatch.get("ok") is False and round_radius_mismatch.get("reason") == "target-mismatch", f"round.radius mismatch should fail: {round_radius_mismatch}")
|
||||
|
||||
before_chamfer_distance = _cache(
|
||||
{
|
||||
"objectId": "chamfer:80",
|
||||
"objectType": "chamfer",
|
||||
"geometrySignature": {
|
||||
"objectType": "chamfer",
|
||||
"faceIds": [80],
|
||||
"center": [2.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"distance": 0.8,
|
||||
"isEqualDistanceChamfer": True,
|
||||
},
|
||||
"capabilities": [{"key": "chamfer.distance", "currentValue": 0.8}],
|
||||
}
|
||||
)
|
||||
after_chamfer_distance = _cache(
|
||||
{
|
||||
"objectId": "chamfer:81",
|
||||
"objectType": "chamfer",
|
||||
"geometrySignature": {
|
||||
"objectType": "chamfer",
|
||||
"faceIds": [81],
|
||||
"center": [2.0, 0.0, 2.0],
|
||||
"axis": [0.0, 0.0, 1.0],
|
||||
"distance": 1.2,
|
||||
"isEqualDistanceChamfer": True,
|
||||
},
|
||||
"capabilities": [{"key": "chamfer.distance", "currentValue": 1.2}],
|
||||
}
|
||||
)
|
||||
chamfer_distance_signature = before_chamfer_distance["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
chamfer_distance_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=chamfer_distance_signature,
|
||||
before_cache=before_chamfer_distance,
|
||||
after_cache=after_chamfer_distance,
|
||||
capability_key="chamfer.distance",
|
||||
expected_target=1.2,
|
||||
edited_object_id="chamfer:80",
|
||||
)
|
||||
_assert(chamfer_distance_ok.get("ok") is True, f"chamfer.distance result should validate target distance: {chamfer_distance_ok}")
|
||||
chamfer_distance_mismatch = check_scdm_target(after_chamfer_distance["objects"][0], capability_key="chamfer.distance", expected_target=0.8) # type: ignore[index]
|
||||
_assert(chamfer_distance_mismatch.get("ok") is False and chamfer_distance_mismatch.get("reason") == "target-mismatch", f"chamfer.distance mismatch should fail: {chamfer_distance_mismatch}")
|
||||
|
||||
before_pattern_spacing = _cache(
|
||||
{
|
||||
"objectId": "pattern:holes",
|
||||
"objectType": "linear_pattern",
|
||||
"geometrySignature": {
|
||||
"objectType": "linear_pattern",
|
||||
"faceIds": [85, 87, 89],
|
||||
"center": [5.0, 0.0, 0.0],
|
||||
"axis": [1.0, 0.0, 0.0],
|
||||
"spacing": 5.0,
|
||||
"pitch": 5.0,
|
||||
"instanceCount": 3,
|
||||
"instanceCenters": [[0.0, 0.0, 0.0], [5.0, 0.0, 0.0], [10.0, 0.0, 0.0]],
|
||||
},
|
||||
"capabilities": [{"key": "pattern.spacing", "currentValue": 5.0}],
|
||||
}
|
||||
)
|
||||
after_pattern_spacing = _cache(
|
||||
{
|
||||
"objectId": "pattern:holes-new",
|
||||
"objectType": "linear_pattern",
|
||||
"geometrySignature": {
|
||||
"objectType": "linear_pattern",
|
||||
"faceIds": [90, 91, 92],
|
||||
"center": [7.5, 0.0, 0.0],
|
||||
"axis": [1.0, 0.0, 0.0],
|
||||
"spacing": 7.5,
|
||||
"pitch": 7.5,
|
||||
"instanceCount": 3,
|
||||
"instanceCenters": [[0.0, 0.0, 0.0], [7.5, 0.0, 0.0], [15.0, 0.0, 0.0]],
|
||||
},
|
||||
"capabilities": [{"key": "pattern.spacing", "currentValue": 7.5}],
|
||||
}
|
||||
)
|
||||
pattern_spacing_before_signature = before_pattern_spacing["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
pattern_spacing_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=pattern_spacing_before_signature,
|
||||
before_cache=before_pattern_spacing,
|
||||
after_cache=after_pattern_spacing,
|
||||
capability_key="pattern.spacing",
|
||||
expected_target=7.5,
|
||||
edited_object_id="pattern:holes",
|
||||
)
|
||||
_assert(pattern_spacing_ok.get("ok") is True, f"pattern.spacing result should validate target spacing: {pattern_spacing_ok}")
|
||||
pattern_spacing_mismatch = check_scdm_target(after_pattern_spacing["objects"][0], capability_key="pattern.spacing", expected_target=5.0) # type: ignore[index]
|
||||
_assert(pattern_spacing_mismatch.get("ok") is False and pattern_spacing_mismatch.get("reason") == "target-mismatch", f"pattern.spacing mismatch should fail: {pattern_spacing_mismatch}")
|
||||
before_pattern_segment = _cache(
|
||||
{
|
||||
"objectId": "pattern:holes",
|
||||
"objectType": "linear_pattern",
|
||||
"geometrySignature": {
|
||||
"objectType": "linear_pattern",
|
||||
"faceIds": [85, 87, 89],
|
||||
"center": [5.0, 0.0, 0.0],
|
||||
"axis": [1.0, 0.0, 0.0],
|
||||
"spacing": 5.0,
|
||||
"pitch": 5.0,
|
||||
"segmentIndex": 1,
|
||||
"movingSide": "after",
|
||||
"patternInstances": [
|
||||
{"sourceObjectId": "a", "center": [0.0, 0.0, 0.0], "faceIds": [85]},
|
||||
{"sourceObjectId": "b", "center": [5.0, 0.0, 0.0], "faceIds": [87]},
|
||||
{"sourceObjectId": "c", "center": [10.0, 0.0, 0.0], "faceIds": [89]},
|
||||
],
|
||||
},
|
||||
"capabilities": [{"key": "pattern.segment_spacing", "currentValue": 5.0}],
|
||||
},
|
||||
_hole("hole:unrelated", 999, diameter=1.0, center=(100.0, 0.0, 0.0)),
|
||||
)
|
||||
after_pattern_segment = _cache(_hole("hole:unrelated-new", 999, diameter=1.0, center=(100.0, 0.0, 0.0)))
|
||||
pattern_segment_ok = validate_scdm_edit_result(
|
||||
{
|
||||
"ok": True,
|
||||
"output_step": str(output_step),
|
||||
"result": {
|
||||
"applied": {
|
||||
"segmentSpacing": 6.5,
|
||||
"targetSpacing": 6.5,
|
||||
"segmentIndex": 1,
|
||||
"spacingMode": "segment_after",
|
||||
}
|
||||
},
|
||||
},
|
||||
before_signature=before_pattern_segment["objects"][0]["geometrySignature"], # type: ignore[index]
|
||||
before_cache=before_pattern_segment,
|
||||
after_cache=after_pattern_segment,
|
||||
capability_key="pattern.segment_spacing",
|
||||
expected_target=6.5,
|
||||
edited_object_id="pattern:holes",
|
||||
)
|
||||
_assert(
|
||||
pattern_segment_ok.get("ok") is True
|
||||
and pattern_segment_ok.get("targetCheck", {}).get("spacingMode") == "segment_after",
|
||||
f"pattern.segment_spacing should validate from the applied edit result even when the old uniform pattern no longer matches: {pattern_segment_ok}",
|
||||
)
|
||||
pattern_segment_mismatch = validate_scdm_edit_result(
|
||||
{
|
||||
"ok": True,
|
||||
"output_step": str(output_step),
|
||||
"applied": {"segmentSpacing": 6.0, "segmentIndex": 1, "spacingMode": "segment_after"},
|
||||
},
|
||||
before_signature=before_pattern_segment["objects"][0]["geometrySignature"], # type: ignore[index]
|
||||
before_cache=before_pattern_segment,
|
||||
after_cache=after_pattern_segment,
|
||||
capability_key="pattern.segment_spacing",
|
||||
expected_target=6.5,
|
||||
edited_object_id="pattern:holes",
|
||||
)
|
||||
_assert(
|
||||
pattern_segment_mismatch.get("ok") is False and pattern_segment_mismatch.get("reason") == "target-mismatch",
|
||||
f"pattern.segment_spacing mismatch should fail from the applied edit result: {pattern_segment_mismatch}",
|
||||
)
|
||||
shell_thickness_ok = check_scdm_target(
|
||||
{
|
||||
"objectType": "thin_wall",
|
||||
"geometrySignature": {"objectType": "thin_wall", "thickness": 1.6},
|
||||
"capabilities": [{"key": "shell.thickness", "currentValue": 1.6}],
|
||||
},
|
||||
capability_key="shell.thickness",
|
||||
expected_target=1.6,
|
||||
)
|
||||
_assert(shell_thickness_ok.get("ok") is True, f"shell.thickness result should validate target thickness: {shell_thickness_ok}")
|
||||
shell_thickness_mismatch = check_scdm_target(
|
||||
{
|
||||
"objectType": "thin_wall",
|
||||
"geometrySignature": {"objectType": "thin_wall", "thickness": 1.6},
|
||||
"capabilities": [{"key": "shell.thickness", "currentValue": 1.6}],
|
||||
},
|
||||
capability_key="shell.thickness",
|
||||
expected_target=2.0,
|
||||
)
|
||||
_assert(shell_thickness_mismatch.get("ok") is False and shell_thickness_mismatch.get("reason") == "target-mismatch", f"shell.thickness mismatch should fail: {shell_thickness_mismatch}")
|
||||
|
||||
summary_before = _cache_with_summary(
|
||||
{"bodyCount": 13, "objectCount": 554, "faceCount": 158, "edgeCount": 396},
|
||||
_hole("hole:85", 85, diameter=0.5, center=(0.5, 1.0, 9.5)),
|
||||
@@ -154,6 +590,57 @@ def main() -> int:
|
||||
summary_fill = check_scdm_summary_delta(summary_before, summary_after_bad, capability_key="feature.fill")
|
||||
_assert(summary_fill.get("ok") is None and summary_fill.get("reason") == "skipped-command-feature", f"fill should skip summary count guard: {summary_fill}")
|
||||
|
||||
fill_before = _cache(
|
||||
_hole("hole:85", 85, diameter=0.5, center=(0.5, 1.0, 9.5)),
|
||||
_hole("hole:87", 87, diameter=0.5, center=(2.0, 1.0, 9.5)),
|
||||
)
|
||||
fill_after = _cache(_hole("hole:87", 87, diameter=0.5, center=(2.0, 1.0, 9.5)))
|
||||
fill_signature = fill_before["objects"][0]["geometrySignature"] # type: ignore[index]
|
||||
fill_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=fill_signature,
|
||||
before_cache=fill_before,
|
||||
after_cache=fill_after,
|
||||
capability_key="feature.fill",
|
||||
edited_object_id="hole:85",
|
||||
)
|
||||
_assert(fill_ok.get("ok") is True, f"feature.fill should pass when the edited feature disappears: {fill_ok}")
|
||||
_assert(fill_ok.get("removalCheck", {}).get("ok") is True, f"feature.fill should record removal evidence: {fill_ok}")
|
||||
fill_still_present = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=fill_signature,
|
||||
before_cache=fill_before,
|
||||
after_cache=fill_before,
|
||||
capability_key="feature.fill",
|
||||
edited_object_id="hole:85",
|
||||
)
|
||||
_assert(
|
||||
fill_still_present.get("ok") is False and fill_still_present.get("reason") == "feature-still-present",
|
||||
f"feature.fill should fail when the edited feature still matches: {fill_still_present}",
|
||||
)
|
||||
fill_no_cache = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=fill_signature,
|
||||
capability_key="feature.fill",
|
||||
)
|
||||
_assert(
|
||||
fill_no_cache.get("ok") is False and fill_no_cache.get("reason") == "removal-check-unavailable",
|
||||
f"feature.fill should require a new cache for removal verification: {fill_no_cache}",
|
||||
)
|
||||
|
||||
round_before = _cache(_feature("round:60", "round", 60, center=(1.0, 0.0, 2.0), capability_key="feature.delete_round_or_chamfer"))
|
||||
round_after = _cache(_feature("hole:85", "hole", 85, center=(5.0, 0.0, 2.0), capability_key="hole.diameter"))
|
||||
round_delete_ok = validate_scdm_edit_result(
|
||||
{"ok": True, "output_step": str(output_step)},
|
||||
before_signature=round_before["objects"][0]["geometrySignature"], # type: ignore[index]
|
||||
before_cache=round_before,
|
||||
after_cache=round_after,
|
||||
capability_key="feature.delete_round_or_chamfer",
|
||||
edited_object_id="round:60",
|
||||
)
|
||||
_assert(round_delete_ok.get("ok") is True, f"round/chamfer delete should pass when the edited feature disappears: {round_delete_ok}")
|
||||
_assert(round_delete_ok.get("targetCheck", {}).get("reason") == "removed", f"round/chamfer delete should use removal target check: {round_delete_ok}")
|
||||
|
||||
ambiguous_after = _cache(
|
||||
_hole("hole:100", 100, diameter=0.75, center=(0.5, 1.0, 9.5)),
|
||||
_hole("hole:101", 101, diameter=0.75, center=(0.5, 1.0, 9.5)),
|
||||
|
||||
Reference in New Issue
Block a user