Commit Graph
274 Commits
Author SHA1 Message Date
Claude 0935a4d099 ci: рекалибровать бюджет initial view и предупреждать до стены, а не после
Запас ушёл с 26 КБ до 8.3 КБ за сутки. По документам код-ревью видно, что это не
диффузное расползание, а один шаг плюс обычная работа:

  #317 256127 · #318 256091 · #341 256046 · #354 257212 · #159 256828
  #357 271143  <- +14 КБ за один заход
  #20 271455 · #361 272848 · #359 272469 · #360 273697  <- текущий факт

Шаг на #357 — plan-art мебели: 44 top-view символа в eager-графе, которые платит
каждый план, включая планы без единого предмета мебели.

Потолок 282000 -> 300000. Правило #352 сохранено: 273697 x 1.10 = 301067, то
есть 300000 остаётся внутри надбавки ~10% над измеренным фактом. Запас
возвращается к 26.3 КБ — примерно к тому, что было до #357.

Запись честная и в комментарии сказана прямо: рекалибровка ничего не ускоряет и
ничего не чинит. Она фиксирует новую норму и возвращает гейту способность красить
того, кто вырастил бандл, а не того, кто пушнул последним. Настоящий рычаг —
ленивый граф, варианты 1 и 2 из #367.

Добавлено предупреждение: пока запас меньше 15000 Б, гейт печатает ::warning:: и
строку в summary. Прежняя редакция полагалась на то, что человек заметит тренд в
выводе; за сутки его не заметил никто, потому что каждая отдельная строка
выглядела нормально. Текст предупреждения называет лечение — иначе следующий
читатель поднимет потолок ещё раз и назовёт это решением.

Тест закрепляет обе стороны: надбавка не больше 10% и не меньше 5% (меньше —
возврат лотереи «красит последний коммит»), тревога срабатывает строго ниже
порога, превышение описывается как превышение. Три мутанта проверены руками,
один добавлен в реестр.

Issue: #367
User-Visible: no
2026-08-29 12:32:01 +03:00
Codex 7c45372c32 fix: guard dismissal honesty, one threshold source, EXIF proven (#39 r1)
r1-M1: dismissal (Escape/scrim/Cancel) while the reduce or keep-original
flow is executing no longer races the decision — hp-close is ignored while
busy, and every flow re-checks it still owns the guard before applying, so
a force-cleared dialog can never silently install its stale result. The
smoke now drives both: hp-close during a hanging decode leaves the busy
dialog up, and a force-cleared guard ends with clean staging, no toast, no
planFile. A new registry mutant removes the busy gate and is killed.

r1-M2: the hard-dialog text takes its limit from the imported
HARD_DIMENSION instead of a literal — recalibration stays a one-file
change, as the spec promises.

r1-M3: AC8 is now proven end to end, not plausible: the smoke splices a
real EXIF APP1 (orientation 6) into a canvas-encoded 8200×4100 JPEG,
asserts the header probe reads the unrotated SOF, that the decode call
carries imageOrientation:'from-image' (captured on the hook), and that the
reduced copy comes out portrait 2048×4096. TESTING.md names the scenario.

Issue: #39
User-Visible: no
2026-08-29 10:13:09 +03:00
Codex 7c31725ac9 feat: warn about huge backdrops and offer a safe reduced copy (#39)
A picked raster is now classified from its HEADER BYTES ONLY before anything
heavy happens: src/backdrop-probe.ts parses PNG IHDR (+colour type/tRNS for
alpha), JPEG SOF and WebP VP8/VP8L/VP8X at fixed offsets, never using a file
field as an allocation size; hostile or truncated headers collapse to
'unknown', which warns without numbers instead of passing silently. The
thresholds live in that module as the single calibration point
(WARN_DECODED_BYTES 128 MiB ≈ 32 MP, HARD_DIMENSION 16384 — the browser
canvas cap, DOWNSCALE_TARGET_PX 4096), derived from the desktop-Chromium
matrix now committed as demo/benchmark_backdrop_decode.mjs with a
conservative tablet margin documented in the spec.

The shared pick flow (src/backdrop-pick.ts) feeds BOTH lazy runtimes — the
editor space dialog and the onboarding first-space dialog — so the guard
cannot drift between them, and nothing of it enters the eager View graph.
Warn shows the real numbers and three actions; the reduced copy decodes
EXIF-aware, keeps aspect and alpha (PNG stays PNG, opaque becomes JPEG
q0.9) and flows through the ordinary planFile → upload path. Hard has two
phases with one outcome: beyond 16384 px only Cancel; a failed or timed-out
(10 s) reduce closes with a toast, clean staging and NO silent fallback to
the original the user just declined. SVG never reaches the probe. The safe
path swaps the manual byte-loop base64 for FileReader — half the JS-heap
peak on every upload, byte-identical output (parity asserted in the smoke).

Proofs: header-table units incl. a fuzz set of hostile headers and ±1
threshold bounds; smoke_backdrop_guard on the real bundle — zero decode
calls before the choice, byte parity of keep-original, a real 6200 px
reduce to 4096 for both alpha and opaque branches, cancel-only hard
dialog, both phase-2 failures (reject and hang under the test-only timeout
override), re-pick after refusal, SVG bypass; four registry mutants
(probe-always-safe, alpha-dropped, hard-demoted, phase-2 silent fallback).
Spec anchor corrected alongside: the server plan limit is 8 MB
(MAX_PLAN_BYTES), attachments are the 50 MB path — an 8 MB JPEG is easily
80-160 MP decoded, so the client-side guard stays the primary defence.

Issue: #39
User-Visible: yes
2026-08-29 10:13:09 +03:00
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
Matysh 3c2d9e8863 build: refresh bundle after golden contract (#209)
Issue: #209
User-Visible: no
2026-08-28 23:20:28 +03:00
Matysh 9d2ef12c96 feat: smooth vacuum trails (#209)
Issue: #209
User-Visible: yes
2026-08-28 23:16:58 +03:00
Codexandclaude[bot] 78c6020747 fix: lazy delivery survives flaky networks and stale caches (#353)
Network failure of the editor runtime is no longer terminal: the loader
re-arms to idle and the next explicit press starts a fresh cycle, while a
fingerprint mismatch on either attempt stays terminal. The toast now says
what actually helps — retry advice for the network, refresh advice for a
foreign build — via one shared lazyLoadFailureMessage helper (new i18n key
editor.retry_advice in en/ru/de).

The field smoke caught a second, deeper bug on the way: Chromium records a
FAILED module in the page module map permanently, so retrying the same URL
(even the cache-busted one) never touched the network again. Every retry
now carries a per-cycle nonce and becomes a genuinely new module request.

A proxy-cached stale entry no longer kills the card silently: the entry
facade is rewritten at build time from a static re-export into a top-level
`try{await import(...)}catch{...}` — importers keep the happy-path
guarantee (await import(entry) still resolves only after
customElements.define), and the catch defines a fallback element with a
localized "reload the page" panel. Content-hashed chunks are served with
`public, max-age=31536000, immutable`, and verifyBundleTree now fails on
orphan chunks that the manifest does not name.

Proofs: loader units for re-arm/terminality/toast wording + an AST check
that both loaders forward the terminality flag; smoke_entry_stale (en/ru)
against a tree without the main chunk; smoke_lazy_editor_chunk extended —
second press after network failure now really opens the editor; pytest for
the immutable header; orphan-tree unit; five new registry mutants.
TESTING.md budget line updated to the #352 ceiling alongside.

Issue: #353
User-Visible: yes
2026-08-28 14:32:08 +00:00
Matysh 24c9a169ac fix: require revision for saved layout replacements
Issue: #356
User-Visible: yes
2026-08-28 17:23:01 +03:00
Matysh 6c9752f5e6 fix: follow marker placement for room climate
Issue: #317
User-Visible: yes
2026-08-28 16:48:10 +03:00
Matyshandclaude[bot] 72913fee84 fix: show entityless active controllers
Issue: #318
User-Visible: yes
2026-08-28 13:07:58 +00: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
Matyshandclaude[bot] 3e437ca3ec fix: reject config writes without a revision
Issue: #340
User-Visible: yes
2026-08-28 11:26:19 +00:00
Matyshandclaude[bot] ca16d1fe59 Fix vacuum trail lifecycle persistence
Issue: #335
User-Visible: yes
2026-08-28 11:01:40 +00:00
Matysh 4cdd6d7aac test: prove German dialog layout at narrow widths
Extend the existing localized dialog footer measurements to German at desktop and 320 px. The smoke now checks opening, physical-wall and space dialogs for containment, responsive wrapping and horizontal overflow, closing #348 review r1-M1.

Issue: #348
User-Visible: no
2026-08-28 11:58:47 +03:00
Matysh 12cd8a1af3 feat: add complete German localization
Add Deutsch across all card surfaces and backend flows, backed by the language registry introduced in #62. German loads as a fingerprint-checked page-shared locale chunk so EN/RU remain synchronous and the initial View budget stays intact. Root render gates prevent mixed-language flashes, retry once, and fail open to English. Extend parity, runtime, bundle, browser and visual coverage, plus contributor and user documentation.

Issue: #348
User-Visible: yes
2026-08-28 11:58:46 +03:00
Matyshandclaude[bot] 2c65fcec03 perf: lazy-load editor runtime
Issue: #337
User-Visible: yes
2026-08-28 05:40:28 +00:00
Matyshandclaude[bot] cab8d128bf feat: add device lifecycle catalog
Issue: #29
User-Visible: yes
2026-08-27 23:17:52 +00: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
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 f514fb27fe feat: junction limits refuse the write in every editing surface (#329)
П3 measures the WALL, not the catalogue atom: a short filler segment that
compensates a thickness step (owner's fixture, 5 cm = (30-20)/2) is a legal
continuation of a long same-thickness wall, so the rule walks the maximal
collinear run through the shared nodes before judging the length.

Resize stops at the last allowed position and names the broken rule instead
of the generic "geometry cannot be saved"; the Thickness dialog refuses
through its own toast. Both channels are pinned by demo/smoke_junction_limits
plus three mutants (angle threshold, write barrier, degenerate apex bevel).

Issue: #329
User-Visible: yes
2026-08-27 23:31:42 +03:00
Matysh ba66698f55 refactor: extract resize controller
Issue: #264
User-Visible: no
2026-08-27 20:30:07 +03:00
Codexandclaude[bot] 69facb9918 docs: STATUS and TESTING reflect the default-catalog reality
The HACS row still described an open queue of 835 PRs; the fresh-install
checklist still started from a custom repository.

Issue: #323
User-Visible: no
2026-08-27 11:00:24 +00:00
Matysh a66fb7ef53 feat: unify zero-thickness walls
Issue: #306
User-Visible: yes
2026-08-26 13:39:52 +03:00
Matysh 64e9bf8f6b Cover dense v8 draft invariants
Issue: #314
User-Visible: no
2026-08-26 09:56:35 +03:00
Matysh 2347e8df88 Fix atomic model-v8 draft writes
Issue: #314
User-Visible: yes
2026-08-26 09:13:50 +03:00
Matysh e1059e2e29 fix: preserve wall identity through structural edits
Issue: #282
User-Visible: yes
2026-08-26 02:20:57 +03:00
Matysh b336eee996 feat: stabilize persisted wall segment identity
Issue: #282
User-Visible: yes
2026-08-26 02:20:57 +03:00
Matysh 36992d68bd fix: match wall thickness hover to masonry
Issue: #303
User-Visible: yes
2026-08-25 12:49:41 +03:00
Matysh 8b4730df05 feat: search opening entity selectors
Issue: #301
User-Visible: yes
2026-08-25 12:17:30 +03:00
Sergey Matyunin ba63fccbb7 fix: preserve wall ownership through compaction
Issue: #299
User-Visible: yes
2026-08-24 23:28:39 +03:00
Sergey Matyunin 2d19ce04ad fix: close #298 review gaps
Issue: #298
User-Visible: no
2026-08-24 23:04:32 +03:00
Sergey Matyunin 3fb2edc904 fix: preserve resize wall thickness carriers
Issue: #298
User-Visible: yes
2026-08-24 22:27:05 +03:00
Sergey Matyunin cd88633535 fix: audit resize availability on real plans
Issue: #292
User-Visible: yes
2026-08-24 18:21:50 +03:00
Sergey Matyunin c4694acea9 test: harden resize capture verification
Issue: #293
User-Visible: no
2026-08-24 17:35:45 +03:00
Sergey Matyunin 6c5163c3c9 fix: restore resize pointer gesture
Issue: #293
User-Visible: yes
2026-08-24 17:35:45 +03:00
Sergey Matyunin 3f6338889e test: prove lattice barrier across editor controllers
Issue: #291
User-Visible: no
2026-08-24 17:13:28 +03:00
Sergey Matyunin 4ccf4a3ccc fix: enforce exact lattice coordinates
Issue: #291
User-Visible: yes
2026-08-24 17:13:21 +03:00
Sergey Matyunin edf8b068ed fix: straighten near-axis wall geometry
Issue: #290
User-Visible: yes
2026-08-24 16:30:11 +03:00
Sergey Matyunin 17086d3cf2 fix: preserve walls beyond short junction rays
Issue: #288
User-Visible: yes
2026-08-24 15:57:25 +03:00
Sergey Matyunin bca3bfd52c fix: prevent mixed-role walls during resize
Issue: #289
User-Visible: yes
2026-08-24 15:19:40 +03:00
Sergey Matyunin 07bd1a6781 fix: reconcile outer walls before resize
Issue: #281
User-Visible: yes
2026-08-24 10:43:33 +03:00
Sergey Matyunin 38ea8a9b9e fix: validate optimize opening rehosts
Issue: #280
User-Visible: yes
2026-08-24 10:33:05 +03:00
Sergey Matyunin 79285ab1d2 docs: correct wall union mutation identifiers
Issue: #278
User-Visible: no
2026-08-24 09:30:29 +03:00
Sergey Matyunin 8156f80140 fix: isolate wall union failures and guard geometry writes
Issue: #278
User-Visible: yes
2026-08-24 08:48:37 +03:00
Sergey Matyunin 9c5b66c3e4 perf: fingerprint Resize handles once per frame
Issue: #277
User-Visible: no
2026-08-24 07:56:49 +03:00
Sergey Matyunin b02a37123d test: align mixed-thickness smoke with safe resize
Issue: #277
User-Visible: no
2026-08-24 07:20:25 +03:00
Sergey Matyunin 6335b0e519 fix: make room resize topology-safe
Issue: #277
User-Visible: yes
2026-08-24 07:03:24 +03:00
Sergey Matyunin 82fe98bcd3 test: prove coincident partition acceptance
Issue: #276
User-Visible: no
2026-08-24 05:57:57 +03:00
Sergey Matyunin a605611105 fix: reconcile coincident partitions during Optimize
Issue: #276
User-Visible: yes
2026-08-24 05:27:20 +03:00
Sergey Matyunin 7bcc5b285b fix: preserve orthogonal wall strips at multi-wall joins
Issue: #275
User-Visible: yes
2026-08-24 01:47:22 +03:00