The gate used to require every Validate run on the tag SHA to be green:
a cancelled duplicate or a red flake that a later re-run had fixed kept
the stable release blocked (v1.73.0, 09.09 — released by hand). Now the
verdict comes from the newest run that was not cancelled: not completed →
wait, success → pass, anything else → fail, no run → wait. The same rule
is documented for the perf workflow and the release runbook.
Mutants: release-gate-counts-cancelled-runs, release-gate-oldest-run-wins.
Issue: #511
User-Visible: no
The #160 contract test keeps the dense profile's longTasks block equal to
the historical isometric one, and both profiles boot through the same lazy
iso-scene-render chunk; the accepted split applies to both. Validate
34356856702 caught the divergence.
Issue: #507
User-Visible: no
Release: v1.73.0
Full Performance of the v1.73.0 stable candidate against the v1.72.0 product
(run 34354409872) was red on one check of the isometric profile:
longTask.countP95 16 → 20 against max(16×1.2, 16+3) = 19.2, with every
timing, longTask.totalP95Ms and longTask.maxSingleMs green. The trace behind
#506 shows why: since v1.73.0-beta.1 the isometric renderer is the lazy
iso-scene-render chunk (#160 Stage 3), so the single v1.72.0 boot task is
split in two around that import — the same work, +2 tasks.
Owner decision 2026-09-09: accept the split. countNoiseAllowance 3 → 5 for
large-house-isometric-v1 only; the ratio, the hard ceiling, total and
maximum single task keep gating real growth. The downloaded CI artefact
re-evaluated with this budget passes (limit 21, actual 20, no failures).
Documented in demo/performance/README.md; the budget test pins the
allowance and the untouched profiles.
Issue: #507
User-Visible: no
Release: v1.73.0
Promote the nine published v1.73.0 betas without new product behaviour:
stable version fields, synchronized generated bundles, the aggregated
bilingual release notes from v1.72.0 and status metadata only. beta.9
carried the startup-regression fix (#506) that Full Performance caught on
the first promotion attempt; the stable body keeps it out as an in-line
regression per the #328 curation rules.
Issue: #506
User-Visible: no
Release: v1.73.0
The beta.9 candidate failed the same phase twice in CI with "Resulting
promise was garbage collected" (runs 34346813552, 34347910231) while it
passes locally; a timer guard did not help, which points at a destroyed
context rather than a starved animation-frame chain. The wait now runs as
page.waitForFunction with raf polling, and the smoke logs frame navigations
and page crashes as `diagnostic …` lines so the next failure names its
cause. Verdict unchanged (animationName of the boot house, or 'missed').
Pre-release gate repair per PROCESS §11.4; locally OK ×2.
Issue: #506
User-Visible: no
Release: v1.73.0-beta.9
Validate 34346813552 on the beta.9 candidate failed only in browser smoke
shard 1: smoke_preloader phase 3 died with "Resulting promise was garbage
collected" — its rAF-only wait for the boot house had no other reference
while the runner withheld animation frames. A timer now bounds the wait
and keeps the promise reachable; the verdict is unchanged (animationName
of the house, or 'missed'). Pre-release gate repair per PROCESS §11.4:
locally `node demo/smoke_preloader.mjs` → OK ×2; all other heavy gates of
that run (golden, perf-smoke, backend, shards 2–3) were green.
Issue: #506
User-Visible: no
Release: v1.73.0-beta.9
Version fields and generated bundles move to 1.73.0-beta.9; the #506
changelog entry leaves Unreleased for the beta.9 section; release notes and
STATUS describe the startup-regression fix that unblocks the stable
promotion. No product change beyond #506, already reviewed and merged.
Issue: #506
User-Visible: no
Release: v1.73.0-beta.9
Every card instance attached its summary-panel runtime through
import().then(), even when the chunk had loaded long ago. The first render
therefore measured a header without summary controls; the controls arrived
a beat later, the stage shrank, the deferred refit opened a `stage-resize`
continuity candidate and the first HA tick paid three extra render passes
(Full Performance: blend stateUpdate1 50 → 130 ms, overlay 217 → 809 ms;
locally 4 performUpdate per tick instead of 1).
summary-runtime-loader.ts separates the summary code from its state: the
loaded factory is cached per page, every host builds its own runtime from
it (no shared preferences, drafts, subscriptions, timers or DOM). A warm
factory yields the runtime synchronously in connectedCallback, before the
first Lit render; a cold mount still pays one lazy import, concurrent cold
mounts share the pending import, a failed import is forgotten so the next
connection retries, and a disconnect cancels the pending attachment of that
connection. SummaryRuntimeSlot owns the per-host lifecycle so the card core
stays under its line ceiling.
Witnesses: loader unit tests (distinct instances, shared pending import,
cancelled attachment, retry after failure); demo/smoke_summary_warm_attach
(warm replacement and cold-key instance on a warm page own the runtime
before the first render, header/stage stable from the first frame, no
stage-resize, one performUpdate per geometry-neutral tick, a real viewport
resize still opens stage-resize); mutant summary-runtime-attaches-after-
first-render. smoke_summary_panel waited for `_summary` as a readiness
proxy; it now waits for the server config load, which stays asynchronous.
Local paired glow benchmarks (4× CPU throttle): blend 159.7 → 49.9 ms and
overlay 326.7 → 120.4 ms at stateUpdate1 with renders 4 → 1; the isometric
load loses the three summary-owned long tasks.
Docs screenshots: all 11 frames decode pixel-identical to the committed
ones; the manifest carries only the new source fingerprint. Initial View
ceiling recentred 299 100 → 299 600 for the +299 B loader.
Issue: #506
User-Visible: yes
Attachment uploads stage the body as `.upload-*` under files_root and then
asked the quota to count that file as stored usage *and* as the incoming
size, so the last file that still fit was refused at the boundary — by
bytes and by count. check_quota/dir_usage now take `exclude` for the
caller's own staged file; other staged files keep counting, so two
concurrent uploads can never both land past the limit.
The support package copied every string key of settings.fill_colors. The
schema stays open for compatibility, but the projection now keeps only the
eleven slots the card defines (SUPPORT_FILL_COLOR_KEYS, pinned to
src/logic.ts DEFAULT_FILL_COLORS by a test); an empty palette is omitted.
The SVG local-reference walk was a recursive DFS: a flat chain of a few
thousand hrefs passed every #436 bound and died with RecursionError, which
the upload view turned into a 500. The walk is iterative and measures the
longest chain through each node (memoised, order-independent); chains
deeper than MAX_SVG_REF_DEPTH = 64 are refused as too_large, cycles stay
invalid_image.
Tests: quota boundaries on the validator and the HA endpoint, a barrier
test for concurrent uploads, palette allowlist and TS parity, reference
chains (plain, hostile id order, cycle) on the validator and the endpoint;
six mutants caught by the standard runner.
Issue: #498
User-Visible: yes
Spec review r1: add the AC list with proofs and the perf/touch statement;
measure the SVG reference limit as the longest chain through a node
(memoised), not the stack height of the first traversal, so a hostile id
order cannot cut a long chain into short segments; state that two uploads
checked while both are staged are both refused (conservative), never both
stored.
Issue: #498
User-Visible: no
Apply re-validated the preview token after both halves were durable, so a
TTL that lapsed during the write, or an eviction by a newer preview of the
same user, answered "preview expired" for a plan that was already replaced
and withheld both update events. The token is now simply spent after the
commit; validity is decided once, on entry under write_lock.
Route runs dropped by drop_unknown_routes never reached the store unless a
marker happened to be orphaned in the same pass; an idle robot kept them in
memory only and a restart brought them back. The drop now happens under
_refresh_lock, leaves with the orphan transaction or with an immediate
write of its own, and rolls back like #335 when the store refuses.
Tests: HA harness for both apply races and a live config/set route drop,
recorder stubs for the four durability cases; five mutants caught by the
standard runner.
Issue: #495
User-Visible: yes
claude-code-action v1.0.218 (Claude Code 2.1.265) runs `claude install`,
which on ubuntu-latest sometimes leaves no launcher at ~/.local/bin/claude
while still reporting success; the action trusts the exit code and the SDK
then fails with ENOENT (anthropics/claude-code-action#1817). Four review
runs in a row died this way after 22:27 UTC 08.09.
Add a step that fetches the exact version the action pins (read from its
run.ts, fallback 2.1.265) from downloads.claude.ai, verifies the sha256
from the release manifest, checks `--version`, and hands the path to the
action via `path_to_claude_code_executable`, which makes the action skip
its own installer entirely.
Issue: #503
User-Visible: no
scripts/merge-candidate.mjs owns the review pipeline's merge: when dev
moved during review, the rebased candidate is pushed to the issue branch,
its diff is compared to the reviewed one by patch-id, Validate on that SHA
is awaited, and only then dev is advanced with --force-with-lease on the
base the candidate was built on — a rejected lease restarts, at most three
times. Every non-merge outcome moves the label with a comment, so the
"label always changes" invariant holds. nightly.yml now finds the Validate
run it dispatched and inherits its conclusion. Three mutants guard this.
Issue: #492
User-Visible: no
guardInputs() replaces guardFiles() in selection and fingerprints: the
files named in the guard, the GUARD_INPUTS a wrapper declares (read
statically — the wrappers run on import), and the closure of imports and
path literals of every guard file, stopping at src/** which stays the
patch side. A diff that touches the registry itself selects every added or
changed definition against the base registry read from git. Five mutants
guard the manifest and this selection.
Issue: #492
User-Visible: no
scripts/check-inputs.mjs declares every Validate check with its roots and
entry points and computes the rest: imports and path literals of the
entries, transitively for code, as leaves for data. classify-changes and
gate-reuse both read it, so "which job runs" and "what its key hashes"
cannot disagree any more. An executable file no check knows widens the run
to the full set and is named in the summary; the coverage list makes such
a file a red unit test rather than a permanent widening. The workflow file
is a toolchain input of every job; backend no longer hashes src/**.
Issue: #492
User-Visible: no
Fingerprint-only: the panel change does not alter any documented frame
(the docs harness already gave the panel host the viewport height), so the
committed images stay and only the source provenance moves.
Issue: #488
User-Visible: no
HA assigns panel/hass/narrow/route before the top-level-await entry has
defined the element, so the values landed as own properties that shadowed
the accessors and the card never received hass. And <ha-panel-custom> has
no height, so the percentage host height collapsed the stage to 0 px.
Adopt pre-upgrade properties through the accessors and size the panel from
the viewport minus HA's safe-area padding. The smoke now reproduces HA's
real mount order and container; two mutants guard both contracts. The
bundle is rebuilt from these sources.
Issue: #488
User-Visible: yes