Почему: таблица приёмки и разбор C6 должны соответствовать текущим SHA веток Codex, а владелец должен видеть, какие расхождения с C6 остались послеd52ecf3и какая часть красного была ошибкой сценария (типы счётчиков,d477dde), а не продукта. - REPORT: таблица по повторному прогону (b022885,d52ecf3), разборd52ecf3. - NOTES C6: что закрыл d52ecf3; открыто — многострочный JSON, сумма журнала в dry-run, dry-run по умолчанию и обязательные --apply/--actor/--backup-evidence. - NOTES разд. 8: K18 наb022885— ротация на ПК есть, на сервере нет, личные пути в новых файлах.
444 lines
56 KiB
Markdown
444 lines
56 KiB
Markdown
# 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`](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`](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`](report-evidence/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`](report-evidence/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`](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`](report-evidence/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`](report-evidence/round3-pilot-rehome.txt)).
|
||
- Контракт, который проверяет сценарий, — `NOTES-FOR-EXECUTORS.md`, C6.
|
||
|
||
### Круг 3б: основа `f7d3c73` (ACCEPTANCE-3), 16:59 и 17:14 NOVT
|
||
|
||
Храповик **не тронут**: после `git fetch` `git merge-base --is-ancestor origin/<ветка>
|
||
origin/codex/bootstrap-platform-foundation` — `not merged` для grok/e2e-harness, fix-live-database,
|
||
fix-format-baseline, fix-reject-device-shifts, fix-sync-c0-bootstrap, fix-tenant-config,
|
||
fix-tenant-rehome, fix-pilot-backups.
|
||
|
||
Команда: `node scripts/test/accept-branch.mjs <7 веток, не являющихся предками основы>`. Первый прогон (16:59, стенд
|
||
`18f944e`) — [`round3-accept-f7d3c73.md`](report-evidence/round3-accept-f7d3c73.md). Пока шёл разбор, Codex
|
||
запушил `fix-tenant-rehome` d52ecf3 (17:02) и `fix-pilot-backups` b022885 (17:07), поэтому все 7 веток прогнаны
|
||
повторно (17:14, стенд `a6a1b5e`) — [`round3-accept-f7d3c73-rerun.md`](report-evidence/round3-accept-f7d3c73-rerun.md).
|
||
В таблице — повторный прогон; на первом прогоне по c73bb3b и a005c5e результаты те же.
|
||
|
||
| Ветка | SHA | check | integration | e2e red/green | Стало зелёным | Стало красным |
|
||
| ------------------------------------ | ------- | ------------------------------------------------------------------------ | ----------------------------------------------------- | ------------- | ------------------------------- | ------------- |
|
||
| основа | f7d3c73 | OK (195 passed, 1 skipped) | 3/4, FAIL tenant-isolation | 46/5 | n/a | n/a |
|
||
| antigravity/fix-reject-device-shifts | b283736 | OK | 4/5, FAIL tenant-isolation | 44/7 | K13/SYNC-A, K13/SYNC-B | — |
|
||
| antigravity/fix-sync-c0-bootstrap | 24cc18d | OK | 5/6, FAIL tenant-isolation | 43/8 | K13/SYNC-A, K13/SYNC-B, SYNC-C0 | — |
|
||
| codex/fix-format-baseline | a05777c | OK | 3/4, FAIL tenant-isolation | 46/5 | — | — |
|
||
| codex/fix-live-database | 1573860 | OK | 4/5, FAIL tenant-isolation | 44/7 | K3/N2, K4/N1 | — |
|
||
| codex/fix-pilot-backups | b022885 | OK | 3/4, FAIL tenant-isolation | 46/5 | — | — |
|
||
| codex/fix-tenant-config | 535052e | **FAIL** `Error: STANDALONE_TENANT_ID uses the development placeholder…` | 3/4, FAIL tenant-isolation | 46/5 | — | — |
|
||
| codex/fix-tenant-rehome | d52ecf3 | OK | 4/5 (rehome-tenant.test.ts OK), FAIL tenant-isolation | 46/5 | — | — |
|
||
|
||
- `git merge-tree`: 28 пар (7 с основой + 21 между собой) — **все чисто**.
|
||
- «Форматирование без поведения» `a05777c`: `изменено файлов: 19 — ТОЛЬКО ФОРМАТИРОВАНИЕ`.
|
||
- `tenant-isolation.test.ts` — N5 (`permission denied for table staff_employees`), исправление в grok/e2e-harness.
|
||
- accept-branch дополнен (`18f944e`): для `rehome-tenant.test.ts` создаётся пустая `krona_acc_rehome_test`
|
||
(суперпользователь, `DATABASE_REHOME_TEST_CONFIRM` из `docs/TENANT-REHOME.md`) — иначе файл пропускается.
|
||
|
||
**codex/fix-tenant-config — вывод владельца подтверждён.** Лог `npm run check`:
|
||
`FAIL tests/unit/sync.test.ts [ tests/unit/sync.test.ts ]`, `Error: STANDALONE_TENANT_ID uses the development
|
||
placeholder, which is allowed only in development on loopback`, `❯ tests/unit/sync.test.ts:556:18`,
|
||
`Test Files 1 failed | 18 passed | 1 skipped (20)`. В ветке: строка 553 `const tenantId = '01J00000000000000000000000'`,
|
||
556 `loadConfig({`, 563 `NODE_ENV: 'test'`. (У владельца `1 failed | 17 passed`; у меня на один файл больше
|
||
из-за оверлея стенда — `tests/e2e/harness/node-commands.test.ts`.)
|
||
|
||
**codex/fix-tenant-rehome — `[PILOT-REHOME]` красный.** Вывод на ветке:
|
||
`dry-run exit: Tenant rehome stopped. Unknown option --from. … expected 1 to be +0`. Скрипт есть, но интерфейс
|
||
не совпадает с C6 (разница — NOTES, C6). По существу скрипт проверен двумя способами
|
||
([`round3-k18-and-rehome.md`](report-evidence/round3-k18-and-rehome.md)):
|
||
|
||
1. На восстановленной копии (после учебного восстановления K18), интерфейсом продукта: dry-run exit 0 и БД не изменилась;
|
||
apply без `--backup-evidence` → отказ; apply → exit 0, под заглушкой 0 строк, счётчики совпали, audit 2 → 3
|
||
(`tenant.rehomed tenant_rehome`), прочие арендаторы не изменились, ledger `RUB 263568` до и после, FK до и после
|
||
совпали (md5), `krona_staff_app`: 0 под заглушкой / 3 под новым tenant, `UPDATE staff_payroll_ledger` →
|
||
`permission denied`, повтор → `Target tenant already contains staff data; refusing merge`, exit 1.
|
||
2. Сценарий `[PILOT-REHOME]` через диагностический адаптер C6 → c73bb3b (не в продукте и не в сценарии):
|
||
вариант 1 (только перевод флагов и формата) — `expected 'undefined' to be '328228'`: **в dry-run нет суммы
|
||
журнала**; вариант 2 (сумму досчитывает адаптер) — `✓ [PILOT-REHOME]`. Других расхождений сценарий не нашёл.
|
||
|
||
На **d52ecf3** (17:02) Codex добавил синонимы `--from/--to`, `--json`, `DATABASE_ADMIN_URL` и сделал dry-run режимом
|
||
по умолчанию. `[PILOT-REHOME]` всё равно красный: `dry-run без JSON-отчёта: { "mode": "dry-run", … } : expected null
|
||
not to be null` — JSON печатается на нескольких строках, а C6 требует его последней строкой. Если снимать
|
||
расхождения по одному в копии сценария (не в коммите, [`round3-k18-and-rehome.md`](report-evidence/round3-k18-and-rehome.md), разд. 6.3), дальше
|
||
падают: счётчики-строки (`'2'` vs `2`) → **это ошибка сценария**, исправлена в `d477dde` (сравнение через `Number()`);
|
||
`expected 'undefined' to be '375888'` — в dry-run нет суммы журнала; `под заглушкой остались строки` — вызов без
|
||
флага теперь dry-run, а по C6 это перенос, для которого продукт требует `--apply --actor --backup-evidence`.
|
||
Других изменений в сценарии нет.
|
||
|
||
**codex/fix-pilot-backups (K18).** `bash scripts/test-backup.sh` и `bash scripts/test-backup-pull.sh` —
|
||
оба `passed`, exit 0 (моки docker/ssh/scp) и на a005c5e, и на b022885 (b022885 добавил ротацию копий на ПК, 90 дней,
|
||
и пользовательский systemd-сервис; ротации на сервере нет). В `npm run check` и CI они не входят. Учебное восстановление без
|
||
Docker (шим `docker compose exec` → локальный `pg_dump` PG17): `backup.sh` exit 0, на сервере 0 незашифрованных
|
||
файлов, `sha256sum -c` OK, расшифровка ключом вне сервера, `pg_restore --exit-on-error` exit 0,
|
||
`restore-drill-report.sql` на источнике и копии — `идентичны` (79 строк, `RLS|27|t`), isolation:
|
||
`source-employee-count|3`, `target-employee-count|0`. Восстановление в кластер без ролей: `pg_restore` exit 1,
|
||
61 ошибка `role "krona_migrator" does not exist` — в копии нет ролей (globals).
|
||
|
||
## Как запустить
|
||
|
||
```sh
|
||
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:
|
||
|
||
```text
|
||
::: [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
|
||
|
||
```text
|
||
== 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
|
||
|
||
```text
|
||
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
|
||
|
||
```text
|
||
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
|
||
|
||
```text
|
||
$ E2E_MIGRATIONS=roundtrip node scripts/test/run-e2e.mjs # (повторный прогон)
|
||
[run-e2e] OK: стенд работает, все красные сценарии — известные открытые дефекты
|
||
$ git status --porcelain
|
||
# (пусто)
|
||
```
|
||
|
||
## N5 (tenant-isolation)
|
||
|
||
До (`778ba2d`, очистка ролью приложения) — [`report-evidence/n5-before.txt`](report-evidence/n5-before.txt):
|
||
`error: permission denied for table staff_employees`, `exit=1`. После (очистка ролью мигратора с
|
||
`app.tenant_id`) — [`report-evidence/n5-after.txt`](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`:
|
||
|
||
```text
|
||
::: [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`](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` нет; на `codex/fix-tenant-rehome` скрипт проверен на копии
|
||
и через диагностический адаптер (интерфейс не совпадает с C6). `accept-branch` в CI не запускается (разовый инструмент
|
||
владельца/приёмщика). Режим `E2E_MIGRATOR_URL/E2E_APP_URL` проверен на том же локальном кластере
|
||
(роли созданы заранее `provision-pg`), на чужом кластере — не проверено.
|
||
- Круг 3б (K18) без Docker не проверено: настоящий `docker compose exec` в пилотном контейнере, systemd
|
||
timer/service, `pull-pilot-backups.sh` с реальным ssh/scp (только моки из тестов Codex), восстановление
|
||
в контейнер `postgres` с `init-app-role.sh`, хранение ключа и политика хранения. Учебное восстановление Codex
|
||
на пилоте (sha256 `2fc680ae…`, 51 сотрудник) не перепроверялось — данных пилота у меня нет.
|