Files
houseplan-card/test
Claude 462b56453c fix: the authoritative adoption keeps its own task (#520)
The r1 diagnosis was wrong, and the measurement in the code review proved
it: removing the two declarations from `static properties` left the cold
start at 19 update cycles, 4 model builds and 4 config epochs, exactly the
numbers of the bug. Lit's forced first-update change does mark `_serverCfg`
changed, but at that moment the body and `_cfgEpochPreservedConfig` are both
null, `preserveGeometry` is true and the epoch does not move. The comment
above `static properties` now says that; the declaration still stays out,
because two owners of one reactivity is what #500 removed.

The real cause is the `await`. Before #500 everything from
`_adoptStructuralResponses` to the end of the load ran in one task: the
adopted bodies, `_adoptInitialSpace`, the viewport restore, `_loadOk`, and
the device seeding — whose `_syncNewDevices`/`_seedHiddenDevices` write the
config back — all landed in a single Lit update. #500 made the adoption an
async sequence, so the caller resumes one microtask later, after Lit has
already painted the adopted config; the seeding writes then arrive as a
second config epoch, a second model build and a second paint of a 60-room
house.

`GatedAdoptionInput` gains `afterAdopt`, the mirror of `beforeAdopt`: it
runs synchronously at the end of the sequence, before the promise resolves.
`_loadFromServer` moves the viewport restore, `_loadOk` and the device
rebuild into it — `_syncNewDevices` refuses to write before `_loadOk`, so
the order inside the hook matters — and the load tail now rebuilds devices
only when nothing was adopted. `_reloadConfigOnly` takes the same route.

Measured with the project's own runner, 7 samples per profile, base
`a44fbd37` against this tree (Chromium 152, sandbox):

  interaction  modelReadyMs 761.3 ≤ 950.56 (base 731.2)
               firstStableRenderMs 2567.2 ≤ 3000 (base 2542.7)
               longTask.maxSingleMs 690 ≤ 921 · cache.entries.cleanFloor 100
  isometric    modelReadyMs 1252.9 ≤ 1499.76 (base 1249.8)
               firstStableRenderMs 1378 ≤ 1610.16 (base 1341.8)

Boot diagnostics on both trees: 18 update cycles, 3 model builds, 3 config
epochs, with the same epoch trace — the candidate is no longer
distinguishable from the base.

Witnesses. `config-adoption.test.mjs` queues a microtask at the start of
the adoption and pins that `afterAdopt` runs before it — the probe fails the
moment the hook crosses an await; `config-adoption-ownership.test.mjs` pins
the wiring in the card and the hook's place in the sequence. Mutants
`adoption-tail-defers-caller-hook` (defers the hook by one microtask) and
`authoritative-load-seeds-devices-after-the-await` (drops the rebuild from
the hook) redden them.

The initial View graph grows 40 B gzip, so the #438 ceiling is recentred
300 300 → 300 400 with the usual dated note; measured 299 812 B keeps 588 B
above and 1 412 B below the band. The overall 301 066 B budget and the #367
headroom debt are untouched.

Issue: #520
User-Visible: no
2026-09-10 20:12:13 +03:00
..
2026-09-09 03:51:25 +03:00
2026-09-09 03:51:25 +03:00
2026-09-07 11:06:28 +03:00
2026-09-07 11:06:28 +03:00
2026-09-07 11:06:28 +03:00