Six cuts, zero verdict changes (spec §3; equivalence pinned by units, the
parity suite and the smokes):
- §4.1 the CPU chain of ws_config_set and ws_plan_optimize runs in the
executor; write_lock still serialises writes, only the HA event loop is
freed (2.8 s of blocking per 576-atom write before).
- §4.2 the stored document's violation counts are cached on the runtime by
rev (store.py junction_baseline); a repeated write never re-judges
`previous`. validate_junction_limits takes baseline_counts and returns the
candidate's counts to cache after a successful save.
- §4.3 П3 builds its node index once per check in both mirrors
(289→11 ms TS, 285→~50 ms py).
- §4.5 П4 uses a bucket grid with the threshold as cell size in both
mirrors (104→19 ms TS, 372→44 ms py); pair enumeration switches to
lexicographic order — same verdict set, equivalence pinned against a
brute-force oracle on cell borders.
- §4.6 a document already carrying the current catalogue is judged as-is:
a no-op re-migration cost 815 ms py / 69 ms TS. Legacy documents migrate
exactly as before (the #329 H1 test stays green).
- §4.7 П5 shares one junction-topology pass per check and pays the masonry
union only when multi-wall nodes exist — and the resize path hands over
the preflight's own artifact, so a pointermove never builds the union
twice (4.2 s → 88 ms full candidate on the benchmark grid).
The frontend baseline is cached per (document identity, config epoch): ten
pointermoves make N+1 limit computations, not 2N — pinned by the smoke on a
real pointer gesture.
demo/benchmark_junction_limits.mjs (npm run benchmark:junction-limits) pins
the budgets for both mirrors: TS full candidate ≤100 ms (measured 88), py
warm validate ≤250 ms (measured 45), cold legacy ≤3.5 s — that path is
one-off and lives in the executor.
Issue: #330
User-Visible: yes
П3 measures the WALL, not the catalogue atom: a short filler segment that
compensates a thickness step (owner's fixture, 5 cm = (30-20)/2) is a legal
continuation of a long same-thickness wall, so the rule walks the maximal
collinear run through the shared nodes before judging the length.
Resize stops at the last allowed position and names the broken rule instead
of the generic "geometry cannot be saved"; the Thickness dialog refuses
through its own toast. Both channels are pinned by demo/smoke_junction_limits
plus three mutants (angle threshold, write barrier, degenerate apex bevel).
Issue: #329
User-Visible: yes
The baseline for inheritance was the raw previous document, which for a
legacy space carries no wall catalogue at all — so every inherited short
segment of a real plan looked new and the resize smoke's legitimate write was
refused (executed: two 5 cm segments against their own 30 cm thickness).
Both sides now cross commitWallSegmentModel first, and inheritance is counted
per rule rather than per subject, because a structural write re-keys the
carriers it re-atomises.
Issue: #329
User-Visible: no
The owner's five limits as pure functions: minimum 15 degrees between
neighbouring rays of a node (a straight wall through the node is a 180 pair,
not a violation), at most 6 walls per node, a segment at least
max(20 cm, its own thickness), 5 cm clearance between non-incident nodes and
between a node and a foreign wall (a T-joint sitting exactly on that wall is
incidence, not a near miss), and a room interior of at least 25 cm2 after the
masonry is subtracted. Thresholds are absolute and do not scale with cell_cm.
newViolations() implements the spec's inheritance boundary: only violations
introduced by the write are reported.
Issue: #329
User-Visible: no