bench: matrix v5 adds mech_assembly; the correctness suite grows with the mechanical and sheet checks

The evidence for tracks B and G, in the shape tracks A and E used.

- benchmarks/tasks_v5.py: 18 tasks, DEFAULT_MATRIX = "v5"; v4, v3 and v2 stay
  addressable so an earlier report reproduces exactly rather than being only
  described. mech_assembly is roadmap criterion 3 - a flange-coupling sheet
  built from the part model and the sheet standard, through the code the
  tools call (apply_titleblock, draw_part/add_view/dimension_part,
  insert_std_part, extract_records -> draw_bom_table, add_balloon), gated on
  two numbers that cannot be negotiated: drawing_critique(focus=None) returns
  zero issues and the finalize score is at least 90 (it scores 100, 18/18 on
  the v5 matrix). The adapter returns the focuses that fired, so a
  regression names itself, and a test drops one balloon to prove the gate
  fails and names mech_bom_balloon_mismatch.
- Seven correctness checks (32 -> 39). mech_section_hatch_area is the one
  that cannot be faked: a sleeve of length 60, outside diameter 40 and bore
  20 cuts two faces totalling 1200 mm^2 exactly, read back out of the drawing
  through entity_measure. sheet_frame_iso5457_a3 pins the A3 frame (20,10 to
  410,287 inside the 420 x 297 trimmed sheet); mech_iso286_on_dimension pins
  40 H7 at +0.025/0 through the layout; std_part_iso4014_m12 pins s = 18 and
  k = 7.5 by value; mech_thread_unrepresented_is_caught is the seventh: a
  quality gate that never fires is not a gate.
- scripts/smoke_mech_com.py executed every new COM path once on AutoCAD 2026
  (25.1s): frame, title block, a sectioned shaft with 12 dimensions, an ISO
  4014 block with 1 ACADMCP_MECH chunk, a weld symbol, the parts list read off
  the drawing, a linked balloon, an xref attached / listed / detached, a
  31687-byte .dwg (AC1032) through ActiveX SaveAs, critique 0 issues, exit 0,
  every scratch document closed. It retries RPC_E_CALL_REJECTED (measured:
  the first call after Documents.Add is refused) and tracks its documents by
  the document list, since SaveAs renames them.
- Defects the evidence found, each fixed with a test that fails on the old
  code:
  * ezdxf dimension_diameter put the text beyond the SECOND chord point;
    ActiveX AddDimDiametric puts it beyond the first (measured live: a d50
    from (x,170) to (x,220), leader 10, text at (x,160)). Headlessly every
    front-view diameter stood on its own chain dimension. ezdxf now follows
    ActiveX.
  * draw_part's anchor goes on MECH; ActiveX refuses entity.Layer on a
    missing layer (measured -0x7ffdfff7 'Key not found'). draw.py ensures
    every target layer before the first entity.
  * bom.extract_records filtered on INSERT; ActiveX names it BLOCKREFERENCE
    (ObjectName AcDbBlockReference) - the live parts list was empty.
  * COM _entity_info reported no dimension text_position: ModelSpace.Item
    narrows to IAcadEntity, which has no TextPosition (measured
    AttributeError); read through _com_unnarrow (TextPosition measured on
    AcDbDiametricDimension, WCS). dim_overlap compared bbox centres and a
    diameter collided with its own bore.
  * pid graph raised KeyError 'start' mid-critique on a LINE whose point read
    AutoCAD refused while busy; such a line is left out.
  * the validator read "ISO 4014 - M12x60" as fake dimension text.
- The published A/B report is re-run over the grown suite rather than
  re-typed (26/39 -> 39/39, 13 fixed, 0 regressed), check_doc_numbers derives
  the task count from the v5 matrix, the token table is remeasured with a
  TOOL_PACKS=core,mech row (193 tools / 61,673 tokens), and the README's
  collected-tests figure is retaken (3990).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-23 13:30:02 +03:00
co-authored by Claude Opus 5.5
parent 3b033033f9
commit 898e775f93
30 changed files with 1355 additions and 50 deletions
+104
View File
@@ -21,6 +21,14 @@ views and UCS, and the live-only group (launch, preferences, document
properties, operator prompts) refused honestly headlessly. Spec:
`docs/superpowers/specs/2026-09-16-v1.6-settings-design.md`.
Tracks B and G of 1.6: the complete mechanical 2D part drawer and the sheet
standard. 25 tools in four new sections — mechanical parts (21), standard
parts (22), mechanical annotation (23) and sheet and delivery (24) — one part
model and one view engine instead of a zoo of per-part generators, a new
`mech` pack, six `mech_*` critique focuses and four new contract members
(`xref_attach`, `xref_manage`, `image_attach`, `drawing_export_dwg`). Spec:
`docs/superpowers/specs/2026-09-22-v1.6-mech-design.md`.
### Added
- **Styles — 10 tools, pack `core`.** `dimstyle_list/create/modify/set_current`,
@@ -74,6 +82,28 @@ properties, operator prompts) refused honestly headlessly. Spec:
`v1.5.1`: **26 / 32 → 32 / 32, six `miss → pass`, zero regressed**.
`scripts/smoke_settings_com.py` runs the track once against the live seat
(record below).
- **Benchmark evidence for tracks B and G.** `benchmarks/tasks_v5.py` (18
tasks, the default matrix; v4 / v3 / v2 stay addressable) adds
`mech_assembly`: a flange-coupling A3 sheet built through the code the tools
call — frame and title block, a hub with a full section and chain
dimensions, a flange with its bolt-circle end view, ISO 4014 bolts and ISO
4032 nuts, the parts list read off the drawing and linked balloons — gated on
`drawing_critique(focus=None)` returning zero issues and a finalize score of
at least 90 (it scores 100); a test drops one balloon and watches the gate
fail by name. The correctness suite grows to 39 with `mech_part_roundtrip`,
`mech_section_hatch_area` (1200 mm² read back out of the drawing),
`mech_iso286_on_dimension`, `mech_thread_unrepresented_is_caught`,
`std_part_iso4014_m12`, `sheet_frame_iso5457_a3` and `bom_balloon_link`.
A/B against `v1.5.1`: **26 / 39 → 39 / 39, thirteen `miss → pass`, zero
regressed**. The token suite measures a `TOOL_PACKS=core,mech` row: 193
tools / 61,673 tokens against 224 / 72,163 by default.
- **Live COM smoke.** `scripts/smoke_mech_com.py` executed once on AutoCAD
2026 (`25.1s (LMS Tech)`): the A3 frame and ISO 7200 title block, a two-step
shaft with a full section and 12 chain dimensions, an ISO 4014 M12x60 block
carrying 1 ACADMCP_MECH chunk, an ISO 7573 parts list read off the drawing
with its linked balloon, an xref attached and detached, and a real
31687-byte `.dwg` written by ActiveX `SaveAs` — `drawing_critique(focus=None)`
returned 0 issues (record below).
- **P&ID — 9 tools, pack `pid`.**
- `pid_symbol_list` / `pid_symbol_insert`: a 43-symbol ISO 10628-2 /
@@ -153,6 +183,33 @@ properties, operator prompts) refused honestly headlessly. Spec:
### Fixed
- **Found by the tracks B + G evidence** (each with a test that fails on the
old code):
- `dimension_diameter` put its text beyond the *second* chord point
headlessly and beyond the *first* on AutoCAD (`AddDimDiametric`'s
ChordPoint, measured: a ⌀50 from (x, 170) to (x, 220) annotated at
(x, 160)). The two engines annotated opposite sides of the same part, and
headlessly every front-view diameter of `mech_part_draw` stood on its own
chain dimension — `dim_overlap` fired on any single-step disk. The
headless engine now follows ActiveX.
- On a live seat `mech_part_draw` died after drawing every view: its anchor
POINT goes on `MECH`, and ActiveX refuses `entity.Layer` on a missing
layer (`Key not found`) where ezdxf creates it. Every mechanical draw
path now ensures its target layers before the first entity.
- `bom_extract` returned nothing live: ActiveX names a block reference
`BLOCKREFERENCE`, and the parts list filtered on `INSERT` only. Both
names are read.
- The live `_entity_info` reported no `text_position` for a dimension (the
entity arrives narrowed to `IAcadEntity`, which has no `TextPosition`),
so `dim_overlap` compared bounding-box centres — a diameter and its bore
read "0.00 mm apart". It is read through the concrete class now.
- `pid_graph` raised `KeyError: 'start'` inside `drawing_critique(focus=None)`
on a LINE whose point read AutoCAD had refused while busy; such a line is
left out instead.
- The validator's `fake_dimension_text` read a standard-part designation
(`ISO 4014 - M12x60`) as a hand-typed dimension, so every parts list
drawn as text cost its sheet a warning. The designation is exempt; a
measurement beside it still flags.
- **Track A hardening** (`tests/test_track_a_hardening.py`; the remaining
review findings are filed with a disposition each in
`docs/analysis/track-a-review-backlog.md`):
@@ -716,6 +773,53 @@ The pick path was executed live earlier in the track (Task 23's measurement:
`GetPoint` answers in WCS while `GetEntity` answers in the current UCS). Each
plot also dropped AutoCAD's `plot.log` into the repo root; it is ignored now.
### Live COM smoke — mechanical parts and the sheet (tracks B + G)
`scripts/smoke_mech_com.py`, AutoCAD 2026 (`25.1s (LMS Tech)`), Windows 11,
2026-09-23, in new documents only (an xref source saved and closed first, then
the sheet) — `apply_layer_set("mech")` → `apply_titleblock` (A3, zones, marks)
→ `draw_part` → `add_view(section)` → `dimension_part` → `insert_std_part` →
`weld_symbol_prims` → `extract_records` → `draw_bom_table` → `add_balloon` →
`xref_attach` / `xref_manage("list")` / `xref_manage("detach")` →
`run_critique(focus=None)` → `drawing_export_dwg("R2018")`, all through the COM
engine, exit 0, every scratch document closed afterwards:
```json
{
"autocad": "25.1s (LMS Tech)",
"part_id": "103",
"views": 2,
"section_omitted": [],
"dimensions": 12,
"bolt_handle": "2F7",
"xdata_chunks": 1,
"parts_list_rows": 1,
"parts_list_representation": "native",
"balloon": "31A",
"xref_attached": "XREF_SOURCE",
"xref_listed": 1,
"xref_detached": true,
"xrefs_after_detach": 0,
"dwg": {
"path": "%TEMP%\\mech_smoke_*\\mech_smoke.dwg",
"exists": true,
"bytes": 31687,
"magic": "AC1032"
},
"critique_issues": 0,
"critique": []
}
```
The first runs found the four live defects listed under **Fixed** (the `MECH`
layer, `BLOCKREFERENCE`, the dimension `text_position`, the refused LINE read)
and one harness fault: the first call after `Documents.Add` can be refused with
`RPC_E_CALL_REJECTED` while the new document initialises, and a refused
cleanup then left the scratch document open. The smoke retries refused calls
for up to 90 s (a refused call was not executed) and tracks its documents by
the document list rather than by an assumed name, because `SaveAs` renames the
document it writes.
## [1.5.1] — 2026-08-06
A patch for three defects found *after* 1.5.0 went to PyPI, two of them by the