From 64767705f153b4d3c86975a02c188bbd7a10fc32 Mon Sep 17 00:00:00 2001 From: Paul Fremantle Date: Sun, 4 Oct 2026 03:55:53 +0100 Subject: [PATCH 1/2] Guard CTC01 source PMI on pattern members in Sheet scripts --- tests/test_sheet_emit.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/test_sheet_emit.py b/tests/test_sheet_emit.py index 4bea73f3..2cd1ec01 100644 --- a/tests/test_sheet_emit.py +++ b/tests/test_sheet_emit.py @@ -572,15 +572,31 @@ def test_step_seam_emits_ap242_pmi_as_sheet_dimensions(self, tmp_path): ast.parse(src) # `measured_dimension` since #873: a generated script must not emit the # transitional overload, or every regenerated AP242 script arrives deprecated. - # Five member-specific tolerances cover only part of recognised patterns, so they - # remain explicit; two other diameter tolerances enrich independent holes. - assert src.count("sheet.measured_dimension(") == 6 + # Five source tolerances belong to individual pattern members (#2172), rather + # than separate measured dimensions that would repeat their bore callouts. + # The independent holes keep their fluent tolerances, and the angular PMI + # remains the one explicit measured dimension. + assert src.count("sheet.measured_dimension(") == 1 + pattern1 = next( + line for line in src.splitlines() if line.startswith("pattern1 = sheet.pattern(") + ) + pattern2 = next( + line for line in src.splitlines() if line.startswith("pattern2 = sheet.pattern(") + ) + assert pattern1.count("ToleranceDecoration(") == 1 + assert pattern2.count("ToleranceDecoration(") == 4 + assert "source_ids=('dimension:0:1:4:29',)" in pattern1 + assert all( + f"source_ids=('dimension:0:1:4:{member}',)" in pattern2 for member in (21, 22, 25, 26) + ) + assert src.count('sheet.dimension(pattern1, "bore.diameter.member_') == 4 + assert src.count('sheet.dimension(pattern2, "bore.diameter.member_') == 4 assert ( sum(".tolerance(" in line for line in src.splitlines() if " = sheet.hole(" in line) == 2 ) - assert src.count("lower_bound=34.8, upper_bound=35.2") == 2 - assert "source_id='dimension:0:1:4:29'" in src + assert pattern2.count("limit_bounds=(34.8, 35.2)") == 2 + assert "source_id='dimension:0:1:4:17'" in src # The transitional MEASURED overload (`dimension(kind=…, value=…)`), not the # referential verb: a regenerated AP242 script must not arrive pre-deprecated (#873). # Bare `sheet.dimension(` stopped meaning that when #938 made every script mirror the From 658ed1f9171f874ae09070f1cafeb62bd9eea235 Mon Sep 17 00:00:00 2001 From: Paul Fremantle Date: Sun, 4 Oct 2026 05:06:28 +0100 Subject: [PATCH 2/2] Keep CTC-01 lane canary bound to source declarations --- .../test_dimension_lane_canary_issue_1757.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/test_dimension_lane_canary_issue_1757.py b/tests/test_dimension_lane_canary_issue_1757.py index 27e41743..3c235977 100644 --- a/tests/test_dimension_lane_canary_issue_1757.py +++ b/tests/test_dimension_lane_canary_issue_1757.py @@ -5,6 +5,7 @@ import hashlib import json import os +import re import subprocess import sys from pathlib import Path @@ -28,6 +29,10 @@ def _run(script: Path, trace: Path) -> dict[str, Any]: trace.mkdir() environment = os.environ.copy() environment["DRAFTWRIGHT_TRACE"] = str(trace) + source_root = str(Path(__file__).resolve().parents[1] / "src") + environment["PYTHONPATH"] = os.pathsep.join( + part for part in (source_root, environment.get("PYTHONPATH", "")) if part + ) result = subprocess.run( [sys.executable, str(script)], cwd=script.parent, @@ -80,21 +85,23 @@ def test_ctc01_feature_relative_lanes_preserve_clear_slot_widths(tmp_path) -> No name: next(line for line in generated_source.splitlines() if line.startswith(f"{name} = ")) for name in ("slot1", "slot2", "control_frame1", "control_frame6") } + identities = {} + for name, line in identified.items(): + matches = re.findall(r"\.identify\('(declaration:\d+)'", line) + assert len(matches) == 1, f"{name} must have one declaration identity" + identities[name] = matches[0] assert "width=40, length=120" in identified["slot1"] - assert ".identify('declaration:5'" in identified["slot1"] assert "width=50, length=100" in identified["slot2"] - assert ".identify('declaration:6'" in identified["slot2"] + assert len(set(identities.values())) == len(identities) assert "source_id='geometric_tolerance:0:1:4:1'" in identified["control_frame1"] - assert ".identify('declaration:58'" in identified["control_frame1"] assert "source_id='geometric_tolerance:0:1:4:20'" in identified["control_frame6"] - assert ".identify('declaration:63'" in identified["control_frame6"] side_prefix = tmp_path / "side" side_script = _script_variant( generated_source, generated_prefix, side_prefix, - 'sheet.layout_override("declaration:58", side="above")\n' - 'sheet.layout_override("declaration:63", side="below")', + f'sheet.layout_override("{identities["control_frame1"]}", side="above")\n' + f'sheet.layout_override("{identities["control_frame6"]}", side="below")', ) baseline = _run(side_script, tmp_path / "side-trace") @@ -103,8 +110,8 @@ def test_ctc01_feature_relative_lanes_preserve_clear_slot_widths(tmp_path) -> No side_script.read_text(encoding="utf-8"), side_prefix, lane_prefix, - 'sheet.layout_override("declaration:5", parameter="slot_width.length", lane=3)\n' - 'sheet.layout_override("declaration:6", parameter="slot_width.length", lane=4)', + f'sheet.layout_override("{identities["slot1"]}", parameter="slot_width.length", lane=3)\n' + f'sheet.layout_override("{identities["slot2"]}", parameter="slot_width.length", lane=4)', ) candidate = _run(lane_script, tmp_path / "lane-trace") @@ -129,7 +136,7 @@ def test_ctc01_feature_relative_lanes_preserve_clear_slot_widths(tmp_path) -> No assert _annotation(baseline, name)["semantic"] == _annotation(candidate, name)["semantic"] assert candidate["drawing"]["layout"]["overrides"][-2:] == [ { - "declaration_id": "declaration:5", + "declaration_id": identities["slot1"], "parameter_id": "slot_width.length", "control": "lane", "authored_value": 3, @@ -138,7 +145,7 @@ def test_ctc01_feature_relative_lanes_preserve_clear_slot_widths(tmp_path) -> No "status": "applied", }, { - "declaration_id": "declaration:6", + "declaration_id": identities["slot2"], "parameter_id": "slot_width.length", "control": "lane", "authored_value": 4, @@ -157,8 +164,8 @@ def test_ctc01_feature_relative_lanes_preserve_clear_slot_widths(tmp_path) -> No candidate, expected_requirements=expected, ) - assert comparison["pareto"]["relation"] == "equivalent" - assert comparison["pareto"]["improved_axes"] == [] + assert comparison["pareto"]["relation"] == "dominates" + assert comparison["pareto"]["improved_axes"] == ["legibility"] assert comparison["pareto"]["regressed_axes"] == [] assert comparison["axes"]["requirements"]["relation"] == "unchanged" assert comparison["axes"]["completeness"]["relation"] == "unchanged"