Обе задачи вошли в стабильный тег кодом, но их строки чейнджлога остались под
«Не выпущено»: #532 и #534 закрывались в тот же день, что и сам выпуск, и
секция версии к тому моменту уже была закрыта. Тело релиза при этом не называло
ни ту, ни другую — обе молча попали под «мелкие исправления и улучшения», хотя
#532 это как раз та правка, ради которой владелец и присылал три профиля
Firefox.
Строки перенесены в секцию v1.74.0, тело релиза переписано: первый пункт теперь
называет все четыре правки скорости (#532, #531, #524, #534). Проверка правил
#328 зелёная на диапазоне v1.73.0..v1.74.0.
Тег не двигается: он указывает на протестированное дерево, а тело релиза на
GitHub обновляется отдельно — именно его читает человек.
Issue: #534
User-Visible: no
#525 увёл оба списка сцены с `map()` на `repeat(items, (item) => item.id, …)`,
чтобы Lit не переиспользовал узлы по позиции и не проигрывал анимацию двери,
которой не было. Правка верная, но на плане с двумя сотнями маркеров она
оказалась дорогой ровно там, где пользы не приносит: при смене пространства
ключи не пересекаются вовсе, и `repeat` строит две карты ключей и обходит оба
списка, чтобы затем всё равно выбросить всё и создать заново.
Бисект по медиане `switchCycleMs` на `large-house`: 871,2 перед #525 → 953,5
после. На раннере эти 80 мс распадаются на шесть-девять дополнительных длинных
задач, и `longTask.countP95` вышел за порог стабильного гейта.
Теперь оба списка рендерятся как `keyed(space.id, repeat(…))`. Внешний ключ
делает смену пространства: поддерево выбрасывается целиком, дифа нет. Внутренний
остаётся, потому что состав списков едет и внутри пространства — у маркеров от
призраков редактора и живого синка, у проёмов от записи с нерешённым хостом,
которая живёт только в режиме plan, — а переходы на `.device-shell-frame`,
`.op-leaf` и `.op-arc` никуда не делись.
Замер после правки: `switchCycleMs` 889,1 против 936,0 на `main` и 871,2 до
#525. Потолок карточки поднят на одну строку — на `import { keyed }`;
переносить нечего, сам рендер не вырос ни на символ.
Issue: #534
User-Visible: yes
Code review r1 found two documentation claims that the task's own contract
contradicts.
Both changelogs promised the picture was unchanged "to the pixel". It is
not: splitting the layer moves the wall hatch by a sub-pixel, and that is
exactly why four day-cycle baselines are re-taken. The entry now says what
a reader can check — the outline around the plan matches pixel for pixel,
the hatch inside the walls is anti-aliased a shade differently.
docs/SUN.md credited Firefox with the fifteen-fold ratio. That number comes
from headless Chromium through CDP tracing; the Firefox profile that opened
the issue measured the same cause differently — 23 MB of texture uploads per
frame and about nine frames per second. Each number now names its engine.
Issue: #532
User-Visible: no
The "Follows the sun" background made the plan crawl in Firefox: 23 MB of
textures per frame, sixteen of about twenty picture-cache tiles thrown
away every frame, nine frames per second. The card's own JavaScript was
idle for 89 % of that.
The cost is the outer outline. It is a triple drop-shadow over the
grouped paper footprint, and although the group holds paper silhouettes
only and never changes on hover or pan, the filter lived in the plan's
own layer — so every repaint of the plan re-ran three blur passes over
the whole sheet. One hint moves the filtered paper into its own layer
and unhooks it from the plan's repaints.
Measured on a demo-stand pan (Chromium, CDP, summed RasterTask): 1456.6
ms against a static background's 96.7 ms before, 84.7 ms against 103.2
ms after. The new smoke measures that ratio and fails above two.
The picture does not change: the outline outside the plan matches byte
for byte and the frame's mean colour moves from 176.59 to 176.66 of 255.
Splitting the layer does move the remaining content onto a different
sub-pixel grid, so the diagonal wall hatch anti-aliases differently and
the four day-cycle baselines are re-taken on the beta candidate.
Issue: #532
User-Visible: yes
Первый кандидат держал не дефект продукта, а мигающий свидетель ресайза: он
фиксировал экранные координаты заранее, а карточка переводит их обратно в момент
события. Свидетель починен (#533), содержание релиза не изменилось.
Трейлер `Release:` просит CI прогнать тяжёлые гейты — смоки, golden и полное
сравнение производительности — на этом SHA.
Issue: #533
User-Visible: no
Release: v1.74.0
Seven version sources move together to 1.74.0; both changelogs gain the
stable section aggregated from the previous stable v1.73.0, the release
notes carry three itemised bullets on each side plus the grouped
small-fixes bullet, and STATUS says what this release is.
What the user gets since v1.73.0: the plan is noticeably smoother and in
Firefox dramatically so — a pan frame now moves what is already drawn by
a composited transform with the scene re-rasterizing on a budget (#531),
and a marker shadow sized in container units no longer restarts a
non-composited animation on every marker at once (#524); the printed plan
is about a third larger, because the in-plan dimension labels shrank and
the separate column of external dimensions is gone (#530); a plan left
over from the era of rooms without walls can be edited again, the
leftover cleaned up on the first save instead of refusing every change
(#529, reported in #527). Grouped under small fixes: alignment guides
follow the gesture again (#521), the summary panel neither freezes the
first frame nor claims an unavailable source (#509) and its settings
dialog scrolls again (#508), a space switch no longer replays a
neighbouring door's leaf (#525), and the card downloads 12.8 KB less on a
cold start (#526).
The speed verdict for #531 is deliberately not claimed here: headless
Firefox and Chromium hold 60 fps on both the old and the new path, so it
is owed by a profile from the owner's machine on the same pan.
`npm test` 2528/0/1 skip, `pytest tests_backend` 467 passed, release
notes pass the #328 verifier against v1.73.0..HEAD, bundle 287 242 B gzip
inside the ceiling, docs strict green.
Issue: #531
User-Visible: yes
Release: v1.74.0
Seven version sources move together to 1.74.0-beta.3; the changelog
sections in both languages close over what has landed since beta.2, the
release notes carry the two shipped bullets on each side with the grouped
small-fixes bullet last, and STATUS says what this beta is.
What the user gets: dragging the plan is smooth again — a gesture frame
now moves what is already drawn by a composited transform, and the scene
re-rasterizes on a budget (100 ms, or a 15 % shift) instead of on every
frame, which on the owner's 144 Hz Firefox cost 200 ms per frame with the
refresh driver skipping 124-144 ticks a second waiting for paint (#531);
and the printed plan is about a third larger, because the in-plan
dimension labels shrank by a quarter and the separate column of external
dimensions is gone, so the drawing keeps a whole scale step (#530).
The speed verdict for #531 is deliberately not claimed here: headless
Firefox and Chromium hold 60 fps on both the old and the new path, so it
is owed by a profile from the owner's machine on the same pan.
`npm test` 2528/0/1 skip, release contract green on all seven sources,
bundle 287 238 B gzip inside the ceiling, docs strict green.
The `Release:` trailer is what asks CI for the heavy gates — smokes,
golden and the full performance comparison — on this exact SHA (#479).
Issue: #531
User-Visible: yes
Release: v1.74.0-beta.3
Ребейз на `dev` (#530) обнулил обе производные копии: хэши чанков считаются от
содержимого, а отпечаток скриншотов — от `src/**`. Пересобрано, отпечаток
принят с `--identical`: все 11 кадров попиксельно совпали.
Issue: #531
User-Visible: no
Перезапись `viewBox` — это не сдвиг, а инвалидация растеризации всей сцены.
Кадр жеста делал её каждый раз: в профиле владельца (Firefox 155, 144 Гц) кадр
доезжал до экрана 200 мс, а драйвер пропускал 124–144 тика в секунду с пометкой
«ждём краску».
Теперь `paintLiveViewport` держит якорь — кадр, чей `viewBox` записан в DOM, и
момент записи. Кадр жеста двигает узлы сцены тем же проективным преобразованием,
которым уже двигались HTML-слои, а `viewBox` переписывается по бюджету: 100 мс
либо 15 % сдвига/масштаба. Ни атрибут, ни стиль не пишутся, если строка не
изменилась.
Issue: #531
User-Visible: yes
The A4 export spent a whole step of the scale series on the "Internal
dimensions" column beside the drawing: a ten-metre house printed 1:100
landscape on a quarter of the sheet while the same plan fits 1:75
upright. The column bought little — "R1" takes as much room beside the
wall as "2.31m" does — and it was read separately from the drawing.
The column is gone. A value with no free lane beside its own wall is
simply not printed; the rectangular rule became the general one. In-plan
type drops to three quarters of its former size (dimensions 5.25 pt,
areas 6 pt, names 6.75/5.25 pt) so the larger drawing is not crowded by
labels that do not scale with it.
On the owner's file the sheet goes from 1:100 landscape to 1:75 upright
and prints 37 values inside the plan against 35 before; the cost is
about seven short values (0.41-1.13 m) that no longer appear anywhere.
Issue: #530
User-Visible: yes
Seven version sources move together to 1.74.0-beta.2; the changelog
sections in both languages close over what has landed since beta.1, the
release notes carry four bullets on each side with the grouped
small-fixes bullet last, and STATUS says what this beta is.
What the user gets: a plan left over from the era of rooms without walls
can be edited again — the leftover is cleaned up on the first save
instead of refusing every change, pointing at an Optimize that cannot
help and blocking the export too (#529, reported in #527); the plan is
smooth again in Firefox, where a marker shadow sized in container units
restarted a non-composited animation on all markers at once and cost 9.4
frames per second (#524); switching spaces no longer replays a
neighbouring door or marker (#525); and the card downloads 12.8 KB less
on a cold start, because the stylesheet minifier had never actually run
(#526).
Internal in the same line: the space-switch witness judges node identity
instead of a transition that #524 removed (#528), and the review gate
charges a round only for what the round changed (#518).
`npm test` 2520/0/1 skip, `pytest tests_backend` 467 passed, release
contract green on all seven sources, bundle 287 286 B gzip inside the
ceiling, docs strict green.
The `Release:` trailer is what asks CI for the heavy gates — smokes,
golden and the full performance comparison — on this exact SHA (#479).
Issue: #529
User-Visible: yes
Release: v1.74.0-beta.2
A plan that still carried a `room_drafts` key while already on the
current wall model could not be edited at all. The card mirrors the same
migration, so a structural edit was refused before the request ever left
the browser; the toast sent the user to "Optimize plans", which reports
that everything is already optimal because it looks at something else
entirely; and the export path calls the same migration, so the one way
out — take a backup, fix the file by hand — was shut too. An empty
`room_drafts: []`, carrying no data at all, was enough to do it.
The carrier is now removed the way the first migration removes it: an
empty key silently, drafts converted one for one into partitions. The
#478 protection against a stale client re-adding the carrier moves to
the layer that can actually tell the two apart —
`validate_wall_model_transition` sees both the submission and the stored
plan, and refuses when the drafts appear over a plan that does not have
them. It no longer keys on the submitted model number: a stale card
echoes back the number it was given, which is exactly how the outdated
client slipped past this guard and met "conflicting wall identifiers"
instead of "update the card and reload the page". The schema invariant
keeps refusing a non-empty carrier as the last line.
Both mirrors change together and stay identical; the parity fixture is
untouched.
Issue: #529
User-Visible: yes
All 11 frames are pixel-identical (docs:accept --identical, #512): the
minified stylesheets render the same, which is the point.
Issue: #526
User-Visible: no
It looked for the tag written as `css` immediately followed by a
backtick. The plugin is a Rollup transform, so the module has already
been through TypeScript by the time it arrives, and the TS printer puts
a space there: `css `. The guard therefore returned null for every
stylesheet in the project, and minification never ran once — around
23 KB of explanatory comments went to every user in every release.
Matching the tag as a word with optional whitespace turns it on:
chunk, raw 1 079 508 -> 1 021 115 B (-58 393)
initial view 300 111 -> 287 284 B gzip (-12 816)
room to the budget 955 -> 13 782 B
The ceiling moves down with the fact, as the tool asks when a graph
shrinks past the band.
The risk is not the two lines; it is that 23 KB of CSS is minified for
the first time. Two witnesses cover it: a browser smoke that puts the
original and the minified text into separate stylesheets and compares
the serialised rules — 1 049 of them, identical up to the whitespace
policy the minifier declares — and a test that takes real comment text
out of src/styles and requires it to be absent from dist, so a plugin
that silently stops working cannot pass again.
Issue: #526
User-Visible: yes
The shadow of a device marker is sized from the marker, and the marker is
sized from the container: --device-shell-shadow is expressed in
--dev-size, which resolves to 2.5cqw. With box-shadow in the transition
list, every container-query re-evaluation — a tooltip, a scrollbar, a
rotation — produced a new computed value and restarted a 150 ms
non-composited transition on every marker at once.
The owner's Firefox profile shows what that costs: 244 box-shadow
transitions, all on span.device-shell-frame, all oncompositor:false, in
bursts of exactly 61 (the markers on screen), four bursts in two
seconds. During them the tab presented 103 frames in 11 seconds — 9.4
per second, CONTENT_FRAME_TIME median 149 ms and up to 320 — while the
refresh driver waited for paint 381 times. Our JavaScript in the worst
three seconds: 16 ms. Chromium starts the same transitions (measured:
one pixel of container width starts two per marker in both engines) and
merely pays less for them, which is why this hid there.
The shadow itself is unchanged; it simply applies at once. The core
keeps the same treatment, so the selection and focus rings appear
without a fade — an instant ring is ordinary feedback, a faded one costs
a full-frame repaint per marker. Hover still animates border-color.
Issue: #524
User-Visible: yes
The fix touches `src/houseplan-card.ts` and `src/styles/plan.styles.ts`, and
the documentation fingerprint covers the whole `src/**` corpus.
Accepted with `npm run docs:accept -- --identical` (#512): all 11 frames were
re-captured locally and matched the committed ones byte for byte, so only the
fingerprint moves, `7a90ca04 → cd8e9598`. No PNG changes. Environment: Linux,
Chromium 152 — the canon platform of #455.
Issue: #525
User-Visible: no
Lit reuses list nodes by position. The opening list and the device markers had
no keys, so on a space switch the leaf that held a slot kept its DOM node and
only changed values — and `.op-leaf` (transform) and `.op-arc`
(stroke-dashoffset) carry a 0.6 s transition, so the browser animated a door
that never moved: the new floor's leaf drove in from the previous floor's
opening angle. Measured on two spaces with a door in the same place and
opposite contact states: the node is reused, transform goes
`rotate(-90deg) → rotate(0deg)`, dash offset `0 → 125.66`, both transitions
`running`. The marker shell adds two more with its `box-shadow`.
Both lists are now rendered through `repeat(…, (item) => item.id, …)`, the
same lesson `glow-scene.ts` already learned for the Glow spots. The trap is
written where it starts — above the two transitions in `plan.styles.ts` —
because that is the file someone edits when adding the next animated property.
`houseplan-card.ts` is at its line ceiling, and the note would have cost the
budget a dozen lines for nothing: the swap itself is line-for-line.
The witness walks the shadow tree per element. `document.getAnimations()` is
empty here EVEN ON THE BROKEN CODE — the card lives in a shadow root and the
document-level call does not reach into it, and the issue proposed exactly
that call. The smoke also builds its own fixture: the demo home has no
openings at all, so two doors in two spaces are prepared in the smoke, and it
asserts the other half of the contract as well — a real contact change inside
one space still animates the leaf.
On `origin/dev` the smoke fails on five facts, naming the offenders:
`op-arc:stroke-dashoffset`, `op-leaf:transform`, `device-shell-frame:box-shadow`
twice. Mutants `openings-rendered-without-keys` and
`device-markers-rendered-without-keys` put each `map` back.
Perf, 7 samples against `19e421b3`: spaceSwitchMs 524.8 (limit 769.35, base
512.9), switchCycleMs 1293.9 (1696.28, 1256.5), firstStableRenderMs 2533.8
(3000, 2529.2), modelReadyMs 732.7 (944.97, 726.9), longTask.maxSingleMs 663
(910, 660) — `benchmark:compare` green in full.
The initial View graph grows 241 B gzip: `repeat` enters it for the first
time. The #438 ceiling is recentred 300 400 → 300 700 with the usual dated
note; measured 300 059 B keeps 641 B above and 1 359 B below the band. The
301 066 B budget is untouched, but only 366 B now separate the ceiling from
it — the #367 headroom debt has stopped being theoretical.
Issue: #525
User-Visible: yes
The candidate was assembled before #520 and #521 landed. Version fields are
already at 1.74.0-beta.1 in all seven sources and the bundles rebuild
byte-identical, so this commit only moves what the beta says about itself:
the #521 changelog entry leaves Unreleased for the beta.1 section in both
languages, the release notes gain its bullet on both sides (four and four,
the grouped small-fixes bullet stays last), and STATUS records the two
issues merged since — the alignment guides that follow the gesture again
(#521, a regression shipped in v1.72.0 and v1.73.0) and the cold-start
adoption fix behind it (#520, internal).
No product change: `npm run build` plus `bundle-sync` leave `dist/**` and
the two mirrored trees untouched, `node scripts/release-contract.mjs
v1.74.0-beta.1` is green on all seven version sources, `npm test` 2515/0.
The `Release:` trailer is what asks CI for the heavy gates — smokes, golden
and the full performance comparison — on this exact SHA (#479).
Issue: #521
Issue: #520
User-Visible: no
Release: v1.74.0-beta.1