krona-staff/tests/e2e/REPORT.md
grok 986843d8d4 docs(e2e): REPORT и NOTES — круг 3
Почему: TASK-GROK-3 — отчёт с командами и фактическим выводом и новые находки для исполнителей.
REPORT: раздел «Круг 3» (ожидания владельца на SHA из задания — все шесть совпали; проверка
форматирования; все открытые ветки после fetch 16:16 NOVT — конфликтов нет; локальная сборка
«после слияния» — храповик просит убрать ровно K3/N2, K4/N1, K13/SYNC-A, K13/SYNC-B, SYNC-C0;
PILOT-REHOME), новые числа ci-local (46 red / 10 green), «Не проверено».
NOTES: контракт C6 для scripts/rehome-tenant.mjs (с ограничениями схемы: RLS журнала, составные
FK, app.user_id в политиках INSERT) и раздел 7: двойное закрытие пула в sync-bootstrap.test.ts,
неописанный предохранитель live-postgres-remediation.test.ts, N5 в основе, FK outbox_id без tenant_id.
2026-09-26 16:25:47 +07:00

46 KiB
Raw Blame History

PR: e2e-стенд на реальной PostgreSQL 17 и независимая приёмка (grok/e2e-harness)

База: codex/bootstrap-platform-foundation @ f5ac179 (содержит 331e490). Ветка перебазирована без конфликтов. Сравнение: https://git.ochenstarik.ru/ochenstarik/krona-staff/compare/codex/bootstrap-platform-foundation...grok/e2e-harness

Изменены только tests/e2e/**, tests/integration/tenant-isolation.test.ts, .forgejo/workflows/ci.yml, scripts/test/** и одна строка в корневом .gitignore (tests/e2e/evidence/, по требованию TASK-GROK-2 §1). src/, apps/, migrations/, deploy/, package.json не тронуты. Всё, что требует правки продукта, — в NOTES-FOR-EXECUTORS.md.

Зачем

156 зелёных тестов на поддельной БД не заметили, что создание сотрудника, подтверждение смены и расчёт зарплаты на реальной PostgreSQL падают всегда. Стенд запускает реальный сервер под ролью приложения без привилегий на реальной PostgreSQL 17, миграции применяет мигратор без SUPERUSER, все токены и пароли случайные (crypto.randomBytes). Каждый сценарий проверяет правильное поведение: красный = открытый дефект.

Что изменилось во втором круге (TASK-GROK-2)

Пункт Что сделано
§1 Запуск без .cmd-обёрток: scripts/test/node-commands.mjs — всегда process.execPath + JS-точка входа из require.resolve (vitest.mjs, prettier.cjs); сервер — один процесс node --import tsx src/server.ts (без дочернего процесса CLI tsx, чтобы kill() под Windows не оставлял сироту). Проверка на Linux — tests/e2e/harness/node-commands.test.ts (в npm test)
§1 tests/e2e/evidence/ убран из git и добавлен в .gitignore; в CI — артефакт e2e-evidence. Разовые доказательства (N5, N13, приёмка) — в tests/e2e/report-evidence/, скрипты туда не пишут
§1 Задача windows в CI (включается переменной KRONA_WINDOWS_RUNNER=true, раннера пока нет)
§1 Перебазирование на f5ac179 — без конфликтов
§2 Двойные ID [владелец/мой] в названиях тестов и ключах known-defects.json
§3 Новые сценарии [K6/D5], [K7/D2], [K8/D1], [K9/D3], [S9], [A4], [N12-DATE] (tests/e2e/50-money-and-errors.e2e.ts), все красные и в known-defects.json
§4 Приёмка antigravity/fix-reject-device-shifts, codex/fix-format-baseline, codex/fix-live-database — таблицы ниже
— Синтетическая ставка 123,45 → 123,00 ₽/ч: расчёт округляет строку до рубля (rounding_unit_minor), тест не должен зависеть от округления (выяснилось на fix-live-database)

Круг 3 (TASK-GROK-3)

Ветка продолжена поверх ba59a48 без переписывания истории (без rebase на b0243d5, без force-push).

Пункт Что сделано
п.1 scripts/test/accept-branch.mjs <ветка>…: worktree на ветку в ../krona-staff-acceptance/ + оверлей текущего стенда (tests/e2e/**, scripts/test/**), npm run check без переменных БД, каждый tests/integration/*.test.ts отдельно на свежей мигрированной БД PG17 (красный — по строке Test Files, ловит ошибки beforeAll/afterAll; файл, где всё пропущено, — отдельный вердикт SKIP), e2e и разница с основой по ID, попарный git merge-tree --write-tree (все ветки между собой и с основой), итог в markdown. Логи — tests/e2e/evidence/accept/<время>/ (пароли в URL заменены на ***)
п.1 Готовые роли: если заданы E2E_MIGRATOR_URL и E2E_APP_URL, global-setup и accept-branch роли не создают и пароли не меняют (БД — из URL, сброс откатом всех миграций). По умолчанию как раньше. provision-pg.mjs --roles keep пересоздаёт только БД: accept-branch создаёт роли один раз на прогон
п.1 Предохранитель изолированной БД для live-postgres-remediation.test.ts (Codex): в admin-режиме интеграционная БД одноразовая (krona_staff_audit_acc_it), krona_staff_test_guard со случайным маркером, DATABASE_TEST_ISOLATED=true — без этого файл пропускается целиком
п.2 «Форматирование без поведения» (для веток с format в имени и --format-check <ветка>): версия из merge-base → prettier (конфиг основы, API из node_modules) → сравнение с веткой; для лишних строк — сколько из них есть в других переданных ветках
п.3 Прогон по всем открытым codex/*, antigravity/* — ниже
п.4 Храповик не тронут: origin/codex/bootstrap-platform-foundation = b0243d5 (только docs поверх f5ac179); git merge-base --is-ancestor origin/<ветка> origin/codex/bootstrap-platform-foundation — NOT merged для fix-live-database, fix-reject-device-shifts, fix-sync-c0-bootstrap, fix-format-baseline
п.5 [PILOT-REHOME-SEED] (зелёный) и [PILOT-REHOME] (красный, в known-defects.json) — tests/e2e/60-pilot-rehome.e2e.ts
— run-e2e.mjs: проверка секретов в evidence/ больше не падает EISDIR на подкаталоге accept/

п.1: ожидания владельца на SHA из задания

Команда: node scripts/test/accept-branch.mjs 1df2019 antigravity/fix-sync-c0-bootstrap codex/fix-format-baseline (16:04 NOVT; 1df2019 передан SHA, потому что ветка Antigravity к этому времени была перезаписана на 6c5bf36). Полный вывод — report-evidence/round3-accept-owner-shas.md.

Ожидание владельца Фактический вывод
sync-rejection.test.ts на 1df2019 FAIL column "created_by" … does not exist FAIL | 1 failed (1) | 2 skipped (2) | error: column "created_by" of relation "staff_employees" does not exist
sync-bootstrap.test.ts на 8baad55 FAIL Called end on pool more than once при Tests 2 passed FAIL | 1 failed (1) | 2 passed (2) | Error: Called end on pool more than once
SYNC-C0 зелёный на 8baad55 e2e «стало зелёным»: SYNC-C0
AG reject + AG c0 — конфликт в src/domain/sync/service.ts 1df2019 | origin/antigravity/fix-sync-c0-bootstrap | КОНФЛИКТ | src/domain/sync/service.ts
Codex format + AG c0 — конфликт origin/antigravity/fix-sync-c0-bootstrap | origin/codex/fix-format-baseline | КОНФЛИКТ | src/domain/sync/service.ts
Codex format + AG reject — чисто 1df2019 | origin/codex/fix-format-baseline | чисто

Все шесть совпали. На 6c5bf36 (первый прогон по именам веток, round3-accept-owner-branches-first.md) sync-rejection.test.ts уже зелёный (1 passed (1) | 2 passed (2)).

п.2: форматирование без поведения

Команда: node scripts/test/accept-branch.mjs --format-check tmp/prettier-only-f5ac179 --format-check codex/fix-format-baseline (round3-format-proof.md). tmp/prettier-only-f5ac179 (c552d38) — локальная синтетическая ветка: prettier --write . поверх f5ac179, 24 файла; не запушена.

Ветка Результат
tmp/prettier-only-f5ac179 (c552d38) изменено файлов: 24 — ТОЛЬКО ФОРМАТИРОВАНИЕ
codex/fix-format-baseline (513cdff, прежняя голова) ЕСТЬ ИЗМЕНЕНИЯ СВЕРХ PRETTIER: src/domain/sync/service.ts 232 строки, tests/unit/sync.test.ts 130 строк; в antigravity/fix-reject-device-shifts есть 232/232 и 130/130 этих строк
codex/fix-format-baseline (a05777c, текущая, 16:09 NOVT) изменено файлов: 19 — ТОЛЬКО ФОРМАТИРОВАНИЕ

п.3: все открытые ветки

Команда: node scripts/test/accept-branch.mjs <все origin/codex/* и origin/antigravity/* кроме основы> после git fetch в 16:16 NOVT, стенд 6f408bc. Полный вывод (интеграционные тесты по файлам, 55 пар merge-tree) — report-evidence/round3-accept-all-open.md. Основа b0243d5: check OK, integration 3/4 (FAIL tenant-isolation), e2e 46 red / 5 green.

Ветка SHA Влита в основу check integration e2e red/green Стало зелёным Стало красным
antigravity/fix-reject-device-shifts b283736 нет (+1) OK 4/5, FAIL tenant-isolation 44/7 K13/SYNC-A, K13/SYNC-B —
antigravity/fix-sync-c0-bootstrap 24cc18d нет (+3, включает b283736) OK 5/6, FAIL tenant-isolation 43/8 K13/SYNC-A, K13/SYNC-B, SYNC-C0 —
codex/fix-format-baseline a05777c нет (+1, от f5ac179) OK 3/4, FAIL tenant-isolation 46/5 — —
codex/fix-live-database 1573860 нет (+2, на a05777c) OK 4/5, FAIL tenant-isolation (live-postgres-remediation OK) 44/7 K3/N2, K4/N1 —
antigravity/employee-notifications 3e20544 да (предок) OK 3/4 49/2 — HARNESS, PILOT-REHOME-SEED, SYNC-L (старый код: нет таблиц/маршрутов sync)
antigravity/offline-sync-backend 5ee7517 да (предок) OK 3/4 46/5 — —
codex/finance-calendar-dashboard c08a7e6 да (предок) OK 4/4 48/3 A4, K4/N1 CTRL, HARNESS, PILOT-REHOME-SEED, SYNC-L (старый код)
codex/frontend-library 331e490 да (предок) OK 3/4 46/5 — —
codex/security-finance-production f74aa6d да (предок) OK 4/4 49/2 — HARNESS, PILOT-REHOME-SEED, SYNC-L (старый код)
codex/server-web-production 2f6b367 да (предок) OK 4/4 49/2 K4/N1 CTRL, HARNESS, PILOT-REHOME-SEED, SYNC-L (старый код)
  • git merge-tree: конфликтов нет ни в одной из 55 пар (Antigravity выстроил цепочку reject → c0, Codex пересобрал format без sync). Шесть веток — предки основы (уже влиты), для них «стало красным» означает только «код старше основы».
  • tenant-isolation.test.ts красный на основе и всех ветках от f5ac179 (error: permission denied for table staff_employees) — это N5, исправление есть в grok/e2e-harness (ba59a48), в основу ещё не влито.

Сборка «как будет после слияния» (локально, не запушено)

tmp/combined-round3 = b0243d5 + merge codex/fix-live-database (1573860) + merge antigravity/fix-sync-c0-bootstrap (24cc18d) — без конфликтов; tmp/combined-round3-grok = то же + merge grok/e2e-harness. Вывод — round3-combined-merge.md.

Сборка check integration e2e red/green Стало зелёным
combined-round3 (48128c8) OK (199 passed, 6 skipped) 6/7, FAIL tenant-isolation 41/10 K13/SYNC-A, K13/SYNC-B, K3/N2, K4/N1, SYNC-C0
combined-round3-grok (e94415a) OK (199 passed, 6 skipped) 7/7 41/10 то же

Храповик на combined-round3-grok: [run-e2e] FAIL: K13/SYNC-A, K13/SYNC-B, K3/N2, K4/N1, SYNC-C0 — дефект исправлен, обновите known-defects.json — ровно список из TASK-GROK-3 п.4. Удалю их отдельным коммитом, когда ветки действительно окажутся в основе.

п.5: PILOT-REHOME

  • [PILOT-REHOME-SEED] — зелёный: 27 таблиц с tenant_id из information_schema.columns, в каждую 2+ строки заглушки 01J00000000000000000000000 и постороннего случайного арендатора (SQL мигратором, app.tenant_id/app.user_id в транзакции); таблица без сида → красный со списком.
  • [PILOT-REHOME] — красный (в known-defects.json): scripts/rehome-tenant.mjs отсутствует (TASK-CODEX-2 п.2.2): … Засеяно таблиц: 27, ledger SUM(amount_minor)=….
  • Сценарий проверен временным эталоном скрипта (не коммитился, удалён): с ним зелёный; мутации «без записи аудита», «без отказа на повтор», «пропущена таблица», «dry-run пишет» — красные (report-evidence/round3-pilot-rehome.txt).
  • Контракт, который проверяет сценарий, — NOTES-FOR-EXECUTORS.md, C6.

Как запустить

export E2E_PG_ADMIN_URL=postgres://<admin>:<pw>@127.0.0.1:55433/postgres   # только создание ролей и БД
E2E_MIGRATIONS=roundtrip node scripts/test/run-e2e.mjs   # храповик known-defects (как в CI)
node scripts/test/run-e2e.mjs --no-known-defects         # строго: любой красный = exit 1
./scripts/test/ci-local.sh                               # локальный эквивалент verify/format/e2e (bash)
node scripts/test/accept-branch.mjs <ветка> [<ветка>…]   # приёмка веток (worktree, check, integration, e2e, merge-tree)
# роли уже есть (общий кластер): E2E_MIGRATOR_URL=… E2E_APP_URL=… — роли и пароли не трогаются

Таблица по ID (основная ветка f5ac179 + этот стенд)

Команда: ./scripts/test/ci-local.sh (внутри E2E_MIGRATIONS=roundtrip node scripts/test/run-e2e.mjs), 2026-09-26 ~16:25 (UTC+7), PostgreSQL 17.11, стенд круга 3:

::: [check] with_db_env npm run check
 Test Files  20 passed (20)
      Tests  196 passed (196)
::: [format-check] FAIL exit=1      # 23 файла базовой линии основы, ни одного из tests/e2e, scripts/test, .forgejo
 Test Files  6 failed | 1 passed (7)
      Tests  46 failed | 10 passed (56)
[run-e2e] OK: стенд работает, все красные сценарии — известные открытые дефекты

Круг 3 добавил [PILOT-REHOME-SEED] (GREEN) и [PILOT-REHOME] (RED, scripts/rehome-tenant.mjs отсутствует (TASK-CODEX-2 п.2.2)); остальные строки таблицы — без изменений со второго круга.

ID владельца / мой Цвет Фактический вывод (первая строка assert)
HARNESS (×5), CTRL (×2) GREEN 7/7 passed — стенд и позитивные контроли
K1/F2 RED { portal: 200, payroll: 200, write: 201 } вместо 401
K1/SYNC-I RED анонимная регистрация устройства HTTP 201
K1/PILOT-1 RED 14 подстановок без :? (первая STANDALONE_MANAGER_TOKEN)
K2/PILOT-2 RED NODE_ENV=development у сервиса app
K3/N2 RED подтверждение смены HTTP 500 code 23502 null value in column "occurred_at" of "staff_outbox"
K4/N1 RED HTTP 500 code 42P08 inconsistent types deduced for parameter $2
K5/N4 RED блокирован K3 (500 на подтверждении)
K6/D5 RED блокирован K3 (500 на подтверждении)
K7/D2 RED afterFirst: 22200, afterSecond: 22200 вместо 33300, 37700
K8/D1 RED блокирован K3 (500 на подтверждении)
K9/D3 RED params.shift_rate_minor не сохранён (HTTP 200, в params нет ключа)
K10/F3 RED rawTokenInCookie: true
K11/F4 RED все попытки 401, 429 нет
K12/N3 RED подписка на https://127.0.0.1:<port> HTTP 201, 1 входящее соединение
K13/SYNC-A, K13/SYNC-B RED upsert_work_session / upsert_timesheet_entry → status: applied
K17/OWN-10, K17/OWN-11 RED 404 Route GET .../terminal/employees/:id/enrollment / PUT .../biometric-consent (нет API, C3)
K19/OWN-07 RED 404 Route POST /access-grants (нет API, C1/C2)
D6/OWN-01, D6/OWN-02 RED 404 Route POST /access-grants (нет API, C1)
OWN-03..06, OWN-08, OWN-09 RED 404 Route POST /access-grants (нет API, C1/C2)
S2/SYNC-J RED отметка 2001 года → status: applied
S3/SYNC-G RED HTTP 500 code 22007 на весь пакет
S4/SYNC-E RED отметка уволенного → applied
S4/SYNC-F RED { ownOrgUnit: null, foreign: "applied" }
S5/SYNC-C RED HTTP 500 code 42703 column "status" does not exist (блокирован SYNC-C0)
S5/SYNC-D RED изменение из портала не видно в инкрементальном pull
S8/SYNC-H RED регистрация устройства по cookie с чужим Origin HTTP 201
S9 RED удаление точки с устройством: 23502 null value in column "tenant_id"
A4 RED 2 тела 500 с SQLSTATE/текстом PostgreSQL (POST /employees 42P08, month=2026-13 22008)
N9 RED чужой push-endpoint перепривязан HTTP 201
N10 RED терминал: { professions: 200, units: 200 }
N11 RED импорт с неверным kind HTTP 500 code 23514
N12 RED лишнее поле и нереальная сумма → HTTP 200
N12-DATE RED month=2026-13 → HTTP 500 code 22008; 2026-02-30 уже 400
F7 RED повтор Idempotency-Key создал второе событие
F10 RED мутация по cookie без Origin HTTP 201
SYNC-C0 (нет ID у владельца) RED bootstrap pull HTTP 500 code 42703 column "status" does not exist
SYNC-K (нет ID у владельца) RED ack курсора из будущего HTTP 200
SYNC-L GREEN отозванное устройство получает 403 (позитивный контроль)
PILOT-REHOME-SEED GREEN 27 таблиц с tenant_id засеяны (заглушка + посторонний арендатор)
PILOT-REHOME RED scripts/rehome-tenant.mjs отсутствует (TASK-CODEX-2 п.2.2)
PILOT-3 GREEN у app нет учётных данных мигратора
N5 (tenant-isolation) GREEN в npm run check с тестовой БД: Test Files 20 passed (20), Tests 196 passed (196)
N13 (скрипт, не vitest) — не воспроизведено: 0 из 30 попыток при 10/25/50 (обход K3 в одноразовой БД), см. ниже

Сверх таблицы TASK-GROK-2 §2 (по тексту AUDIT.md): K1/SYNC-I (анонимный доступ без токенов на /sync), K1/PILOT-1 (подстановки без :? названы в K1), K2/PILOT-2 (development в пилоте — суть K2).

Приёмка веток исполнителей (TASK-GROK-2 §4)

Способ: git worktree add --detach <dir> origin/<ветка>, поверх — tests/e2e/ и scripts/test/ из этой ветки (в ветки исполнителей ничего не коммитилось), затем E2E_MIGRATIONS=roundtrip E2E_DATABASE_NAME=krona_staff_acc node scripts/test/run-e2e.mjs. «Было» — основная ветка f5ac179 с тем же стендом (45 red / 9 green, храповик OK). Полные таблицы — tests/e2e/report-evidence/acceptance-*.txt. Ни одна ветка не влита в основную (git merge-base --is-ancestor → нет), поэтому known-defects.json не сокращён.

codex/fix-live-database @ ea68e12, перепроверено на b289dd6

== roundtrip OK: rolledBack=11 reapplied=11
      Tests  41 failed | 13 passed (54)
| K13/SYNC-A | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
| K13/SYNC-B | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
| K3/N2 | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
| K4/N1 | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
ID Было Стало Доказательство
K3/N2 RED GREEN было: подтверждение смены 500 23502 occurred_at; стало: passed (8 ч × 123,00 ₽ = 98400 в ledger)
K4/N1 RED GREEN было: 500 42P08; стало: passed (201, сотрудник в БД)
K13/SYNC-A RED GREEN было: upsert_work_session → applied; стало: passed (см. находку по fd1ec3d)
K13/SYNC-B RED GREEN было: upsert_timesheet_entry → applied; стало: passed
K5/N4 RED RED было: блокирован K3; стало: смена должна быть начислена ровно один раз: expected 196800n to be 98400n
K6/D5 RED RED было: блокирован K3; стало: после отклонения оплаченной смены должно быть сторно: expected 98400n to be 0n
K8/D1 RED RED было: блокирован K3; стало: C4 verify: HTTP 404 Route GET .../payroll/runs/:id/verify not found
A4 RED RED было: 2 утечки; стало: 1 — POST /payroll/calculate month=2026-13 → code=22008
F14 RED GREEN prettier --check . → All matched files use Prettier code style! (без оверлея evidence)

Ветку обновили force-push во время приёмки (ea68e12 → b289dd6, изменён только tests/integration/live-postgres-remediation.test.ts); повторный прогон на b289dd6 дал те же 41 red / 13 green и ту же таблицу.

Вердикт: K3 и K4 исправлены (подтверждено на живой PostgreSQL 17, миграции roundtrip). После влития убрать K3/N2, K4/N1 из known-defects.json. Цепочка расчёта теперь доходит до денежных дефектов: K5 и K6 подтверждены по существу.

codex/fix-format-baseline @ fd1ec3d

      Tests  43 failed | 11 passed (54)
| K13/SYNC-A | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
| K13/SYNC-B | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
$ node node_modules/prettier/bin/prettier.cjs --check .
All matched files use Prettier code style!
ID Было Стало Доказательство
F14 RED (23 файла) GREEN All matched files use Prettier code style!
K13/SYNC-A RED GREEN было: applied; стало: passed
K13/SYNC-B RED GREEN было: applied; стало: passed
остальные без изменений — compare по e2e-results.json: других отличий нет

Находка: «style»-коммит fd1ec3d содержит функциональное исправление Antigravity. После нормализации prettier src/domain/sync/service.ts отличается от 331e490 на 255 строк diff и совпадает с antigravity/fix-reject-device-shifts (0 строк); tests/unit/sync.test.ts — 139 и 0. Изменение поведения скрыто в коммите форматирования — смешение тем (AGENTS.md).

antigravity/fix-reject-device-shifts @ 795bba0, перепроверено на 1df2019

      Tests  43 failed | 11 passed (54)
| K13/SYNC-A | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
| K13/SYNC-B | GREEN | 1/1 | зелёный — ИСПРАВЛЕН, уберите из known-defects.json |
ID Было Стало Доказательство
K13/SYNC-A RED GREEN было: upsert_work_session → applied; стало: passed
K13/SYNC-B RED GREEN было: upsert_timesheet_entry → applied; стало: passed
остальные — — других отличий нет

Ветку перебазировали на f5ac179 во время приёмки (795bba0 → 1df2019); повторный прогон на 1df2019: те же 43 red / 11 green, отличия только K13/SYNC-A/B.

Вердикт: K13 исправлен. Убрать K13/SYNC-A, K13/SYNC-B отдельным коммитом после влития (и после исправления очистки в sync-rejection.test.ts, TASK-GROK-2 §4.1).

Windows

  • Запуск стенда переписан без npx и node_modules/.bin (раньше spawn('npx') и spawn('.../.bin/tsx') падали на Windows с ENOENT). Проверка на Linux: tests/e2e/harness/node-commands.test.ts — 8 тестов: команды начинаются с process.execPath, без .cmd/.bin/npx, файлы существуют; под win32 дочерний процесс получает SystemRoot/TEMP, секреты родителя не наследуются.
  • Реальный запуск на Windows 11 — не проверено: Windows-раннера у git.ochenstarik.ru нет. В CI добавлена задача windows (runs-on: windows), включаемая переменной KRONA_WINDOWS_RUNNER=true; для полного e2e нужен секрет WINDOWS_E2E_PG_ADMIN_URL.
  • scripts/test/ci-local.sh — bash, на Windows не предназначен (под Windows запускать node scripts/test/run-e2e.mjs).

Evidence вне git

$ E2E_MIGRATIONS=roundtrip node scripts/test/run-e2e.mjs   # (повторный прогон)
[run-e2e] OK: стенд работает, все красные сценарии — известные открытые дефекты
$ git status --porcelain
                                                             # (пусто)

N5 (tenant-isolation)

До (778ba2d, очистка ролью приложения) — report-evidence/n5-before.txt: error: permission denied for table staff_employees, exit=1. После (очистка ролью мигратора с app.tenant_id) — report-evidence/n5-after.txt: 2 прогона, exit=0. На f5ac179: npm run check с тестовой БД — Test Files 20 passed (20), Tests 196 passed (196).

CI (.forgejo/workflows/ci.yml)

  • verify: postgres:17-alpine@sha256:…, роли как в pilot (мигратор NOSUPERUSER, app NOBYPASSRLS), миграции up → откат всех 11 → up → up, npm run check с тестовой БД, npm audit.
  • format: отдельно (F14 красный до влития codex/fix-format-baseline).
  • e2e: PostgreSQL 17, E2E_MIGRATIONS=roundtrip, храповик; evidence — артефакт e2e-evidence (actions/upload-artifact@ea165f8… # v4.6.2, Forgejo 9.0.3).
  • windows: выключена без раннера (см. выше). docker-build: метка docker-host, needs: verify, только push.
  • Все actions закреплены SHA (F13), SHA совпадают на github.com и code.forgejo.org.

Механизм красных сценариев — храповик known-defects.json, а не «allowed to fail»: сломанный стенд, новый красный ID или исправленный, но не вычеркнутый ID делают задачу e2e красной.

Валидация: actionlint -config-file actionlint.yaml .forgejo/workflows/ci.yml (1.7.12, метки docker, docker-host, windows) → 0 замечаний. Локально ./scripts/test/ci-local.sh:

::: [migrate-roundtrip] OK   (== roundtrip OK: rolledBack=11 reapplied=11)
::: [check] OK               (Test Files 20 passed (20), Tests 196 passed (196))
::: [audit] OK               (found 0 vulnerabilities)
::: [format-check] FAIL      (Code style issues found in 23 files — F14)
::: [e2e] OK                 (Tests 45 failed | 9 passed (54), храповик OK)

N13 (гонка первого расчёта)

node scripts/test/n13-race.mjs (10/25/50 параллельных первых расчётов × 5 попыток) — report-evidence/n13-race.txt: как есть 15/15 → 500 (K3); с обходом K3 в одноразовой БД — 0 некорректных из 30 (с подтверждённой сменой и без). Не воспроизведено, как и у владельца; вероятная причина (гипотеза по коду) — upsert ensureDefaultPayRules сериализует расчёты тенанта. Прогон до перебазирования, на 778ba2d.

Не проверено

  • Реальный Forgejo-раннер (workflow выполнялся только локальным эквивалентом), docker build.
  • Запуск на Windows 11 (раннера нет); Windows-only: DPAPI, ACL каталога данных, установщик, WinUI, named pipe — см. тесты Codex в apps/KronaStaffTerminal.Tests.
  • OWN-01..11, K8 (C4) и K9 (C5) проверяют предполагаемые контракты из NOTES.
  • K5/N4, K6/D5, K8/D1 на основной ветке блокированы K3; по существу проверены только на codex/fix-live-database.
  • S9 проверен удалением SQL-запросом мигратора (публичного API удаления точки нет).
  • Причина невоспроизводимости N13 — гипотеза по коду.
  • Круг 3: scripts/rehome-tenant.mjs не существует — [PILOT-REHOME] проверен только временным эталоном; контракт C6 — предположение. accept-branch в CI не запускается (разовый инструмент владельца/приёмщика). Режим E2E_MIGRATOR_URL/E2E_APP_URL проверен на том же локальном кластере (роли созданы заранее provision-pg), на чужом кластере — не проверено.