Two ways the six focuses turned a reader guess into a gate-failing error:
- The "no P&ID content" guard was `not graph["nodes"]`, but build_graph
(include_foreign=True) files ANY INSERT as an `unknown_block` at 0.3 the
moment a plain LINE's end lands on its box. A bolt with a note leader on a
purely mechanical sheet became a P&ID node, the leader a foreign edge, and
its free end a `pid_dangling_line` error — so drawing_finalize (focus=None)
failed a sheet the seven mechanical focuses pass. Now `has_pid_content`
needs a recognised node (catalog / xdata / heuristic); an `unknown_block`
is never tagged or scored, and `_dangling` only reports an edge that is a
P&ID line by evidence (a class from XDATA or a P&ID layer, or an end on a
recognised node) — on a real P&ID the same leader is not dangling either.
- `_incompatible` read `port["kind"]` off inferred ports, which the graph
hard-codes to "process" for every foreign block. A signal line correctly
drawn to a CTO control valve's actuator was an error whose hint said to
redraw the control signal as a process line. Inferred ports are skipped;
only a declared (catalogue / XDATA) port kind is judged.
Tests: both repros as regressions (unit + the server drawing_finalize gate),
a foreign GATE_VALVE whose dangling line is still reported (the heuristic
path spec §9.3 pins is not silenced), a note leader on a real P&ID, and the
spec's own negative — all six return [] on the README gear sheet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd