test: grade the wall-key invariant by what the product does
Validate / docs (push) Failing after 25s
Validate / process-workflow-sync (push) Successful in 1m13s
Validate / provenance (push) Failing after 1m16s
Validate / process-gate (push) Failing after 1m25s
Validate / changes (push) Successful in 1m9s
Validate / reuse (push) Successful in 58s
Validate / hacs (push) Failing after 17s
Validate / hassfest (push) Failing after 20s
Validate / frontend (push) Successful in 10m20s
Validate / backend (push) Failing after 9m54s
Validate / smoke (1) (push) Failing after 18m2s
Validate / smoke (2) (push) Failing after 17m49s
Validate / smoke (3) (push) Failing after 15m44s
Validate / smoke_done (push) Skipped
Validate / golden (push) Failing after 15m1s
Validate / performance_smoke (push) Failing after 13m49s

The first cut of checkWallKeys compared the stored key against endpoints
snapped to the lattice and called any mismatch a violation. Both halves were
wrong, and measurement says so: wallIntervals reports the query key for the
disputed edge as 0.887500,0.195833@1.5706, i.e. the form built from the
coordinates as stored, and the two owner configurations that differ in exactly
these keys produce byte-identical wall bodies and multi-wall node maps. The
check would have reddened a plan that renders correctly.

Graded now: a drift inside the tolerant fallback's half-pitch reach is an
observation, a key beyond it or one that does not parse as coordinates is a
violation. The threshold is expressed in grid steps with a 1e-3 slack — with a
relative 1e-6 the four identically drifted records of one plan split between
the two classes on their last bits, so the check repeated the very rounding tie
it exists to expose.

visual-matrix leaves KEY_CONTRACT_DEBT: its keys drift inside the reach and now
read as observations. large-house stays — its labels do not parse, and
wallIntervals shows all 80 solid edges resolving to zero thickness (#260).

Issue: #259
User-Visible: no
This commit is contained in:
Claude
2026-08-23 14:11:22 +03:00
parent 2970b8091b
commit 5e95a28406
4 changed files with 217 additions and 117 deletions
+14 -12
View File
@@ -21,18 +21,20 @@ carry exact endpoints `a` / `b` in normalised plan coordinates. Config always
stores centimetres. Old `{key, cm}` data remains readable and is upgraded when
the affected boundary is edited. Open boundaries refuse thickness. One physical
stretch has one thickness (atomic collinear spans when neighbours overlap only
partially). **The key is computed from endpoints snapped to the lattice**
(`Math.round(v · 240) / 240`), never from the raw stored numbers: `wallKey`
quantises the midpoint with `Math.round`, so a wall whose length is an odd
number of grid steps has its midpoint exactly on a rounding tie, and the two
representations of one vertex — the exact node `83/240` and a stored
`0.345833333` — fall on opposite sides of it. That is how #258 lost two records
whose keys had drifted by one step. The tolerant fallback in `lookupWall` does
not rescue such a record: its tolerance is exactly half a pitch, which is
exactly how far a one-step key error sits from the true midpoint, so the
comparison lands on its own boundary. `scripts/model-invariants.mjs`
(`checkWallKeys`, #259) compares the stored key against the lattice key as
strings, without tolerance. Exact endpoints make a thickness boundary independent of whichever
partially). The lookup key is computed from the
edge coordinates **as stored in the configuration**, not from a lattice-snapped
copy of them. That matters because `wallKey` quantises the midpoint with
`Math.round`: a wall whose length is an odd number of grid steps has its
midpoint exactly on a rounding tie, so the exact node `83/240` and a stored
`0.345833333` land in different buckets and the same edge acquires two possible
keys, one grid step apart. A record carrying the other one is still found — the
tolerant fallback in `lookupWall` reaches half a pitch, which is exactly that
distance — but only through the fallback, and the comparison sits on its own
boundary. Measured on two real configurations differing in exactly this way:
identical wall bodies, byte for byte (#258). `scripts/model-invariants.mjs`
(`checkWallKeys`, #259) grades it accordingly — a drift within the fallback's
reach is an observation, a key beyond it or one that does not parse as
coordinates at all is a violation, because nothing will find that record. Exact endpoints make a thickness boundary independent of whichever
room topology later happens to split the same straight line. Normalisation
merges consecutive solid pieces into each maximal run of equal thickness; a
different thickness or a virtual gap remains a real break. Likewise,