Commit Graph
88 Commits
Author SHA1 Message Date
Claude 4d73774779 ci: ребейз виден автору до пуша, конфликт называет файлы, уход dev отмечается
Конвейер приводит ветку к dev сам (#257) и при конфликте возвращает задачу, не
тратя цикл ревью. Оставались три щели, и все три про то, что человек узнаёт
поздно и без подробностей.

Первое. Отставание теперь видно в scripts/pre-push-gate.mjs до пуша, с числом
коммитов и готовой командой. Это предупреждение, а не гейт: гейтом остаётся
конвейер, который забыть не может. Смысл в цене — после любого ребейза разбор
становится полным, а не по дельте (§7.2), а конфликт всё равно чинится на машине
автора. Отключается --no-rebase-check.

Второе. Конфликт называет файлы. Список снимается ДО `git rebase --abort`: abort
снимает состояние конфликта вместе с ним, и раньше автору доставалось «не
ребейзится» без единого имени. Логика проверена на настоящем конфликте в
одноразовом репозитории — два файла названы.

Третье. Если dev ушёл вперёд, пока шло ревью, это записывается в summary
прогона, а при зелёном вердикте ещё и комментарием: вердикт вынесен по дереву,
которое уже не совпадает с вершиной линии, и слияние приведёт ветку к dev.
Комментарий только при зелёном — шуметь на каждом прогоне ни к чему, а вот
молчать перед слиянием нельзя.

Чего задача не делает: не заставляет dev стоять на месте, пока идёт ревью. Если
возвраты частые именно из-за темпа, лечится очередью слияний, а это решение о
процессе, не о скрипте.

Два мутанта проверены руками — «советовать ребейз всегда» и «никогда не сообщать
про уход dev», — каждый убит.

Issue: #364
User-Visible: no
2026-08-29 10:06:41 +03:00
Claude 583637313f test: гейт «новый код не добавляет any»
В src/** сейчас 1034 вхождения явного any в 49 файлах — больше, чем называл
аудит (330), потому что монолит с тех пор разделился и его обвязка уехала в
houseplan-editor-runtime.ts. Разовая замена такого объёма — месяц риска ради
нуля пользовательской ценности, поэтому долг снимается при плановом извлечении
подсистем (#34). Задача гейта одна: не давать долгу расти.

Судятся только добавленные строки диапазона. Изменённая строка со старым any
выглядит в диффе добавленной, и это намеренно: тронул — либо типизируй, либо
обоснуй на той же строке `// any-ok: <причина>`. Голый маркер, пустая причина и
шаблоны вроде todo, hack, потом не проходят.

Ложных срабатываний нет по построению, а не по старанию: текст разбирается
парсером TypeScript, и нарушением считается узел AnyKeyword. Регулярка по строке
ловила бы слово any в прозе внутри шаблона html и в комментариях; здесь
комментарии, строковые литералы, многострочные шаблоны и идентификаторы
company, anyOf, manyRooms узлами такого вида не являются вовсе.

Проверено исполнением на настоящем дереве, а не только юнитами: пробные коммиты
в src/wall-thickness.ts показали, что добавленный any падает с файлом и строкой,
типизированная строка в файле с 122 старыми any проходит, any-ok с конкретной
причиной проходит, а голый и «todo» — нет, и что any в прозе, строке и
идентификаторах не даёт ни одного срабатывания.

В job frontend checkout получил полную историю без блобов: diff-aware проверке
нужен диапазон, а содержимое старых ревизий — нет.

Заодно закрыта ловушка в test/validate-workflow.test.mjs: имя job искалось через
indexOf('  frontend:'), а эта строка встречается внутри `      frontend: ${{ ...
}}` в outputs job changes, поэтому срез уходил не туда. Теперь имя ищется с
начала строки.

Четыре мутанта проверены руками, два добавлены в реестр: гейт, судящий все
строки, и гейт, принимающий голый маркер.

Issue: #342
User-Visible: no
2026-08-28 16:02:17 +03:00
Matysh 1179a02122 ci: restore smoke test modules and isolate resize camera
Issue: #336
User-Visible: no
2026-08-28 12:47:37 +03:00
ClaudeandSergey Matyunin 1ea460e467 ci: перепаковка кадров документации без потерь при съёмке
oxipng снимает с набора 19.4%: 2096 КБ становятся 1689 КБ, и все десять кадров
остаются пиксельно идентичными — декодированные RGBA совпадают по sha256. Это
выбор фильтров строки и уровня сжатия, а не квантование: визуального решения нет.

Внутри съёмки, а не отдельным проходом по закоммиченным файлам: манифест хранит
imageSha256 каждого кадра, поэтому жать их в репозитории руками нельзя —
check-docs покраснеет; а если жать после подсчёта хешей, следующая съёмка вернёт
неоптимизированные байты. Хеш считается после перепаковки.

Версия oxipng попадает в манифест рядом с версией браузера и по той же причине:
байты кадра зависят от того, чем жали. Отсюда же правка шага «Вердикт» — иначе он
объявил бы «тот же браузер, а картинки изменились — изменился продукт», хотя
изменился упаковщик.

Пин версии и контрольной суммы вместо apt-get: пакет из образа раннера может
пропасть, а падение шага съёмки стоит целого цикла приёмки (#175, #206).

Проверено исполнением на прежней базе: съёмка прогнана целиком с подставным
oxipng, 2096 -> 1689 КБ, хеши манифеста совпали с файлами, check-docs зелёный.

Issue: #345
User-Visible: no
2026-08-28 10:28:13 +03:00
Codex b573590a96 ci: a force-push runs everything — the classifier stops guessing a rewritten range (#347)
github.event.before dies with a force-push, and the merge-base fallback then
guessed a diff range: on issue/333 it reported two review-doc files while the
real diff touched custom_components/** — frontend and backend jobs silently
skipped and the run stayed success, the exact #171/#207 class of silent pass
that nearly hid a genuine backend regression from code review.

The classifier now distinguishes the two fallback cases instead of merging
them: a ZERO before is a genuinely new branch and keeps the merge-base
range; a NON-ZERO before that no longer exists is a rewritten history, and
the range is not provable — frontend/backend/integration all go true, with
a loud note in the step summary. A force-push is rare and almost always
follows a rebase, where the full run is what an honest signal costs.

The three branches of the decision are pinned by a workflow-contract unit
next to the existing performance-workflow contracts.

Issue: #347
User-Visible: no
2026-08-28 10:19:37 +03:00
Matyshandclaude[bot] 2c65fcec03 perf: lazy-load editor runtime
Issue: #337
User-Visible: yes
2026-08-28 05:40:28 +00:00
Claude 59ae6b1064 ci: полная история без блобов там, где содержимое старых файлов не нужно
Полный клон — 215 МБ .git, blobless — 26 МБ, история коммитов и теги в обоих
полные (замер в #345). Две job Validate качают историю целиком: preflight и
changes. Первой нужны сообщения коммитов, трейлеры и имена изменённых файлов,
второй — только `git diff --name-only`. Содержимое старых ревизий не читает ни
одна из них ни на одном шаге.

Коммиты и деревья по-прежнему скачиваются полностью, поэтому диапазоны и
merge-base работают как раньше. Единственная догрузка блоба по требованию —
`git show origin/main:.github/workflows/process.yml` в шаге сверки, один файл.

Браузерным job фильтр не нужен: у них глубина по умолчанию, истории они не
касаются вовсе.

Тест закрепляет и обратную сторону: --depth=1 сюда подставлять нельзя, он того
же размера, но без merge-base, а на нём стоят процессный гейт, smoke-select и
каждый диапазон origin/dev..HEAD. Два мутанта проверены руками — снятый фильтр и
подмена на depth=1, — каждый убит.

Issue: #345
User-Visible: no
2026-08-28 08:00:51 +03:00
Codex ddfca3a865 fix: close code-review 330-r1 — budgets from the slowest machine, the bench in Validate, AC1 through the execution thread (#330)
H2: the benchmark budgets were calibrated on the author's sandbox with a
1.14x margin — the review runner measured tsFullCandidateMs at 169-171 ms
against a 100 ms ceiling. Budgets now keep the spec's 2-3x allowance over
the SLOWEST observed machine, and the benchmark runs as a step of the
Validate perf job on every push (it needs no browser and no bundle), not
only inside the weekly mutation gate.

M1: the promised AC1 backend test exists now and does what AC1 means: it
patches validate_junction_limits with a thread-recording wrapper inside the
real HA harness — on the event loop that would be MainThread — and proves
the verdicts survived the move (a clean write is accepted, a write adding a
spike is refused with junction_limit_angle). Spec revision 4 rewrites AC1
around this invariant instead of a fragile millisecond assertion.

M2: §4.6 equivalence is now behavioural on both sides (three boundary
fixtures each: as-is counts equal through-migration counts, TS and python),
and the parity suite gained the §7 boundary fixtures (exact 15°, exact
20 cm, the thickness-step filler run, exact 5 cm).

H1 was already closed by 7513f93d (the review ran on the previous HEAD):
check-docs is green on this tree — the screenshots and their manifest come
from one capture run.

Issue: #330
User-Visible: no
2026-08-28 03:07:44 +03:00
Claude 1866c0921e ci: ревьюер не перегоняет зелёные гейты, локальный набор перед пушем
Ревьюер гонял tsc, юниты и сборку заново в каждом раунде, хотя Validate на том
же SHA уже зелёный. Промпт прямо это требовал. Теперь шаг `validated` спрашивает
у Validate состояние ровно этого SHA, и доказательство такое же строгое, как у
reuse-маркеров (#208): не «недавно было зелено», а completed success на этом
коммите. После ребейза SHA другой, прогона для него нет — ревьюер честно гоняет
сам, и промпт это говорит.

Что Validate не покрывает, в примечании названо отдельно: смоки по диффу,
golden при правке рендера, инварианты на конкретной конфигурации. Иначе
экономия превратилась бы в «CI зелёный, значит всё проверено».

scripts/pre-push-gate.mjs — локальный набор: tsc, юниты, смоки по диффу
(smoke-select), мутанты по диффу (mutation-gate --changed). Замер на реальном
диапазоне 953f675~1..953f675: 46 секунд на всё вместе с двумя смоками.

Три свойства, без которых набор бесполезен: не останавливается на первом
упавшем; громко перечисляет, чего не проверял; не претендует на полноту. Бандл
не собирает — раскладывает закоммиченный dist, а свежесть проверяет сам продукт
через assertFreshDemoBundle внутри смока.

В хуке выключен по умолчанию: 20-45 секунд на каждый пуш, включая пуш одной
строки документации, — цена осознанная, включается HP_PREPUSH_GATE=1.

Дельта-промпт для spec-ревью (пункт 2) уже существует: блок «объём разбора по
дельте» из #214 покрывает оба этапа и прямо называет «дифф файла ТЗ или тела
issue для spec». Ничего не добавлял.

Issue: #343
User-Visible: no
2026-08-28 02:15:03 +03:00
Claude 2b1964f973 ci: бандл собирается один раз, четыре лёгкие джобы стали одной
Бандл собирался пятью job независимо: три шарда смоков, golden, перф-смок —
каждая гоняла `bundle:sync`, то есть `tsc --noEmit` плюс rollup. Теперь его
собирает `frontend` и выкладывает артефактом, остальные скачивают и раскладывают
`bundle-sync.mjs`. Подмену артефакта отдельной проверкой ловить не нужно:
assertFreshDemoBundle сверяет вшитый в бандл отпечаток с sourceFingerprint
выкачанного дерева, и каждая браузерная job делает это перед первым кадром.

`npm ci` остаётся во всех: браузерным job нужен playwright из node_modules, а не
только бандл. Артефакт node_modules был бы медленнее `npm ci` с тёплым кэшем.

docs, process-workflow-sync, provenance и process-gate стали шагами одной job
`preflight`. Независимость сохранена намеренно: у каждого шага
continue-on-error, вердикт в конце падает и перечисляет всё упавшее сразу.
Прежняя запись «краснеет сам и не роняет остальные» продолжает действовать — на
уровне шагов, с той же гранулярностью в логе.

hacs и hassfest не тронуты: предложение сузить их до dev и тегов уже выполнено
классификатором `changes` — на ветках задач они и так идут только при правке
манифестов, а на dev фильтров нет намеренно (гейт беты требует, чтобы «зелёный
Validate» значил одно и то же).

test/validate-workflow.test.mjs закрепляет то, что в диффе строк не видно:
висячая зависимость `needs` не роняет YAML, а молча пропускает job навсегда.
Три мутанта проверены руками — висячая зависимость, вернувшаяся вторая сборка,
шаг без continue-on-error, — каждый убит.

Issue: #336
User-Visible: no
2026-08-28 02:04:40 +03:00
Codex 2c20f2dc35 perf: mutation-gate builds the bundle only for browser guards, compiles incrementally, shards and diffs (#332)
Four independent cuts into the 2-4 hour full run, none touching the contract
"a mutant must turn its guard red":

- guardNeedsBundle: rollup runs only for guards that open the built bundle
  (demo/ smokes, golden captures, bundle:sync) — 68 of 253 registry entries.
  Unit and backend guards never read dist/ as a build artifact (verified
  against every test that mentions dist/**: they read the git checkout or
  synthetic files), so 185 mutants skip the most expensive step entirely.
- seedTestBuild + incremental tsc: the mutant worktree starts from the main
  tree's warm test-build/ and .tsbuildinfo; tsc compares file hashes, not
  mtimes, so the fresh checkout stays warm and only the mutated delta is
  recompiled. This also speeds up the long guards that run tsc themselves.
- --changed[=range]: run only mutants whose patch files are touched by the
  diff (origin/dev..HEAD by default). An empty selection is an honest success
  with an explicit message — the full registry remains the pre-release
  contract, per the workflow comment.
- --shard=i/n: deterministic interleaved slices; the workflow runs a 4-way
  matrix, and a warm test-build step feeds every shard. Interleaving spreads
  the expensive browser mutants across shards instead of clumping them.

Measured per mutant on this machine: unit 12-13 s (was ~50-70 s), backend
6 s, browser 32 s (unchanged — the bundle is genuinely needed there). Full
run estimate drops to ~70 sequential minutes, ~20 on four shards.

Unit coverage: guard classification on real registry shapes, a floor on both
classes so the split cannot silently collapse, changed-selection semantics,
and shard completeness/disjointness with an anti-clumping bound.

Issue: #332
User-Visible: no
2026-08-28 01:33:36 +03:00
Codex 6a3ac52258 ci: the performance-workflow contract test follows the renamed labels
The test pins the literal workflow name and the release-gate label; both
moved to the Russian names of #327.

Issue: #327
User-Visible: no
2026-08-27 18:50:02 +03:00
Codex 1a8b480355 ci: every workflow and job carries a human-readable Russian name
Owner decision (chat, 2026-08-27): the running check's name must say what it
does, in Russian. Scripts locate workflows by file name (release-gate.mjs ->
validate.yml), so display names are free; job ids and needs are untouched.
The same content is cherry-picked to main because release workflows execute
from the default branch and process.yml must stay identical in main and dev.

Issue: #327
User-Visible: no
2026-08-27 18:46:53 +03:00
Matysh 8210af6472 ci: parallelize independent full performance profiles
Issue: #322
User-Visible: no
2026-08-27 12:13:26 +03:00
Matysh 6b9f5eea96 ci: support legacy performance baselines
Issue: #322
User-Visible: no
2026-08-27 11:04:38 +03:00
Codex b4cccfdf63 fix: pin DoR to the commit era and merge only the reviewed SHA (#311, #312)
Правило 10 гейта (#311): DoR сверяется с моментом НАПИСАНИЯ кода — authorDate
коммита класса A не может предшествовать первому labeled-событию S5-ready+
из timeline issue; продвижение метки больше не прячет нарушение, ребейзы
конвейера его не смывают (authorDate переживает их). Проверка вторичная к
правилу 8: недоступный timeline — warn, правило 8 остаётся fail-closed.
LOG_FORMAT несёт authorDate третьим полем (append-совместимо).

Шаг слияния конвейера (#312): сливается только проверенный SHA — вершина
ветки сверяется с материалом ревью (допустим ровно один doc-коммит публикации
с диффом только docs/reviews/ поверх); расхождение отменяет слияние с
возвратом в S6-in-progress тем же путём, что конфликт (инвариант «метка
меняется всегда» сохранён). PROCESS.md §2.7 фиксирует правило «вердикт
привязан к SHA» и для ревьюера.

Issue: #311
Issue: #312
User-Visible: no
2026-08-26 03:15:57 +03:00
Claude 2970b8091b ci(process): ask the reviewer about the wall key invariant
Issue: #259
User-Visible: no
2026-08-23 13:51:37 +03:00
Claude 10999a5c5c ci(process): привести ветку к dev до код-ревью, а не после
Ревью шло по ветке как есть, слияние делало ребейз: проверенный SHA и
слитый SHA были разными коммитами. Текстовое расхождение ловил конфликт,
смысловое git склеивал молча — так пришёл регресс #234. Заодно конфликт
обнаруживался после сорока минут работы ревьюера, хотя виден до них.

Новый шаг для этапа code, сразу после выбора ветки: потомок dev —
ничего; отстала и ребейзится — ребейз, push с --force-with-lease, ревью
приведённого состояния и запись о ребейзе в промпт (§7.2 требует полного
разбора); конфликт — возврат в S6-in-progress без запуска ревью.

Issue: #257
User-Visible: no
(cherry picked from commit 793a6486d8)
2026-08-23 10:55:44 +03:00
Matysh 6d0fa3b819 build: stop committing the stand copy of the bundle
Issue: #255
User-Visible: no
2026-08-23 10:15:45 +03:00
Matysh 7e69b4bb28 ci: shard the smoke suite and stop validating review docs
Issue: #254
User-Visible: no
2026-08-23 09:55:04 +03:00
Matysh 74d2285d1b ci: ask the reviewer about numbers seen twice
Issue: #254
User-Visible: no
2026-08-23 09:50:06 +03:00
Matysh 59be468506 test: run model invariants over every shipped model
Issue: #254
User-Visible: no
2026-08-23 09:40:50 +03:00
Matysh 5fa3733c7e ci: make the screenshot verdict compare browsers, not counts
Issue: #246
User-Visible: no
2026-08-22 19:30:48 +03:00
Matysh e50c01245a ci: capture documentation screenshots in one place
Issue: #246
User-Visible: no
2026-08-22 19:12:27 +03:00
Matysh 6683e526d8 docs: put the smoke shortlist into the gate rules
Issue: #241
User-Visible: no
2026-08-22 18:33:17 +03:00
Matysh 636d0a56fa ci: name check-docs in the review gate set
Issue: #237
User-Visible: no
2026-08-22 11:43:55 +03:00
Matysh 33470306d2 fix: keep the review document outside the tree the reviewer mutates
Three code-review rounds on #220 published a verdict and then failed the
run: the document never reached the branch, so the #171 guard refused
before the label step and neither the merge nor S8-merged happened. The
cause was structural. The document lived as an untracked file inside the
very checkout the reviewer edits while proving that a test can fail, and
restoring that tree — git checkout, git clean — deletes an untracked file.
Spec rounds survived only because they never mutate anything.

The reviewer now writes to REVIEW_DOC under RUNNER_TEMP, outside the
repository, and the publish step copies it into docs/reviews before
committing. Tree cleanup can no longer destroy the artefact, and the
reviewer no longer needs to touch docs/reviews at all.

Verified against a local git fixture on five paths: document outside the
repo with a mutated tree, nothing anywhere (loud failure), document only in
the working copy, document already committed by the reviewer, and a branch
that moved during the review.

Same file as main, byte for byte.

Issue: #220
User-Visible: no
2026-08-21 10:53:07 +03:00
Matysh afd5d589d5 fix: spend the review budget on blocking verdicts only
The pipeline punished what it prescribed: after a failed merge it tells the
author to rebase and restore S7-code-review, and that attempt finished the
budget. On #225 a green code review with green CI ended in review-4.

Only yellow and red verdicts spend the budget now; a green verdict returned
nothing and consumes nothing. Attempts and cycles became separate
quantities: the attempt number names the review document, the limit
compares blocking cycles. The exhaustion comment lists what it counted, and
the guard reports a recount instead of stripping review-4 on its own.

Same file as main (41325a8), byte for byte.

Issue: #227
User-Visible: no
2026-08-20 23:54:13 +03:00
Matysh 2b45086794 docs: scope a repeat review round to the delta
The reviewer prompt was identical for every round, and the canon said
nothing about the scope of a repeat pass, so r2 re-derived the product
framing and re-checked acceptance criteria the fix never touched: the r2
pass on #150 cost a full pipeline run over one line in a test fixture.

From the second cycle on, the subject is the delta against the SHA the
previous verdict was given on: each earlier finding must be shown closed
by a line of code or text, only the criteria the delta can reach are
re-verified, and whatever is carried over is listed with the round and SHA
it came from. Cheap gates still run every round.

The scope shrinks, the strictness does not. A fix can break a criterion an
earlier round accepted — that is how regression #102 happened — so the
boundary is the findings plus everything the delta can reach, and a
non-local delta (a rebase onto a moved dev, a behaviour contract change, a
new subsystem) still gets the full pass.

Issue: #214
User-Visible: no
2026-08-20 11:50:16 +03:00
Matysh 72eae1059c perf: skip a heavy gate whose inputs are byte-identical to a green run
Every push to dev paid for the full browser trio and the backend suite,
including commits that touch only documentation, workflows or process
scripts — the bundle and the harness were byte-identical, so the runs
proved nothing new. On 2026-08-19 alone that was roughly six pushes at
about seven minutes each.

The reuse key per heavy job is sourceFingerprint (src, demo fixtures,
golden scenarios, build manifests) plus that job's own harness: smoke
takes demo/smoke_*.mjs, golden takes demo/golden/** including baselines,
performance_smoke takes demo/performance/**, backend takes tests_backend
and the Python sources. A cache marker is written only by a successful run
of the same key, so a hit proves a job with identical inputs already
passed. scripts/** is deliberately outside every key: infrastructure work
edits it constantly and reuse would never fire.

This is not the path filter from the `changes` job, which stays disabled
on dev on purpose: there the scope is guessed from paths and "green" means
different things, here input equivalence is proven by a hash. And a
release candidate always bumps the version, which is part of the
fingerprint, so its keys are new by construction and the full gate set
still runs before every beta and release.

A waived job is announced with a notice and a run summary line rather than
skipped in silence, and the marker save tolerates a concurrent identical
run instead of reddening the job.

Issue: #208
User-Visible: no
2026-08-19 23:33:44 +03:00
Matysh f287bddd97 fix: cache Playwright browsers instead of reinstalling them via apt
performance_smoke burned nearly all of its 15-minute budget before the
benchmark even started, twice in a row: validate.yml had no browser cache
at all, so every browser job paid for a full `playwright install
--with-deps` — apt work the ubuntu-latest image makes redundant, with
unbounded retries against an unreachable azure mirror on top. For a
measuring job that is worse than lost minutes: the timing window competes
with package installation on the same runner.

#175 fixed this for the review pipeline but deliberately left the flag
here, reasoning that a prerelease gate values predictability over
minutes. That reasoning was wrong — the flag is what made the gate
unpredictable.

Browsers are now cached per package-lock hash in smoke, golden,
performance_smoke and the full performance run; installation happens only
on a cache miss and no longer touches apt. performance_smoke keeps
headroom for a cold cache at 20 minutes. If the image ever drops a
required library, Chromium fails to launch with a clear missing-libraries
error; that is the moment to bring the flag back.

Issue: #206
User-Visible: no
2026-08-19 20:06:38 +03:00
Matysh 6e93aa705c docs: fix in-scope Medium findings inside the current issue
Filing and servicing a separate issue costs far more than fixing a small
problem in place — the owner's call of 2026-08-19 (#202). A Medium finding
inside the task's scope no longer becomes its own issue: with no High
findings the verdict is yellow, the author fixes it and the fix passes
another review cycle. Only an out-of-scope Medium is still filed
separately, because foreign scope is never patched from a task branch.

Applied to the canon (PROCESS.md), the reviewer prompt in process.yml and
AGENTS.md; the verdict format now writes "Medium: N -> in-task | #NN".

Issue: #202
User-Visible: no
2026-08-19 13:46:25 +03:00
Matysh e727023bf4 fix: install Chromium without --with-deps in the review pipeline
Validate / smoke (push) Failing after 31s
Validate / golden (push) Failing after 12s
Validate / backend (push) Failing after 19m25s
Validate / performance_smoke (push) Failing after 14m0s
Validate / docs (push) Failing after 27s
Validate / changes (push) Successful in 44s
Validate / provenance (push) Successful in 48s
Validate / process-gate (push) Failing after 46s
Validate / hacs (push) Failing after 12s
Validate / hassfest (push) Failing after 24s
Validate / frontend (push) Successful in 6m14s
On a Playwright cache miss the flag pulled Chromium's system libraries
through apt, spending minutes of the 45-minute review budget on packages
the ubuntu-latest image already ships — and the runner's retries against
the unreachable azure mirror made the step look hung on a live run. If the
image ever drops a required library, Chromium fails to launch with a clear
missing-libraries error; that is the moment to bring the flag back.

validate.yml keeps the flag deliberately: it is the prerelease gate, where
predictability is worth more than minutes.

Issue: #175
User-Visible: no
2026-08-18 20:01:23 +03:00
Matysh ae7fb621e7 fix: fail loudly when a review verdict has no document
On #150 both spec-review verdicts survived only as issue comments: the
publish step found nothing staged, printed a warning, and exited zero, so
the label moved and the missing artifact went unnoticed until the next
review caught it (#171). A verdict without a document in docs/reviews/ now
fails the run before the label step, preserving the invariant that an
unchanged label means a failed run.

An empty working copy alone is not a failure: the reviewer occasionally
commits the document itself through its app token, bypassing this step
(CODE-REVIEW-150-r1, committer GitHub), so the branch is checked first. A
postcondition verifies the exact expected filename reached the branch, and
the rebase-conflict path no longer exits zero either.

Issue: #171
User-Visible: no
2026-08-18 18:02:44 +03:00
Matysh c27185cfa4 fix: verify the PAT before reviewing, pick the freshest task branch
Issue #150 reached a green verdict and then hit two pipeline defects at once.
The review document push came back 403 as github-actions[bot]: the PAT had
died, and checkout's persisted credential quietly took its place — a masked
actor instead of a loud failure. Credentials are no longer persisted, and the
token is now proven alive before the review starts, not after forty minutes of
reviewer work.

Branch selection took the first match alphabetically, and with a spec-era
branch sitting next to the implementation branch that meant the stale one.
The freshest branch by commit date is chosen instead, with a warning naming
every candidate when more than one exists.

Verified against the real #150 branches: the fix branch wins, the warning
fires.

Issue: #114
User-Visible: no
2026-08-18 17:21:06 +03:00
Sergey Matyunin a1b8861eff fix: preserve plan-only room label scale
Issue: #167
User-Visible: yes
2026-08-17 13:16:04 +03:00
Sergey Matyunin 7f397a6875 feat: add plan-only space export
Issue: #167
User-Visible: yes
2026-08-17 12:33:52 +03:00
Sergey Matyunin 18f5155bbf fix: base first-push validation on dev
Validate / backend (push) Failing after 9m11s
Validate / golden (push) Failing after 8m16s
Validate / docs (push) Failing after 22s
Validate / provenance (push) Successful in 33s
Validate / process-gate (push) Failing after 36s
Validate / changes (push) Successful in 26s
Validate / hassfest (push) Failing after 17s
Validate / hacs (push) Failing after 21s
Validate / frontend (push) Successful in 4m44s
Validate / performance_smoke (push) Failing after 2m2s
Validate / smoke (push) Failing after 2m6s
Issue: #165
User-Visible: no
2026-08-16 21:53:41 +03:00
Sergey Matyunin 0253c4765c docs: isolate screenshot tooling from release fixtures
Validate / process-gate (push) Failing after 4m52s
Validate / changes (push) Successful in 4m39s
Validate / provenance (push) Successful in 4m55s
Validate / docs (push) Failing after 5m3s
Validate / hacs (push) Skipped
Validate / hassfest (push) Skipped
Validate / frontend (push) Skipped
Validate / smoke (push) Skipped
Validate / performance_smoke (push) Skipped
Validate / backend (push) Skipped
Validate / golden (push) Skipped
Issue: #35
User-Visible: no
2026-08-16 01:22:55 +03:00
Sergey Matyunin 88a647f6b2 docs: refresh the current user experience guide
Issue: #35
User-Visible: no
2026-08-16 01:12:14 +03:00
Sergey Matyunin 053e2a9b68 ci: move workflows to Node 24 actions
Issue: #145
User-Visible: no
2026-08-16 00:00:36 +03:00
Sergey Matyuninandclaude[bot] 6c48c6d5c6 feat: add architectural snap overlay
Issue: #137
User-Visible: yes
2026-08-14 08:27:45 +00:00
Matysh 6ecbedfb85 ci: heavy Validate jobs run only where relevant paths changed
Validate / changes (push) Successful in 52s
Validate / process-gate (push) Failing after 1m17s
Validate / provenance (push) Successful in 1m20s
Validate / hacs (push) Failing after 16s
Validate / hassfest (push) Failing after 13s
Validate / frontend (push) Successful in 7m30s
Validate / backend (push) Failing after 8m41s
Validate / performance_smoke (push) Failing after 9m45s
Validate / golden (push) Failing after 14m25s
Validate / smoke (push) Failing after 28m53s
Every push to every branch ran 128 browser smokes, 50 golden scenes, a Home
Assistant install and a performance pass — including a push that added one spec
file. The pipeline made such pushes routine: every spec revision and every
review document is a push to a task branch and used to cost the full suite.

A changes job classifies the push range; frontend, smoke, golden, performance
and backend now run only when their paths moved, and hacs and hassfest only for
manifests, translations or Python. provenance and process-gate always run — they
judge commits, not code.

The exception carries the design. On dev everything runs, always, unfiltered:
the beta gate accepts "green Validate at the exact SHA", and if the volume of a
run depends on the diff, green stops meaning one thing — a release candidate
touches manifests and changelogs, would skip the browser suites under filtering,
and a run with skipped jobs still concludes success. That would be the sixth
silent success of the week. Filters save time on task branches, where Validate is
an early signal and the real acceptance is the code review running gates itself.

A new branch with a zero before-sha is classified from the merge-base with dev,
not from the root of history.

Issue: #136
User-Visible: no
2026-08-14 10:16:18 +03:00
Matysh 328ed7afc0 test: a registry of known breakages that tests must catch
Validate / provenance (push) Successful in 46s
Validate / hacs (push) Failing after 10s
Validate / hassfest (push) Failing after 12s
Validate / process-gate (push) Failing after 35s
Validate / frontend (push) Successful in 6m11s
Validate / backend (push) Failing after 9m24s
Validate / golden (push) Failing after 10m10s
Validate / performance_smoke (push) Failing after 12m46s
Validate / smoke (push) Failing after 30m15s
Five times in this project a green test meant nothing was checked. The
continuity smoke stayed green after the entire mechanism it guards was cut out.
The golden scene created to protect doorway light was empty — 1,177 warm pixels
against 107,119, all of them icons. The shadow smoke passed while no shadow was
drawn. Each time the test had been written alongside the code, went green at
once, and nobody ever asked whether it could go red.

The gate makes that question routine. Each mutant is a few lines of patch that
reproduce a known breakage, plus the name of the test that must fail on it. A
worktree is patched, the bundle rebuilt, the guard run — and a guard that stays
green fails the gate. Six mutants cover the holes documented in #85; the anchors
are exact strings from today's source, so the registry cannot silently drift —
a unit test that runs with the ordinary suite refuses a stale anchor.

The full run rebuilds the bundle per mutant, so it lives in its own workflow,
before a stable release and on a weekly schedule, not in Validate. The rules for
new tests are written at the top of docs/TESTING.md, and the sixth of them is
the cheapest: an assertion that reads back the property the code just set is
not written at all.

Issue: #85
User-Visible: no
2026-08-14 02:05:53 +03:00
Sergey Matyunin 50099acc75 fix: allow stable promotion of published beta history
Validate / provenance (push) Successful in 5m46s
Validate / process-gate (push) Failing after 5m56s
Validate / hacs (push) Failing after 20s
Validate / hassfest (push) Failing after 14s
Validate / frontend (push) Successful in 13m53s
Validate / backend (push) Failing after 10m41s
Validate / golden (push) Failing after 9m40s
Validate / performance_smoke (push) Failing after 17m26s
Validate / smoke (push) Failing after 34m54s
Full Performance / performance (push) Failing after 1h54m13s
Issue: #130
User-Visible: no
2026-08-13 22:59:23 +03:00
Matysh 565f518dcd perf: make review scope and ceremony fit the size of the task
The owner's report: the process works but every stage takes a long time even on
simple bugs. Two causes, and neither was the one that first comes to mind.

The reviewer ran everything regardless. On #89 it installed Chromium, ran all 127
smoke files and a full golden capture — right for a task rated 10/10 for
complexity, absurd for a bug about a room divider. Full suites are the pre-beta
gate; the review now runs typecheck, unit and build always, and smokes, golden,
pytest or performance only where the diff and the AC call for them. The price of
narrowing it is honesty: the reviewer must list which gates it ran, which it did
not, and why, so a skipped gate is a visible decision rather than a silent one.

The reviewer also built its own environment out of model turns, with no npm cache
and no browser cache, paid for from the same forty-five minutes. The workflow now
installs dependencies and Chromium as ordinary cached steps, after switching to
the task branch so the lockfile is the branch's own.

Second, ceremony did not scale down. The light track makes a spec cheap; the new
trivial track does without one — S2-analysis straight to S5-ready, no spec review,
AC in the issue body. It is deliberately hard to qualify for: a bug on one surface,
no new UX contract, no migration, no i18n, no perf or touch effect, three checkable
AC at most, and expected behaviour already on record. Nothing left to decide is the
criterion that holds the whole thing up, and it cannot be met by feeling sure.

Code review is never skipped on either track. It is what stands in for testing
here, so it is the one stage speed may not buy.

Issue: #127
Issue: #128
User-Visible: no
2026-08-13 21:59:55 +03:00
Matysh 8a3f6efa0a fix: the review document is published even without a task branch
Issues labelled before the pipeline existed keep their spec straight in dev and
have no issue/NN branch. The publish step quietly exited zero for them, so the
verdict would arrive as a comment and the analysis behind it would be thrown
away — the fifth instance today of a step reporting success by doing nothing.

The document now goes wherever the spec itself lives: the task branch when there
is one, dev otherwise. Publishing also survives dev moving on while the review
ran, which takes up to forty-five minutes, by rebasing once before it gives up.

Four issues are waiting on this — #12, #30, #44 and #52 — each with a spec in dev,
a status label applied during the bulk pass in August and a review that never ran
because nothing was there to raise the event.

Issue: #114
User-Visible: no
2026-08-13 21:11:41 +03:00
Matysh 7c1edbfa9b ci: realign the workflow copy in dev with main
The two copies of this file must match byte for byte; a comment line had drifted
by one character. main is the copy the issues event actually reads, so it is the
reference. Trivial in itself, and worth closing anyway: the file's own header
warns that a divergence between these two branches is one of the ways this
pipeline fails quietly.

Issue: #114
User-Visible: no
2026-08-13 20:53:35 +03:00
Matysh 024cdc0d94 feat: an outsider's issue is worked like any other once admitted
The guard refused to review any issue the owner had not filed himself. The rule
was meant to keep malformed outside reports out of the pipeline, but it checked at
every step instead of at the entrance, and it duplicated a guarantee the platform
already gives: only someone with write access can apply a label. Applying the
first status label is the owner's explicit decision, and it is the only place the
question belongs.

So the author check is gone. While an issue carries no status label it sits
outside the process and the invariants do not apply; once labelled, the task is in
flight and who filed it stops mattering.

The old rule also cost real work. On #123 an outside bug report had been analysed
and specified before the guard turned it away in nine seconds, and the remedy on
offer was to refile the same thing as the owner's own issue.

Issue: #114
User-Visible: no
2026-08-13 20:49:04 +03:00
Matysh 4e539b02df fix: the guard says why it refused, in the issue
A review label promises work. When the guard declined it wrote the reason to the
run log and nothing else, so the issue sat in a status nobody was acting on and
nobody could tell. #123 showed it: an outside reporter's issue was walked up to
S4-spec-review, the guard refused in nine seconds because only the owner's issues
enter the process, and the issue itself said not a word.

Refusals that a human can act on now become a comment: wrong author, blocked,
review-4. Only when a stage was actually recognised, so an unrelated label change
stays silent.

This is the same defect as the merge conflict that left the label untouched, seen
from the other side. The pattern is worth naming: doing nothing quietly is the
most expensive thing a pipeline can do.

Issue: #114
User-Visible: no
2026-08-13 20:36:25 +03:00