test(harness): тестовый фасад window.__hpTest и гейт no-new-private-writes (#629)

- scripts/no-new-private-writes.mjs: смоки и demo/helpers/** не добавляют
  записей в приватное состояние карточки (присваивание, ++/--, delete по
  цепочке с сегментом _x; от this — нет) и вызовов _setMode/_openRoomEdit/
  _openMarkerDialog/_openSpaceDialog. Зачёт правки по полю, перенос блока —
  movedLinesByFile из no-new-any; исключение // private-ok: <причина>.
  --count — остаток на HEAD. Подключён в gate:small и в шаг frontend рядом с
  no-new-any, с той же базой.
- demo/helpers/hp-test.mjs: 10 операций через контрактные хуки и события
  фикстуры (setMode, setTool, switchSpace, openRoomEdit, openMarkerDialog,
  openSpaceDialog, setServerConfig, setLayout, input, close); ставится
  launch*() из demo/serve.mjs. В бандле фасада нет.
- demo/srv/demo.html: доставка houseplan_config_updated/_layout_updated,
  __pushServerConfig/__pushServerLayout; после доставки запись со старым
  expected_rev — conflict, как у настоящего сервера.
- HP_SMOKE_CHECKS=1 печатает имена проверок в finish().
- smoke_area_relocation, smoke_glow, smoke_grid_snap переведены на фасад без
  потери утверждений; новый smoke_test_facade доказывает каждую операцию.
- 7 мутантов, docs/TESTING.md (раздел + правило №6), PROCESS.md §2.7, AGENTS.md.

Issue: #629
User-Visible: no
This commit is contained in:
Claude
2026-09-25 01:25:20 +00:00
committed by claude[bot]
parent 035399e7a6
commit fa319e37b3
21 changed files with 1469 additions and 233 deletions
+4
View File
@@ -117,6 +117,10 @@ test('гейт «новый код не добавляет any» вызывае
);
assert.match(frontend, /node scripts\/no-new-any\.mjs --base/,
'гейт обязан вызываться, иначе долг типизации снова начнёт расти');
// #629: тот же шаг и та же база судят записи смоков в приватное состояние.
assert.match(frontend, /node scripts\/no-new-private-writes\.mjs --base "\$base" --head "\$HEAD_SHA"/,
'гейт приватных записей обязан вызываться с той же базой, что no-new-any');
assert.match(frontend, /exit "\$status"/, 'падение первого гейта не должно прятать второй');
// Гейт diff-aware, поэтому без истории он бессмысленен: на глубине 1
// merge-base не считается и диапазон выродится в «всё».
assert.match(frontend, /fetch-depth: 0, filter: 'blob:none'/);