Three blocking findings on the refs group, all of them the same shape: a
number or a refusal the caller was entitled to read as measured.
1. ezdxf xref_manage: the victim scan and the `inserts` count both walked
`doc.modelspace()` only, then dropped the definition with
`delete_block(safe=False)` -- the flag that suppresses the in-use check.
An xref shown on a paper-space sheet was reported as 1 insert where the
drawing held 2, and detaching it left that sheet INSERT with no BLOCK
behind it: `ezdxf.recover` reports `FIX 103 UNDEFINED_BLOCK: Deleted
entity INSERT(#34) without required BLOCK definition` and the repo's own
drawing_audit silently deletes the sheet geometry. The scan now walks
`doc.blocks`, which is every entity space (model space, each paper-space
tab, every block definition) and is exactly what ezdxf's own safe check
searches with `doc.query('INSERT[name==...]i')` -- hence the
case-insensitive name match. `delete_block` is now `safe=True`, as the
plan's Interfaces block pins it, so a future miss raises instead of
writing a corrupt file.
2. COM xref_manage(action='list') reported `kind: "attach"` for every row,
a literal constant. Re-measured on this seat's registered type library
(C:\Program Files\Common Files\Autodesk Shared\acax25*.tlb, "AutoCAD
2025 Type Library", installed with AutoCAD 2026): IAcadBlock's xref
members are exactly IsXRef, Path, Name, Reload, Unload, Bind, Detach,
XRefDatabase -- no overlay indicator exists on the interface, so an
overlay was reported to the client as an attachment. The row now says
`kind: None`, the same honesty the adjacent `inserts` field already had,
and the contract plus the tool docstring say so.
3. COM xref_attach had no duplicate-block-name guard, although
contracts/refs.py and the tool docstring both promise the refusal and
ezdxf implements it. Two different part.dwg files, or a re-attach after a
repath, silently re-pointed or half-wrote the definition while the
payload claimed the new path. Added `_block_names(doc)` (Blocks.Count /
Blocks.Item(index) / block.Name -- the members xref_manage already walks)
and the refusal fires before AttachExternalReference is dispatched, with
the same message the headless engine gives. Lower-cased, because AutoCAD
symbol-table names are case-insensitive.
Tests: five new ones, each failing against the unfixed backends -- an xref
shown on Layout1 is counted and detached with the drawing, the detached file
audits clean through ezdxf.recover, delete_block's in-use check is pinned,
the live listing reports kind None (against a fake that models only the
measured members, so it cannot enshrine an invented one), and the live attach
refuses a held name (xref and plain block, either case) without dispatching.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd