Почему: таблица приёмки и разбор 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— ротация на ПК есть, на сервере нет, личные пути в новых файлах.
315 lines
38 KiB
Markdown
315 lines
38 KiB
Markdown
# Заметки для исполнителей (Codex / Antigravity)
|
||
|
||
Автор: Grok (стенд тестов и независимая приёмка). Я не меняю `src/`, `apps/`,
|
||
`migrations/`, `deploy/`; всё, что требует изменения продукта, описано здесь.
|
||
|
||
Как запускать:
|
||
|
||
```sh
|
||
# bootstrap-админ PostgreSQL 17 (только создаёт роли и БД; сервер и миграции под ним не работают)
|
||
export E2E_PG_ADMIN_URL=postgres://<admin>:<pass>@127.0.0.1:55433/postgres
|
||
node scripts/test/run-e2e.mjs # храповик known-defects (как в CI)
|
||
node scripts/test/run-e2e.mjs --no-known-defects # строгий режим: все дефекты красные
|
||
E2E_MIGRATIONS=roundtrip node scripts/test/run-e2e.mjs # + откат всех миграций перед запуском
|
||
./scripts/test/ci-local.sh # локальный эквивалент verify/format/e2e
|
||
```
|
||
|
||
**Правило храповика.** Исправили дефект → его сценарий позеленел →
|
||
`run-e2e.mjs` падает с «дефект исправлен, обновите known-defects.json» —
|
||
удалите ID из `tests/e2e/known-defects.json` в том же коммите. Новый красный
|
||
сценарий, которого нет в списке, — регрессия.
|
||
|
||
## 1. Предполагаемый контракт для решений владельца (OWN-01..11) — Codex, этап 4
|
||
|
||
API пока нет, поэтому сценарии `tests/e2e/30-owner-decisions.e2e.ts` написаны
|
||
против наиболее вероятного контракта. Если выбираете другой — поменяйте тесты и
|
||
этот раздел в том же PR, смысл проверок (кто что может и когда) менять нельзя.
|
||
Все пути относительно `/api/v1/staff`.
|
||
|
||
### C1. Роли точки/бухгалтера/директора (standalone-аналог Core RBAC)
|
||
|
||
- `POST /access-grants` (только главный менеджер, `staff:employee:write`)
|
||
тело `{ "role": "point_manager" | "accountant" | "director", "org_unit_ids": ["<ULID>"], "display_name": "..." }`
|
||
→ `201 { "id", "role", "org_unit_ids", "access_token" }`. Токен показывается один раз,
|
||
хранится как хеш (как ключи сотрудников), используется как `Authorization: Bearer`.
|
||
- `point_manager` видит/считает только свои `org_unit_ids`; чужая точка → `403`
|
||
(и для нового `POST /payroll/batches`, и для старого `POST /payroll/calculate`).
|
||
- В platform-режиме те же роли приходят из Core (User ≠ Employee, AGENTS.md).
|
||
|
||
### C2. Пакет зарплаты точки и согласование
|
||
|
||
| Метод и путь | Кто | Результат |
|
||
| ------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| `POST /payroll/batches` `{org_unit_id, from, to}` | accountant; point_manager своей точки | `201 {id, status:"draft", org_unit_id, lines:[{work_session_id, employee_id, org_unit_id, amount_minor}]}` |
|
||
| `POST /payroll/batches/:id/submit` | только accountant | `200 {status:"submitted"}`; point_manager → `403`; повтор после подписи → `409` |
|
||
| `GET /notifications` | любой пользователь | `200 {notifications:[{type:"payroll.batch.submitted", subject_id:<batchId>}]}`; каждому директору ровно одно уведомление на отправку |
|
||
| `POST /payroll/batches/:id/sign` | только director | `200 {status:"signed"}`; accountant → `403`; если смена пакета изменилась после отправки → `409`, пакет получает `status:"returned"` |
|
||
| `POST /payroll/batches/:id/return` `{comment}` | director | `200 {status:"returned", return_comment}` |
|
||
| `GET /payroll/batches/:id` | accountant, director | `200 {status, return_comment, lines:[...]}` |
|
||
|
||
Инварианты, которые проверяют тесты:
|
||
|
||
- до подписи в `staff_payroll_ledger` для сотрудников пакета нет ни `accrual`, ни `payout` (OWN-05);
|
||
- подпись атомарно пишет `accrual` и `payout`; после подписи `PATCH /work-sessions/:id`
|
||
для смен пакета → `409`, пакет не отправляется повторно (OWN-06);
|
||
- строка пакета принадлежит точке, где отработана смена (`staff_work_sessions.org_unit_id`),
|
||
а не основной точке сотрудника (OWN-09).
|
||
|
||
### C3. Согласие на биометрию
|
||
|
||
- `PUT /employees/:id/biometric-consent` `{ "status": "granted" | "revoked", "document_ref": "..." }`
|
||
(главный менеджер) → `200`.
|
||
- `GET /terminal/employees/:id/enrollment` (терминальный токен) →
|
||
`200 { "employee_id", "registration_allowed": boolean, "reason": "consent_missing" | "consent_revoked" | null }`.
|
||
Без записи о согласии — `false/consent_missing` (OWN-10); после отзыва — `false/consent_revoked` (OWN-11).
|
||
- Шаблоны отпечатков на сервер не передаются (docs/TERMINAL-API-REQUIREMENTS.md);
|
||
сервер хранит только факт согласия. Тот же флаг стоит отдать в `/sync/pull` (employee payload).
|
||
|
||
Сейчас все OWN красные с `404 Route ... not found` (C1–C3 не реализованы);
|
||
OWN-03..09 дополнительно упрутся в K3/N2 (подтверждение смены падает 500).
|
||
|
||
ID в названиях тестов — двойные «владелец/мой» (`[K3/N2]`, `[D6/OWN-01]`, `[K17/OWN-10]`,
|
||
`[K19/OWN-07]`), по таблице TASK-GROK-2 §2 и AUDIT.md.
|
||
|
||
### C4. Проверочный пересчёт подписанной ведомости (K8/D1)
|
||
|
||
- `GET /payroll/runs/:id/verify` (право `staff:payroll:read`) →
|
||
`200 { "run_id", "stored_total_minor", "recalculated_total_minor", "matches": boolean }`.
|
||
- Пересчёт идёт по снимку версий правил, параметров и ставок, действовавших при подписи
|
||
(и ревизиям смен), а не по текущим значениям. После смены ставки `matches` остаётся `true`.
|
||
|
||
### C5. Параметры пресета в назначении оплаты (K9/D3)
|
||
|
||
- `PUT /employees/:id/pay-assignment` принимает `params: { <key>: integer }`, ключи — из
|
||
`param_declarations` выбранного правила (например, `shift_rate_minor`, `percent_bp`,
|
||
`upsell_rate_minor`, `avg_check_target_minor`, `avg_check_bonus_minor` у `waiter_upsell`);
|
||
неизвестный ключ → 400. `GET .../pay-assignment` возвращает их в `params`.
|
||
- Старые поля `rate_minor`/`percent_bp`/`guarantee_minor` можно оставить как сокращения.
|
||
|
||
### C6. Перенос арендатора пилота (PILOT-REHOME, TASK-CODEX-2 п.2.2)
|
||
|
||
Что проверяет `tests/e2e/60-pilot-rehome.e2e.ts` (`[PILOT-REHOME]`):
|
||
|
||
- Запуск: `DATABASE_ADMIN_URL=<админ БД> node scripts/rehome-tenant.mjs --from 01J00000000000000000000000 --to 01M2R6AC5652NPT3MG34DKN4ZP [--dry-run] [--json]`.
|
||
Стенд передаёт `DATABASE_ADMIN_URL` суперпользователя в БД стенда (`E2E_DB_ADMIN_URL` или
|
||
`E2E_PG_ADMIN_URL` с именем БД).
|
||
- `--json`: последней строкой stdout — `{"tables":{"<таблица>":<строк у --from>},"ledger_amount_minor_sum":<число или строка>}`
|
||
(счётчики — число или строка с числом).
|
||
Таблицы с нулём можно не печатать.
|
||
- `--dry-run`: exit 0, те же числа, в БД ничего не меняется.
|
||
- Перенос: exit 0; под `--from` 0 строк во **всех** таблицах с `tenant_id` (список из
|
||
`information_schema.columns`, не вручную); у `--to` те же счётчики и та же
|
||
`SUM(amount_minor)` в `staff_payroll_ledger`; другие арендаторы не тронуты.
|
||
- В `staff_audit_log` у нового арендатора появляется запись с `action`, содержащим `rehome`
|
||
(например, `tenant.rehomed`, старый tenant — в `new_values`).
|
||
- После переноса `krona_staff_app` видит тех же сотрудников (те же `id`) под новым tenant и никого
|
||
под старым; `UPDATE staff_payroll_ledger` ролью приложения по-прежнему отклоняется.
|
||
- Повторный запуск (под `--from` пусто / у `--to` уже есть данные) — exit ≠ 0, ничего не меняет.
|
||
|
||
Что нужно учесть (видно по схеме, `pg_policy`/`pg_constraint` на PG17):
|
||
|
||
- У `staff_payroll_ledger` нет политики UPDATE/DELETE, у приложения нет права UPDATE, FORCE RLS
|
||
действует и на владельца: `krona_migrator` строки журнала не перенесёт. Нужна роль с обходом RLS
|
||
(суперпользователь/BYPASSRLS) — поэтому `DATABASE_ADMIN_URL`.
|
||
- Внешние ключи составные `(tenant_id, …)` и не DEFERRABLE: переносить родителя раньше детей
|
||
нельзя — нужен порядок, отложенные проверки или `session_replication_role = replica` в одной транзакции.
|
||
- Политики INSERT `staff_audit_log`, `staff_payroll_ledger`, `staff_attendance_events`,
|
||
`staff_timesheet_entries` требуют `actor_id/created_by/updated_by = current_setting('app.user_id')` —
|
||
для записи аудита без обхода RLS выставить `app.user_id`.
|
||
|
||
**Расхождение с `codex/fix-tenant-rehome` (c73bb3b, 16:48 NOVT; ACCEPTANCE-3 с просьбой держаться C6 — 16:35).**
|
||
Сценарий на ветке: `dry-run exit: Tenant rehome stopped. Unknown option --from.` По существу скрипт делает
|
||
всё, что проверяет сценарий (см. REPORT «Круг 3б»); не совпадает только интерфейс:
|
||
|
||
| C6 (сценарий) | c73bb3b | Итог в сценарии |
|
||
| ------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------- |
|
||
| `--from`, `--to` | `--source`, `--target` | `Unknown option --from`, exit 1 |
|
||
| без флага — перенос | нужен `--apply`, без режима — `Specify --dry-run or --apply.` | — |
|
||
| — | `--apply` требует `--actor <ULID>` и `--backup-evidence <json>` | у сценария нет ни того, ни другого |
|
||
| `--json` | неизвестный флаг → exit 1; JSON печатается всегда | — |
|
||
| `DATABASE_ADMIN_URL` | `DATABASE_REHOME_URL` (запасной вариант `DATABASE_MIGRATION_URL`) | — |
|
||
| одна строка JSON, числа | многострочный JSON (`JSON.stringify(…, null, 2)`), счётчики — строки | парсер сценария ищет JSON в последней строке |
|
||
| `ledger_amount_minor_sum` в dry-run и apply | в dry-run суммы нет; в apply `payrollLedger: [{currency, amountMinor}]` | с адаптером: `expected 'undefined' to be '328228'` |
|
||
| `tables` — строки у `--from` | в apply `tables` — у `--to` после переноса (`staff_audit_log` +1) | не проверяется |
|
||
|
||
**d52ecf3 (17:02)** закрыл строки 1, 4, 5 таблицы: `--from/--to`, `--json`, `DATABASE_ADMIN_URL` принимаются. Без
|
||
флага теперь **dry-run** (по C6 — перенос), так что запуск сценария без `--apply` не переносит данные. Открытыми
|
||
остаются многострочный JSON (сценарий: `dry-run без JSON-отчёта`), сумма журнала в dry-run и обязательные
|
||
`--apply --actor --backup-evidence` (разд. 6.3 в `report-evidence/round3-k18-and-rehome.md`). Типы счётчиков
|
||
расходились из-за ошибки сценария; она исправлена в d477dde, сравнение идёт через `Number()`.
|
||
|
||
Какой интерфейс оставить, решает владелец. Варианты: (а) Codex добавляет в скрипт C6-совместимые
|
||
`--from/--to/--json`, `DATABASE_ADMIN_URL`, сумму журнала в dry-run, а `--actor`/`--backup-evidence`
|
||
берёт из переменных окружения (например, `REHOME_ACTOR_ID`, `REHOME_BACKUP_EVIDENCE`); (б) владелец утверждает
|
||
интерфейс c73bb3b — тогда я меняю сценарий под него отдельным коммитом, а сумму журнала в dry-run
|
||
оставляю требованием (TASK-GROK-3 §4 п.3: «`--dry-run` — ничего не меняет, печатает те же числа», то есть числа п.2, включая `SUM(amount_minor)`). В TASK-CODEX-2 §2.2 п.4 dry-run
|
||
описан как «только отчёт о количестве строк по таблицам», то есть по этому пункту c73bb3b задание
|
||
выполняет, а расходится только с C6.
|
||
|
||
## 2. Дефекты продукта, найденные сценариями (все красные сейчас)
|
||
|
||
Команда и фактический вывод — `tests/e2e/evidence/e2e-run.txt`; таблица — `tests/e2e/REPORT.md`.
|
||
|
||
| ID | Файл | Что исправить |
|
||
| ---------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||
| K4/N1 | `src/portal.ts` POST /employees | `42P08 inconsistent types deduced for parameter $2`: явные `$2::varchar(26)`, `$5::text` или отдельный запрос профессии |
|
||
| K3/N2 | `src/portal.ts` PATCH /work-sessions, `src/domain/payroll/service.ts` calculate/approve | `staff_outbox.occurred_at` NULL (23502): передавать `occurred_at` или `DEFAULT now()` новой миграцией |
|
||
| K5/N4 | `service.ts` | после N2 проверить: пересекающийся период не должен начислять смену повторно (409 или исключение уже начисленных смен) |
|
||
| K12/N3 | push-подписки | allowlist push-сервисов / запрет приватных адресов, `redirect:'manual'`; сейчас сервер подключается к `127.0.0.1` |
|
||
| N9 | `push-dispatcher.ts` | `ON CONFLICT ... DO UPDATE SET employee_id` → 409 для чужого endpoint |
|
||
| N10 | GET /professions, /org-units | требовать право справочника; терминал сейчас получает 200 |
|
||
| N11 | импорт | валидировать `kind` и схему `items` → 400 вместо 500 (23514) |
|
||
| N12 | PUT pay-assignment | `additionalProperties:false`, верхние границы сумм |
|
||
| N12-DATE | `POST /payroll/calculate` | `month=2026-13` → 500 `22008`; проверять месяц 01–12 → 400 (диапазон `2026-02-30` уже даёт 400) |
|
||
| K6/D5 | `portal.ts` PATCH /work-sessions | смена подписанной ведомости: `409`, либо сторно в ledger; сейчас (на fix-live-database) 200 и начисление остаётся |
|
||
| K7/D2 | `operations.ts` импорт `payroll_entries` | дельта по сумме всех записей дня, вставка по ключу: две точки за день → 222,00 вместо 333,00 ₽; считать дельту по ключу |
|
||
| K8/D1 | расчёт/подпись | снимок правил и ставок в подписанной ведомости; контракт проверки C4 (ниже) |
|
||
| K9/D3 | PUT pay-assignment | сохранять `params` по `param_declarations` правила (C5 ниже); сейчас `shift_rate_minor` официанта теряется |
|
||
| S9 | миграция 0011 FK устройств | `ON DELETE SET NULL` на составном `(tenant_id, org_unit_id)` обнуляет `tenant_id` → 23502; `SET NULL (org_unit_id)` или RESTRICT |
|
||
| A4 | обработчик ошибок | в теле 5xx нет SQLSTATE (`code`) и текста PostgreSQL; подробности только в лог |
|
||
| K1/F2 | `config.ts`/`identity.ts` | standalone без токенов не должен выдавать права (процесс без токенов отдаёт 200 на /portal, /payroll, POST /professions) |
|
||
| K10/F3 | POST/DELETE /session | в cookie — идентификатор сессии, а не сырой токен; logout отзывает сессию |
|
||
| K11/F4 | вход | rate limit (тест ждёт 429 не позже 30 неверных попыток; идёт на отдельном процессе, чтобы лимит не мешал другим сценариям) |
|
||
| F7 | POST /attendance/events | использовать `Idempotency-Key` / `staff_idempotency_keys` |
|
||
| F10 | `assertSameOrigin` | мутации по cookie без `Origin` отклонять |
|
||
| K13/SYNC-A, K13/SYNC-B | `src/domain/sync/service.ts` | устройство не должно выставлять `confirmed`/`approved` (сейчас `applied`) |
|
||
| SYNC-C0 | `pull()` bootstrap | `SELECT ... status FROM staff_org_units` — такого столбца нет → 500 на любой начальной загрузке |
|
||
| S5/SYNC-C | `pull()` bootstrap | при `has_more=true` отдаётся `next_cursor=latest`, остаток после 100 сущностей теряется; нужен курсор снимка |
|
||
| S5/SYNC-D | портал/импорт | изменения вне sync не пишутся в `staff_sync_change_log` → терминал не узнаёт о новых/изменённых сотрудниках |
|
||
| S4/SYNC-E | `executeCommand` | проверять `status='active'` сотрудника |
|
||
| S4/SYNC-F | `executeCommand` | сохранять `org_unit_id` в `staff_attendance_events`; payload не может переопределить точку устройства |
|
||
| S3/SYNC-G | `push()` | ошибки БД одной команды → `rejected` для неё, а не 500 и откат всего пакета |
|
||
| S8/SYNC-H | `/sync/devices` | `assertCookieMutationOrigin` как в портале |
|
||
| K1/SYNC-I | `requireManagerContext` | без токенов не выдавать права (как F2) |
|
||
| S2/SYNC-J | `executeCommand` | окно времени отметки как в POST /attendance/events (−180 дней … +5 мин) |
|
||
| SYNC-K | `acknowledge()` | отклонять курсор больше `MAX(cursor_seq)` |
|
||
|
||
## 3. deploy/ (K1/PILOT-1, K2/PILOT-2) — Codex
|
||
|
||
- PILOT-1: все подстановки в `deploy/pilot/compose.yaml` → `${VAR:?VAR is required}`
|
||
(сейчас 14 без `:?`: пустой `STANDALONE_MANAGER_TOKEN` молча превращает pilot в F2).
|
||
- PILOT-2: `NODE_ENV: development` в `app`. Конфликт: `config.ts` запрещает
|
||
`STAFF_RUNTIME_MODE=standalone` при `NODE_ENV=production`. Нужно решение: либо
|
||
разрешить standalone в production при обязательных токенах и HTTPS, либо
|
||
отдельный режим для pilot. Тест требует только «не development».
|
||
- N6: `scripts/create-app-role.mjs` не работает мигратором без SUPERUSER
|
||
(«Only roles with SUPERUSER may change the SUPERUSER attribute»). CI и e2e
|
||
обходятся `scripts/test/provision-pg.mjs` (роли создаёт bootstrap-админ, как
|
||
`docker/postgres/init-app-role.sh`). В pilot мигратор = `POSTGRES_USER` =
|
||
суперпользователь; рекомендую схему как в provision-pg.mjs.
|
||
|
||
## 4. CI
|
||
|
||
- Задаче `docker-build` нужен раннер с меткой `docker-host` (Docker daemon).
|
||
Без него задача ждёт в очереди; остальные задачи не блокируются.
|
||
- `format` красная, пока не исправлен F14 (23 файла на замер 2026-09-26, в основном `src/`, `docs/`, `tests/unit/`).
|
||
- `format` станет зелёной после влития `codex/fix-format-baseline` (проверено на ветке).
|
||
- Задача `windows` выключена, пока нет раннера с меткой `windows`: включается переменной
|
||
репозитория `KRONA_WINDOWS_RUNNER=true`; для полного e2e нужен секрет
|
||
`WINDOWS_E2E_PG_ADMIN_URL` (локальная PostgreSQL 17 на раннере).
|
||
- `tests/e2e/evidence/` не хранится в git; в CI это артефакт `e2e-evidence`.
|
||
- Реальный Forgejo-раннер мной не запускался (нет API-токена) — не проверено.
|
||
|
||
## 5. N13 (гонка первого расчёта) и утечка текста ошибок БД
|
||
|
||
- `scripts/test/n13-race.mjs`, вывод `tests/e2e/evidence/n13-race.txt`: 10/25/50
|
||
параллельных первых `POST /payroll/calculate` × 5 попыток. Как есть — все 500 (N2),
|
||
гонку не видно. В одноразовой БД с обходом N2 (`DEFAULT now()` только там) — 0 из 30
|
||
некорректных попыток (без смен и с подтверждённой сменой): один `run_id`, один расчёт в БД.
|
||
- Почему не воспроизводится (вывод из кода, не проверено отдельным экспериментом):
|
||
`calculatePayroll` сначала вызывает `ensureDefaultPayRules`, а там
|
||
`INSERT ... ON CONFLICT DO UPDATE` по одним и тем же строкам `staff_pay_rules` тенанта.
|
||
Первая транзакция держит блокировки этих строк до коммита, остальные ждут — расчёты
|
||
одного тенанта выполняются по очереди случайно. Если upsert правил вынесут из расчёта
|
||
или сделают `DO NOTHING`, окно `SELECT ... FOR UPDATE` (строки нет) → `INSERT ... ON
|
||
CONFLICT DO NOTHING` со своим `runId` → строки/UPDATE по несуществующему `runId` вернётся.
|
||
Рекомендация: явный `pg_advisory_xact_lock` по (tenant, org_unit, period_start) или
|
||
`INSERT ... ON CONFLICT DO UPDATE ... RETURNING id`. Скрипт стоит перезапустить после
|
||
такого изменения.
|
||
- Ответы 500 содержат сырой текст ошибки PostgreSQL (например
|
||
`null value in column "occurred_at" of relation "staff_outbox" ...`) — клиенту видна
|
||
схема БД. Для 5xx отдавать нейтральное сообщение, подробности — только в лог.
|
||
|
||
## 6. Приёмка веток исполнителей (2026-09-26)
|
||
|
||
Подробно — `tests/e2e/REPORT.md` и `tests/e2e/report-evidence/acceptance-*.txt`.
|
||
|
||
- `codex/fix-live-database` (ea68e12, перепроверено на b289dd6): K3/N2 и K4/N1 зелёные на живой PostgreSQL 17.
|
||
Дальше по цепочке открылись K5/N4 (196800 вместо 98400 — двойное начисление) и
|
||
K6/D5 (отклонение оплаченной смены: 200, начисление осталось). A4 частично: `POST /employees`
|
||
больше не 500, но `month=2026-13` по-прежнему отдаёт `code=22008` с текстом.
|
||
- `codex/fix-format-baseline` (fd1ec3d): `prettier --check .` чистый. **Но коммит «style»
|
||
содержит функциональное исправление Antigravity**: `src/domain/sync/service.ts` и
|
||
`tests/unit/sync.test.ts` после нормализации prettier совпадают с
|
||
`antigravity/fix-reject-device-shifts` (0 строк diff) и отличаются от 331e490 на 255/139
|
||
строк. Изменение поведения стоит вынести в отдельный коммит с объяснением или влить
|
||
ветку Antigravity отдельно, иначе история смешивает темы (AGENTS.md).
|
||
- `antigravity/fix-reject-device-shifts` (795bba0, перепроверено на 1df2019 — уже на f5ac179):
|
||
K13/SYNC-A и K13/SYNC-B зелёные, остальное без изменений.
|
||
- Ни одна ветка не влита в основную (f5ac179), поэтому ID из `known-defects.json` не удалены.
|
||
- Суммы в тестах кратны рублю: расчёт округляет строку до `rounding_unit_minor`
|
||
(по умолчанию 100 коп.). Если округление должно быть другим — это решение продукта, не теста.
|
||
|
||
## 7. Круг 3 (TASK-GROK-3, 2026-09-26)
|
||
|
||
Команды и полный вывод — `tests/e2e/REPORT.md` («Круг 3») и `tests/e2e/report-evidence/round3-*.md`.
|
||
Приёмка одной командой: `node scripts/test/accept-branch.mjs <ветка>…` (нужен `E2E_PG_ADMIN_URL`
|
||
или готовые роли в `E2E_MIGRATOR_URL` + `E2E_APP_URL`).
|
||
|
||
- **Antigravity, `sync-bootstrap.test.ts` (8baad55)**: `Error: Called end on pool more than once` при
|
||
`Tests 2 passed`. Причина: `app.close()` в тесте вызывает хук `onClose` → `database.close()`
|
||
(`src/app.ts:148`), затем `afterAll` закрывает тот же `Database` ещё раз. На текущей голове
|
||
`24cc18d` файл зелёный (`1 passed (1) | 2 passed (2)`).
|
||
- **Antigravity, `sync-rejection.test.ts` (1df2019)**: `column "created_by" of relation "staff_employees"
|
||
does not exist` в `beforeAll` (`Tests 2 skipped`). На `6c5bf36`/`b283736` исправлено.
|
||
- **Конфликты**: на прежних головах `1df2019 × 8baad55` и `8baad55 × 513cdff` конфликтовали в
|
||
`src/domain/sync/service.ts`; на текущих головах (fetch 16:16 NOVT) конфликтов нет ни в одной из 55 пар.
|
||
- **Codex, `codex/fix-format-baseline`**: на `513cdff` в `service.ts`/`sync.test.ts` было 232/130 строк сверх
|
||
prettier, все есть в `antigravity/fix-reject-device-shifts`; на `a05777c` — только форматирование (19 файлов).
|
||
- **Codex, `live-postgres-remediation.test.ts`**: выполняется только при `DATABASE_TEST_ISOLATED=true`,
|
||
`DATABASE_TEST_GUARD_TOKEN`, таблице `krona_staff_test_guard` с этим маркером и имени БД
|
||
`krona_staff_audit_*`. Соглашение нигде, кроме самого теста, не описано, и ни CI, ни
|
||
`scripts/test/*` его не выполняют — в `npm run check` файл всегда `skipped`. `accept-branch` ставит
|
||
предохранитель в одноразовой БД: `1 passed (1)`. Предложение: описать соглашение в README/AGENTS
|
||
и после слияния включить его в CI (могу сделать в своей зоне, `.forgejo/workflows/ci.yml`).
|
||
- **N5 до сих пор в основе**: `tenant-isolation.test.ts` красный на `b0243d5` и всех ветках от
|
||
`f5ac179` (`permission denied for table staff_employees`); исправление — в `grok/e2e-harness`.
|
||
Локальная сборка основа + fix-live-database + fix-sync-c0-bootstrap + grok/e2e-harness:
|
||
интеграционные 7/7, храповик просит убрать ровно K3/N2, K4/N1, K13/SYNC-A, K13/SYNC-B, SYNC-C0.
|
||
- **Схема (низкий приоритет)**: `staff_notification_deliveries.outbox_id → staff_outbox(id)` — единственный
|
||
внешний ключ без `tenant_id`; все остальные составные `(tenant_id, …)`. База не мешает доставке
|
||
ссылаться на событие outbox другого арендатора (RLS фильтрует чтение, но не ссылку). Предложение:
|
||
`UNIQUE (tenant_id, id)` на `staff_outbox` и FK `(tenant_id, outbox_id)`.
|
||
- **PILOT-REHOME**: скрипта нет → `[PILOT-REHOME]` красный; контракт — C6.
|
||
|
||
## 8. Круг 3б (основа f7d3c73, 2026-09-26 ~17:00 NOVT)
|
||
|
||
Вывод — `tests/e2e/report-evidence/round3-accept-f7d3c73.md`, `round3-k18-and-rehome.md`.
|
||
|
||
- **codex/fix-tenant-config (535052e)**: `npm run check` красный —
|
||
`tests/unit/sync.test.ts:556:18` → `STANDALONE_TENANT_ID uses the development placeholder…`
|
||
(строка 553 — заглушка, 563 — `NODE_ENV: 'test'`). Вывод владельца подтверждён; как исправить —
|
||
ACCEPTANCE-3 (разрешить заглушку при `NODE_ENV === 'test'`).
|
||
- **codex/fix-tenant-rehome (c73bb3b → d52ecf3)**: по существу перенос верный (копия после восстановления: счётчики,
|
||
ledger, FK, аудит `tenant.rehomed`, изоляция, отказ повтора). `[PILOT-REHOME]` красный из-за интерфейса —
|
||
таблица различий в C6. Интеграционный тест `rehome-tenant.test.ts` зелёный только с
|
||
`DATABASE_REHOME_TEST_URL`/`DATABASE_REHOME_TEST_CONFIRM` — в `npm run check` и CI он всегда `skipped`.
|
||
Тест требует подключения суперпользователя (`rehome-tenant.test.ts:92-93`) и содержит один `it` —
|
||
запуск скрипта от не-суперпользователя (как `krona_migrator` в пилоте) не покрыт.
|
||
- **codex/fix-pilot-backups (a005c5e → b022885, K18)**: `test-backup.sh`/`test-backup-pull.sh` проходят (моки), но в
|
||
`npm run check` и CI не входят. Учебное восстановление без Docker прошло (шим docker → pg_dump PG17,
|
||
CMS, sha256, pg_restore, `restore-drill-report.sql` совпал). Замечания:
|
||
- `backup.sh` снимает только `pg_dump -Fc` одной БД: ролей (globals) в копии нет. В кластере без ролей
|
||
`pg_restore` → exit 1, 61 ошибка `role "krona_migrator" does not exist`. Шаг 4 runbook («Restore required
|
||
roles and grants») нужно сделать исполнимым: скрипт создания ролей для восстановления или
|
||
`pg_dumpall --globals-only --no-role-passwords` рядом с копией.
|
||
- `restore-drill-isolation.sql` жёстко зашит на `01J00000000000000000000000` → `01M2R6AC5652NPT3MG34DKN4ZP`;
|
||
после переноса «source/target» поменяются местами — стоит параметризовать (`psql -v`).
|
||
- В `docs/BACKUP-RESTORE.md` личный путь `C:/Users/trush/Documents/KronaStaffBackups/` — в репозитории
|
||
лучше указать роль или место без имени пользователя.
|
||
- b022885: ротация копий на ПК есть (`BACKUP_LOCAL_RETENTION_DAYS`, 90 дней; в `test-backup-pull.sh` есть проверка),
|
||
ротации на сервере нет. В новых `scripts/run-pilot-backup-pull.ps1` и `deploy/pilot/backup.home-server.example`
|
||
тоже зашиты личные пути. Контроль отказов таймера и задачи pull пока описан только в документе.
|
||
- Предложение (моя зона, после слияния): добавить в CI `bash scripts/test-backup.sh`,
|
||
`bash scripts/test-backup-pull.sh` и запуск `rehome-tenant.test.ts` / `live-postgres-remediation.test.ts`
|
||
на одноразовых БД — так же, как это делает `accept-branch`.
|