docs: ledger SDD Среза 1 (находки, рулинги, итоги check-full) перед удалением рабочих каталогов
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
7f891ddb48
commit
67c2d457c5
297
docs/superpowers/plans/2026-10-04-slice-1-directories-ledger.md
Normal file
297
docs/superpowers/plans/2026-10-04-slice-1-directories-ledger.md
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
# Ledger Среза 1 (справочники) — SDD фазы 1 и 2, финальное ревью
|
||||||
|
|
||||||
|
Скопирован из `.superpowers/sdd/` перед удалением рабочих каталогов. Ветка feat/5-slice-1-directories, база Среза 44806c8, HEAD на момент копирования 7f891dd (+ этот коммит).
|
||||||
|
Маркеры: T<n>.<k> — находки ревью задач; C/I/M-n — финальное ревью; R-n — рулинги (design #n+11 для R-12..., см. design.md); O-n — наблюдения.
|
||||||
|
|
||||||
|
---
|
||||||
|
## Фаза 1
|
||||||
|
|
||||||
|
# SDD ledger — plan: docs/superpowers/plans/2026-10-04-slice-1-directories-phase1.md
|
||||||
|
|
||||||
|
Spec: openspec/changes/slice-1-directories/ (reachable). Branch feat/5-slice-1-directories, worktree .wt/slice-1/arm-core. Base at start: 44806c8.
|
||||||
|
Нумерация: R-1 уже в design.md (#12). Рулинги контроллера с R-2; decisions в design.md с №13.
|
||||||
|
Модели: T1,T2,T4,T6,T8 + ревью — opus; T3,T5,T7 + ревью — sonnet.
|
||||||
|
|
||||||
|
## Pre-flight: таблица конфликтов
|
||||||
|
|
||||||
|
| Пара/задача | produces vs consumes | находка |
|
||||||
|
|---|---|---|
|
||||||
|
| T1 -> всё | `RelatedGroupMember.kind`, `*_CODE_SEQ`, `AuditAction.*` | T5/T8 импортируют `COMPANY_GROUP_CODE_SEQ`/`RELATED_GROUP_CODE_SEQ` из `src/models/directories.py` — совпадает |
|
||||||
|
| T2 -> T3,T5..T8 | `get_async_session` (commit), `DomainError`, `RequestAudit`, `Page/PageParams` | T3 override `get_async_session` через `begin_nested` — совместимо с R-1. tasks.md 1.2 говорит `get_uow_session` — расхождение уже снято R-1 (design #12). dev_auth/auth.current_user используют `get_async_session`, коммитов не делают (проверено grep) |
|
||||||
|
| T2 | существующий `common/exceptions.py` (`NotFoundError(Exception)`, `CouldntCreateError`) | `NotFoundError` перепишется в подкласс `DomainError`; `CouldntCreateError` нигде не используется (grep) — удалить безопасно |
|
||||||
|
| T3 -> T5..T8 | фабрики `make_*`, `auth_client(email)`, `app` | сигнатуры совпадают с использованием в T5/T6/T8 тестах (`office=`, `group=`, `related_group=`) |
|
||||||
|
| T4 -> T5,T6,T8 | `VisibilityScope`, `*_visible`, `office_for_create` | `legal_entity_is_visible`/`company_group_is_visible` в репозиториях не нужны (фильтр в SQL) — остаются как plan-mandated pure helpers; не дефект |
|
||||||
|
| T5 -> T6,T7,T8 | `repositories/directories.py`, `get_company_group(scope)`, `get_scope`, `next_code` | T6 и T8 добавляют в тот же файл/роутер: последовательно, конфликтов нет |
|
||||||
|
| T6 -> T7,T8 | `get_legal_entity(scope)`, `close_memberships` | T7 добавляет маршруты в `legal_entities.py` T6; T8 использует `get_legal_entity` |
|
||||||
|
| T5 vs spec | plan Task 5 тест 6: `q` по имени; spec directories-api: `q` по `name`/`crm_id`/`code` | R-2: спека главнее — `q` ищет по name/crm_id/code |
|
||||||
|
| T5 vs spec | plan не указывает порядок history; spec: по убыванию `valid_from` | R-3: history ГК (и ЮЛ) — по убыванию `valid_from` |
|
||||||
|
| T6 vs spec | spec `q` ЮЛ: подстрока имени/ИНН; plan: `q` без уточнения | R-4: `q` ищет по name и inn |
|
||||||
|
| T7 vs spec | plan `LEGAL_FORMS` шире spec (ООО/АО/ПАО/ЗАО/ИП) | §3 «ОПФ (ООО, АО, ПАО, ИП…)» — многоточие, расширенный список допустим; не конфликт |
|
||||||
|
| T8 vs spec | plan `delete` удаляет закрытые строки состава; spec молчит | допустимо (OQ-34), FK иначе блокирует; без рулинга |
|
||||||
|
| внутренняя согласованность T1..T8 | тесты vs код vs файлы | T1: файлы перечислены полностью; тест уникальности ГСК+ГСЗ согласован с индексом. T2: интеграционный тест через scratch_db_url согласован с кодом. T3–T8: тесты соответствуют интерфейсам. Противоречий нет |
|
||||||
|
| Global Constraints vs rubric | verbatim-дубли/пустые тесты | не обнаружено |
|
||||||
|
|
||||||
|
Rulings (pre-flight):
|
||||||
|
- R-2: `q` в списке ГК ищет по name/crm_id/code (спека) — план говорит только об имени — цена: нет, надмножество. (design #13)
|
||||||
|
- R-3: история ГК/ЮЛ отдаётся по убыванию `valid_from` (спека) — цена: нет. (design #14)
|
||||||
|
- R-4: `q` ЮЛ ищет по name и inn (спека) — цена: нет. (design #15)
|
||||||
|
|
||||||
|
Task 1: dispatched (opus, implementer ab4cdc613d6744c3f), BASE=13d11c0
|
||||||
|
Task 1: implementer DONE_WITH_CONCERNS (commit 458392e). Ruling: R-5 (design #16) — миграция 0002 пересоздаёт related_group_member (copy→drop→create в порядке DBML→insert), а не ADD COLUMN: тест Среза 0 сверяет порядок колонок с create_all; цена ошибки — вернуть ADD COLUMN и ослабить сравнение. Также поправлены test_dbml_parity и test_offline_sql (offline UPDATE вместо INSERT) — вынужденно. Dispatching review (opus).
|
||||||
|
Task 1: review (opus) — spec OK, quality Approved; Critical/Important: none.
|
||||||
|
T1.1 minor: нет LOCK TABLE перед CTAS в 0002 (потеря параллельной записи; сейчас риск 0) — deferred
|
||||||
|
T1.2 minor: пересоздание теряет GRANT/RLS/триггеры вне Alembic; нет в docstring миграции — deferred (отражено в design #16)
|
||||||
|
T1.3 minor: docstring downgrade не упоминает падение при ЮЛ в ГСК и ГСЗ одновременно — deferred
|
||||||
|
T1.4 minor: тест сохранения данных не проверяет legal_entity_id/valid_from/created_by — deferred
|
||||||
|
T1.5 minor: в плане `-k related_group_member` не выбирает тестов (использовали -k create_all) — deferred
|
||||||
|
Task 1: complete (commits 13d11c0..458392e, review clean; R-5 = design #16 в 3a0ee80, D-1 уточнён c80e029)
|
||||||
|
Task 2: dispatched (opus, implementer aab564bc92cf06234), BASE=c80e029
|
||||||
|
Task 2: implementer DONE (commit 5976986). Ruling R-6 (design #17): SessionDep (scope="function") — обязателен для всех следующих задач (`session: SessionDep` из src/api/dependencies/db.py; guard-тест). Прочие решения T2 (№2-5 отчёта: extra с code/message → ValueError, статусы через fastapi.status, константы Page) — мелкие, приняты. Observation O-1: tasks.md 1.2 всё ещё называет get_uow_session/get_audit_service (устарело по R-1/R-6; править при архивации). O-2: индекс codegraph отстаёт — Read/Grep.
|
||||||
|
Task 2: review (opus) — spec OK, Approved; Critical/Important: none.
|
||||||
|
T2.1 minor: тест одной сессии не проверяет общность сессии current_user и маршрута (держится на ключе кэша FastAPI + guard-тесте) — deferred
|
||||||
|
T2.2 minor: guard-тест обходит только build_api_router, не src.app.app.routes — deferred
|
||||||
|
T2.3 minor: tasks.md 1.2 называет get_uow_session/get_audit_service (устарело) — deferred (правка текста tasks.md при финале фазы; O-1)
|
||||||
|
T2.4 minor: DomainError не переживает pickle/copy (args) — deferred
|
||||||
|
Task 2: complete (commits c80e029..5976986, review clean; R-6 = design #17 в cd6cca3)
|
||||||
|
Task 3: dispatched (sonnet, implementer ac5cf3be1f90b1c92), BASE=cd6cca3
|
||||||
|
Task 3: implementer DONE (8eb8592). Concern: warning build_verifier не подавлен вне test_me (O-3 кандидат). Review dispatched (sonnet).
|
||||||
|
Task 3: review (sonnet) — spec OK, Approved; Critical/Important: none.
|
||||||
|
T3.1 minor: warning build_verifier ("Включён dev-эмитент") не подавлен в общей фикстуре app (только в test_me autouse) — deferred → поручено Task 5 (конфтест app: подавить через monkeypatch src.core.auth.logger, убрать локальный autouse); статус `fixed` фиксируется в Task 5
|
||||||
|
T3.2 minor: pytest.raises(IntegrityError) в test_factories не проверяет конкретное ограничение — deferred
|
||||||
|
T3.3 minor: AuthClientFactory импортируется из tests.conftest — deferred
|
||||||
|
T3.4 minor: docstring conftest не упоминает новые фикстуры — deferred
|
||||||
|
Task 3: complete (commits cd6cca3..8eb8592, review clean)
|
||||||
|
Task 4: dispatched (opus, implementer afd5eb6c8cf3e7fb9), BASE=8eb8592
|
||||||
|
Task 4: implementer DONE (d142c6f). Решения: legal_entities_visible через подзапрос IN (а не outerjoin) — разрешено (семантика та же); company_groups_visible требует CompanyGroup во FROM (учесть в Task 8 get_related_group — join явно); O-4: tasks.md 2.1 называет apply_scope_*/assert_office_allowed (устарело, брифы опираются на plan names). Review dispatched (opus).
|
||||||
|
Task 4: review (opus) — spec OK, Approved; Critical/Important: none. Ruling R-7 (design #18): подзапрос IN в legal_entities_visible; ГК/монозаёмщики без филиала видны только ГО.
|
||||||
|
T4.1 minor: company_groups_visible молча даёт декартово произведение, если CompanyGroup нет во FROM — deferred (Task 8 get_related_group обязан join'ить CompanyGroup явно; передано в бриф)
|
||||||
|
T4.2 minor: тест с JOIN вызывающего проверяет только FROM, не WHERE подзапроса — deferred
|
||||||
|
T4.3 minor: лишние проверки подстрок в тесте — deferred
|
||||||
|
T4.4 minor: tasks.md 2.1 называет apply_scope_*/assert_office_allowed — deferred (синхронизировать текст tasks.md в конце фазы; O-4)
|
||||||
|
Task 4: complete (commits 8eb8592..d142c6f, review clean)
|
||||||
|
Task 5: dispatched (sonnet, implementer ae35074f0d4d0fe82), BASE=01187b5
|
||||||
|
Task 5: implementer DONE (d85ecd2, 80e8905). T3.1: fixed d85ecd2. Решения исполнителя приняты: R-8 (design #19).
|
||||||
|
Task 5: review (sonnet) — spec OK, Approved; Critical/Important: none.
|
||||||
|
T5.1 minor (informational): глобальный pre-check crm_id раскрывает существование строки crm_id — rejected как дефект (спека требует глобальную уникальность 409), записан в design #19
|
||||||
|
T5.2 minor: эндпоинт regional_offices вызывает repo напрямую (мимо services) — deferred
|
||||||
|
T5.3 minor: эндпоинт ГК пересобирает PageParams/CompanyGroupFilter из CompanyGroupListQuery — deferred
|
||||||
|
T5.4 minor: set_archived(bool) с noqa FBT вместо двух методов — deferred
|
||||||
|
T5.5 minor: OFFICE_NOT_FOUND не проверяет is_active (учтено в design #19) — deferred
|
||||||
|
T5.6 minor: пробелы тестов (multi-field PATCH, повторный archive без аудита, неизменность чужой ГК, collation в сортировке) — deferred
|
||||||
|
T5.7 minor: conftest заменяет logger заглушкой только с warning (logger.disable устойчивее) — deferred
|
||||||
|
Task 5: complete (commits 01187b5..80e8905, review clean)
|
||||||
|
O-5: RegionalOfficeOut дублируется в schemas/me.py и schemas/directories.py — OpenAPI-компоненты с длинными именами; решить в фазе 2 (фронт/OpenAPI).
|
||||||
|
Task 6: dispatched (opus, implementer a8549ee04ec10f373), BASE=69769cc
|
||||||
|
Task 6: implementer DONE (347b46e). Review (opus): spec OK, Needs fixes.
|
||||||
|
T6.1 IMPORTANT: office_for_create применяется к regional_office_id до сравнения с текущим — PATCH с тем же значением от пользователя филиала даёт 422 (нарушает Review Focus 1) — fix round 1 dispatched (resume implementer a8549ee04ec10f373); fix base 347b46e
|
||||||
|
T6.2 minor (M-1): тест отката не доказывает откат аудита (аудит пишется после close_memberships) — deferred
|
||||||
|
T6.3 minor (M-2): сообщение OFFICE_NOT_ALLOWED про ГК возвращается и для ЮЛ — deferred
|
||||||
|
T6.4 minor (M-3): дублирование _strip_required_name/_check_office_exists/_now между ГК и ЮЛ — deferred (вынести в Task 8 если третий потребитель)
|
||||||
|
T6.5 minor (M-4): pre-check INN/CRM_ID не защищён от гонки (IntegrityError → 409 общим обработчиком, текст общий) — deferred
|
||||||
|
Task 6: fix round 1/5 (1 addressed, 0 open — T6.1 fixed 462a30f; commits 347b46e..462a30f); re-review (sonnet): all addressed, no new Critical/Important.
|
||||||
|
T6.6 minor (re-review): явный null от пользователя филиала при текущем офисе NULL пропускается как no-op (до фикса — превращался в свой офис); не покрыто тестом — deferred
|
||||||
|
Task 6: complete (commits 69769cc..462a30f, review clean after 1 fix round). Решения исполнителя приняты: R-9 (design #20).
|
||||||
|
Task 7: dispatched (sonnet, implementer aa1f12b4cd4ecdaac), BASE=e03f979
|
||||||
|
Task 7: implementer DONE (7007c51, a19875d). Решения: ALIAS_TAKEN отдаёт только legal_entity_id владельца; чужой/несуществующий алиас — одинаковый 404; alias хранится дословно, пробельная строка → EMPTY_ALIAS. O-6: DIRECTORY_DELETE по seed только у ADMIN (DELETE алиасов/ГСК доступен только admin) — матрица Среза 0, не менялась; отметить человеку. Review dispatched (sonnet).
|
||||||
|
Task 7: review (sonnet) — spec OK, Approved; Critical/Important: none. Решения исполнителя приняты: R-10 (design #21).
|
||||||
|
T7.1 minor: гонка двух POST одного алиаса → IntegrityError → общий 409 без кода ALIAS_EXISTS (в отчёте исполнителя ошибочно указано 500) — deferred
|
||||||
|
T7.2 minor: find_alias_owner LIMIT 1 при теоретических дублях может дать ALIAS_TAKEN вместо ALIAS_EXISTS — rejected (сервис сам не допускает дублей; только гипотетические legacy-данные)
|
||||||
|
Task 7: complete (commits e03f979..a19875d, review clean)
|
||||||
|
Task 8: dispatched (opus, implementer a8179e82b2dfb35b5), BASE=69d641c
|
||||||
|
Task 8: implementer DONE (e3ea074, 88a4b6d, a04d9db). Решения: add_member возвращает (член, ЮЛ); список ГСК/ГСЗ массивом (не Page); DELETE members = DIRECTORY_EDIT по спеке; состав в before_value при удалении группы; новый src/common/clock.py (utc_now). Review dispatched (opus).
|
||||||
|
Task 8: review (opus) — spec OK, Approved; Critical/Important: none. Решения исполнителя приняты: R-11 (design #22).
|
||||||
|
T8.1 minor: удаление группы, на которую ссылаются consolidation/ex_snapshot (FK), даст 500 вместо 409 — deferred (к OQ-34, Срез 3/7: GROUP_IN_USE)
|
||||||
|
T8.2 minor: гонки pre-check → IntegrityError → общий 409/500 без доменного кода (общая проблема всех сервисов справочников; см. T6.5, T7.1) — deferred
|
||||||
|
T8.3 minor: список ГСК/ГСЗ — массив, а не Page (Global Constraints: списки {items,total,limit,offset}) — deferred (решить в фазе 2 до фронта)
|
||||||
|
T8.4 minor: нет теста DELETE чужой ГСК пользователем филиала — deferred
|
||||||
|
Task 8: complete (commits 69d641c..a04d9db, review clean)
|
||||||
|
O-1/O-4: tasks.md 1.2/2.1 приведены к фактическим именам (commit после design #22).
|
||||||
|
|
||||||
|
---
|
||||||
|
## Фаза 2 и финальное ревью
|
||||||
|
|
||||||
|
# SDD ledger — plan: docs/superpowers/plans/2026-10-04-slice-1-directories-phase2.md
|
||||||
|
|
||||||
|
Spec: openspec/changes/slice-1-directories/ (reachable). Branch feat/5-slice-1-directories, worktree .wt/slice-1/arm-core. Base фазы 2: b5a8324 (начало Среза 1: 44806c8).
|
||||||
|
Ledger фазы 1: ../2026-10-04-slice-1-directories-phase1/progress.md (T1.1–T8.4, O-1…O-5, R-2…R-11).
|
||||||
|
Нумерация: рулинги R-12 (design #23), R-13 (design #24) приняты сессией; далее R-14 (design #25+). Наблюдения O-7+ (O-6 фазы 1 — DIRECTORY_DELETE только у ADMIN). Находки T9.k…T15.k.
|
||||||
|
Модели: T9,T10,T12,T14 + ревью — opus; T11,T13,T15 + ревью — sonnet; финальное ревью — opus.
|
||||||
|
|
||||||
|
## Pre-flight: таблица конфликтов
|
||||||
|
|
||||||
|
| Пара/задача | produces vs consumes | находка |
|
||||||
|
|---|---|---|
|
||||||
|
| T9 -> T10 | ядро `plan_import`, `ImportReport.to_dict`, `read_rows`, `FileFormatError`, операции | T10 потребляет все операции и `DirectorySnapshot`; совпадает. Снимок строит T10 (`load_snapshot`) |
|
||||||
|
| T10 vs код фазы 1 | план требует `apply_group_update(session, audit, group, *, name, office_id, now)` и `apply_entity_update(...)` «вынесенные» из сервисов; в коде есть только классы `CompanyGroupService`/`LegalEntityService` (функций нет) | R-14 (ниже): T10 выносит внутреннюю логику update в модульные функции в `services/*.py`, `CompanyGroupService.update`/`LegalEntityService.update` вызывают их; поведение API и тесты фазы 1 не меняются |
|
||||||
|
| T10 vs spec | план: `dry_run` «`await session.rollback()` … в тестах — откат SAVEPOINT»; фикстура `app` оборачивает запрос тестов в `begin_nested` | rollback внутри SAVEPOINT-обёртки корректен; T10 обязан убедиться тестом подсчёта строк, что откат реально откатывает вставки (а не только expire) |
|
||||||
|
| T10 vs T9 (Review Focus 4) | повторный dry_run расходует `nextval` | допустимо (дыры в кодах), тест сравнивает отчёты, не коды |
|
||||||
|
| T10 vs спека | план: «`storage is None` — S3Object не создаётся» | согласовано с dev без S3; не конфликт |
|
||||||
|
| T11 -> T10 | fixtures-тест использует эндпоинт T10; пороги `offices_created == 53`, `groups_created == 345`, UNSUPPORTED_TYPE ≥ 500 | цифры плана не проверены — ожидание исполнителю: сверить с фактом и при расхождении с планом описать (не подгонять код под числа без анализа) -> R-14b? нет: ruling не нужен, фиксируется в отчёте |
|
||||||
|
| T12 vs R-13 | `UserOut.regional_office: RegionalOfficeOut` | R-13 применяется в T12 (одна модель) |
|
||||||
|
| T12 vs spec users-admin | план: `LAST_ADMIN` при снятии роли/права/деактивации; спека D-8 «защита от снятия USERS_ADMIN с себя/последней роли» | план строже/равен спеке; смысл — «последнее право USERS_ADMIN у активных»; проверка перед записью. Конфликта нет |
|
||||||
|
| T12 vs Global Constraints | «пользователей не удалять; email lower unique» — в DBML уникальность email? | исполнитель проверяет DBML/индекс (`lower(email)`); если уникальный индекс только по email — проверка на уровне сервиса (план так и требует) |
|
||||||
|
| T12 -> T13/T14 | `UserOut`, `RoleOut`, `PermissionOut`, `Page[UserOut]`, `/directories/regional-offices` | T13 types.ts дублирует контракт вручную; T14 использует. Совпадение имён полей проверяет ревью T13/T14 по схемам T12 |
|
||||||
|
| T13 -> T14 | `apiFetch`, `ApiError`, `useMe`, `AuthGate`, `DevLogin` | T14 использует `useMe`/`api`; Admin.tsx вызывает `useMe()` повторно (второй запрос /me) — допустимо; ревью T14 проверит отсутствие запросов `/admin/*` без права |
|
||||||
|
| T13 vs Global Constraints | `web/src/utils.ts` «удалить или не трогать» | не трогать, если не мешает; не дефект |
|
||||||
|
| T14 vs spec | spec users-admin «минимальный экран»: sidebar-группа «Система» | совпадает с планом |
|
||||||
|
| T15 vs T9..T14 | README/CLAUDE.md документируют фактические имена; `tasks.md` все `[x]`; design.md — рулинги | рулинги вносит контроллер по ходу (после каждой задачи), T15 лишь сверяет |
|
||||||
|
| T15 vs scope | `make check-full` требует `../fixtures`/`../docs`, compose, docker | доступны по постановке |
|
||||||
|
| внутренняя согласованность T9 | тест `-k`/ожидания: `report.rows==21, groups_created==1, offices_created==1, entities_created==18, ГСК с 2 членами` — план велит «зафиксировать по факту», не угадывать | OK; `type_raw` для ФЛ: `UNSUPPORTED_TYPE`; 21 строка: 18 ЮЛ + ФЛ/дубль — по факту |
|
||||||
|
| внутренняя согласованность T10 | тест «срез РБ»: «в БД 1 ГК, 18 ЮЛ, 1 ГСК (2 члена)» vs Task 9 | совпадает |
|
||||||
|
| внутренняя согласованность T12 | Review Focus 5: самодеактивация | покрыт тестом и требованием |
|
||||||
|
| Global Constraints vs rubric | verbatim-дубли/пустые тесты | не обнаружено |
|
||||||
|
|
||||||
|
Rulings (pre-flight):
|
||||||
|
- R-12 (design #23), R-13 (design #24): приняты сессией.
|
||||||
|
- R-14: T10 выносит update-логику ГК/ЮЛ в модульные функции `apply_group_update`/`apply_entity_update` (в плане названы как существующие, но их нет) — альтернатива «дублировать код update в сервисе импорта» нарушает DRY; цена ошибки: небольшой рефакторинг фазы 1 в T10, регрессия закрыта тестами фазы 1; откат — вернуть дублирование. (design #25 — будет внесён после T10 вместе с остальными решениями исполнителя)
|
||||||
|
|
||||||
|
Task 9: dispatched (opus, implementer a92fc138de6b6b79f), BASE=dd176a3
|
||||||
|
Task 9: implementer DONE_WITH_CONCERNS (df8a528). Concerns: фактически 20 ЮЛ/ИП (не 18), DUPLICATE_IN_FILE в файле РБ не возникает; полная выгрузка: groups_created=344 (ГК 1-5AL0CDM целиком DUPLICATE_IN_FILE), LE_IN_OTHER_GROUP невозможен на пустой БД. Пины openpyxl, et_xmlfile, types-openpyxl. 12 решений исполнителя (report).
|
||||||
|
Rulings: R-14 (design #25) apply_group_update/apply_entity_update выносятся в T10; R-15 (design #26) 20 ЮЛ/ИП, groups_created=344, LE_IN_OTHER_GROUP на пустой БД недостижим — спека/план/tasks.md исправлены; R-16 (design #27) решения исполнителя T9 приняты. Commit chore: см. git log (после df8a528).
|
||||||
|
Task 9: review (opus) — spec OK; Needs fixes.
|
||||||
|
T9.1 IMPORTANT (I-1): повреждённый xlsx (битый sheet/workbook/styles XML, неверная ссылка ячейки) → сырое ParseError/ValueError вместо FileFormatError UNSUPPORTED_FILE — fix round 1
|
||||||
|
T9.2 IMPORTANT (I-2): нет лимита строк/размера — xlsx 5 КБ с r="2000000" съедает 297 МБ (материализация пустых строк) — fix round 1 (стриминг, лимит строк, проверка суммарного zip file_size)
|
||||||
|
T9.3 minor (M-1): числовой ИНН теряет ведущий ноль (0274… → 9 цифр → INVALID_INN, видимый отказ) — deferred (follow-up/OQ-88)
|
||||||
|
T9.4 minor (M-2): csv row = номер записи, а не строки файла (reader.line_num) — включено в fix round 1 (дёшево)
|
||||||
|
T9.5 minor (M-3): CreateRelatedGroup возможна без последующего AddMember (пустая группа) — deferred, зафиксировано в design #27
|
||||||
|
T9.6 minor (M-4): пустые Организация/Группа компаний при непустом iD создают сущность с name="" — deferred (вопрос OQ; допущение в README Task 15)
|
||||||
|
T9.7 minor (M-5): при существующей ГК + skip новый РФ из файла всё равно создаётся (CreateOffice без ссылок) — deferred (буквальный порядок §13.2)
|
||||||
|
T9.8 minor (M-6): параметризованный тест не покрывает DUPLICATE_IN_FILE/LE_UPDATED/GK_UPDATED/in-file GK_NAME_CONFLICT (покрыты отдельными тестами); нет теста GK-обновление + LE_IN_OTHER_GROUP — deferred
|
||||||
|
Task 9: fix round 1/5 dispatched (resume a92fc138de6b6b79f); FIX_BASE=7f14e70
|
||||||
|
Task 9: fix round 1/5 (3 addressed, 0 open — T9.1 fixed 1364f43, T9.2 fixed 1364f43, T9.4 fixed 1364f43; commits 7f14e70..1364f43); re-review (sonnet): all addressed, no new Critical/Important.
|
||||||
|
T9.9 minor (re-review): workbook.close() в генераторе _xlsx_rows происходит при GC, не детерминированно (BytesIO, fd не течёт) — deferred
|
||||||
|
T9.10 minor (re-review): широкий кортеж исключений вокруг итерации может скрыть программную ошибку как «плохой файл» — deferred
|
||||||
|
T9.11 minor (re-review): MAX_UNCOMPRESSED_BYTES=200 МиБ щедрый; sharedStrings грузится целиком; не ограничены колонки/длина ячейки — deferred (кандидат на 50 МиБ)
|
||||||
|
O-7: MAX_IMPORT_ROWS=100_000 считает и пустые строки: xlsx с отформатированными пустыми хвостами >100k будет отвергнут (UX: пояснить в README Task 15).
|
||||||
|
Task 9: complete (commits dd176a3..1364f43, review clean after 1 fix round). Решения исполнителя: R-16 (design #27).
|
||||||
|
Task 10: dispatched (opus, implementer ad5722f28c02ecade), BASE=1364f43
|
||||||
|
Task 10: implementer DONE_WITH_CONCERNS (e373dfc refactor R-14, c9c3b69 feature). Concerns: rollback под SAVEPOINT-фикстурой сносит seed — dry_run-тест на отдельной БД; 413 после spool multipart Starlette; архивные ГК в снимке; HTTPException(413) из сервиса. 12 решений (report). Review dispatched (opus).
|
||||||
|
Task 10: review (opus) — spec OK, Approved; Critical/Important: none. Решения исполнителя приняты: R-17 (design #28).
|
||||||
|
T10.1 minor: dry_run-тест не проверяет FakeStorage.objects == {} («ни файла») — deferred → pre-final cleanup batch (дёшево, инвариант)
|
||||||
|
T10.2 minor: нет теста dry_run=true на файле РБ (спека «Реальная выгрузка принята») — deferred → cleanup batch
|
||||||
|
T10.3 minor: нет теста dry_run + on_existing=update (откат истории/аудита) — deferred → cleanup batch
|
||||||
|
T10.4 minor: safe_filename не ограничивает длину/управляющие символы → NUL или >1024 байт даст 500 вместо 422 — deferred → cleanup batch
|
||||||
|
T10.5 minor: HTTPException(413) в сервисном слое (по брифу) — deferred
|
||||||
|
T10.6 minor: committed_sessionmaker/committed_client в файле теста, дублируют фабрику conftest — deferred → cleanup batch (вынести в conftest)
|
||||||
|
T10.7 minor: архивные ГК в снимке, при update переименовываются без разархивации; OQ — deferred (design #28; вопрос человеку: добавить в 07-open-questions.md в ../docs)
|
||||||
|
T10.8 minor: update-режим на масштабе: ≈3 round-trip на изменённую строку; 1.1 с измерено только для skip — deferred (профилировать в Task 11 по необходимости)
|
||||||
|
T10.9 minor: LE_IN_OTHER_GROUP раскрывает имя ЮЛ другой ГК при выдаче DIRECTORY_ADMIN филиальному пользователю — deferred (design #28)
|
||||||
|
T10.10: решения исполнителя перенесены в design #28 — fixed (commit chore R-17)
|
||||||
|
Task 10: complete (commits 1364f43..c9c3b69, review clean)
|
||||||
|
Task 11: dispatched (sonnet, implementer aede0ffb3cd46d81b), BASE=58075e9; в диспетчеризацию включён cleanup T10.1–T10.4, T10.6 (commit A) + тест полной выгрузки (commit B)
|
||||||
|
Task 11: implementer DONE (c793b42 cleanup T10.1-4,6; c9cd839 test). dry_run 8400 строк = 1.14 с, тест не skip. Review dispatched (sonnet).
|
||||||
|
Task 11: review (sonnet) — spec OK, Approved; Critical/Important: none. Решения исполнителя приняты: R-18 (design #29). T10.1, T10.2, T10.3, T10.4, T10.6 — fixed c793b42.
|
||||||
|
T11.1 minor: print с замером времени (noqa T201) вместо record_property/logger — deferred
|
||||||
|
T11.2 minor: таймер охватывает только HTTP-запрос, в docstring не оговорено — deferred
|
||||||
|
T11.3 minor: safe_filename чистит только Cc; Cf (bidi U+202E, zero-width) остаются в имени/аудите/ключе S3 — deferred (design #29)
|
||||||
|
T11.4 minor: после усечения имя может кончаться пробелом перед расширением — deferred
|
||||||
|
T11.5 minor: кейс «nul» в HTTP-тесте проходит и на старом коде (регрессию ловит unit-тест) — deferred
|
||||||
|
O-8: полная выгрузка 8400 строк, dry_run = 1.14 с (лимит 60 с).
|
||||||
|
Task 11: complete (commits 58075e9..c9cd839, review clean)
|
||||||
|
Task 12: dispatched (opus, implementer a696b70ed168ae6f6), BASE=83746e6
|
||||||
|
Task 12: implementer DONE (4dd966b R-13 refactor, 800f3a4 feature). Отклонение от R-13: RegionalOfficeBrief (для /me) + RegionalOfficeOut(Brief) с is_active в schemas/directories.py (иначе /me получает is_active и ломает тест); 422 OFFICE_NOT_FOUND (R-8); FOR NO KEY UPDATE на строке права/роли (FOR UPDATE дедлочит на FK аудита); EmailStr отвергает .local; гонка email с разным регистром (open). Review dispatched (opus).
|
||||||
|
Task 12: review (opus) — spec OK, Approved; Critical/Important: none. Решения исполнителя приняты: R-19 (design #30), включая отклонения от R-13 (иерархия Brief/Out) и 422 OFFICE_NOT_FOUND.
|
||||||
|
T12.1 minor: не покрыта ветка kept_roles (замена ADMIN на другую активную роль с USERS_ADMIN → 200; на неактивную → 409) — deferred → cleanup batch
|
||||||
|
T12.2 minor: гонка проверена только set_roles×set_roles — deferred (опц.)
|
||||||
|
T12.3 minor: тест экранирования `_` не доказывает экранирование (нет строки, различающейся только в позиции `_`) — deferred → cleanup batch
|
||||||
|
T12.4 minor: нет assert кода LAST_ADMIN в test_api_admin.py:403 — deferred → cleanup batch
|
||||||
|
T12.5 minor: USER_ROLE_CHANGE before сортирован, after в порядке запроса — deferred (задокументировано, design #30)
|
||||||
|
T12.6 minor: _check_office_exists теперь в трёх сервисах — deferred (вынести в repositories/directories)
|
||||||
|
T12.7 (concerns исполнителя): гонка создания email в разном регистре → оба создаются, логин → 403 fail-closed; одинаковый email-гонка → общий 409 без EMAIL_EXISTS (семейство T6.5/T7.1/T8.2); EmailStr отвергает *.local; UserOut.roles показывает неактивные роли — deferred; общий маппинг IntegrityError пересмотреть в final
|
||||||
|
O-9: EmailStr не принимает домен .local — dev-пользователи вида x@armka.local через API/UI создать нельзя (seed-dev создаёт напрямую); упомянуть в README (Task 15).
|
||||||
|
Task 12: complete (commits 83746e6..800f3a4, review clean)
|
||||||
|
Task 13: dispatched (sonnet, implementer a3c5011bf4b5363d7), BASE=6217e4e
|
||||||
|
Task 13: implementer DONE_WITH_CONCERNS (498450b). biome/tsc/build/web-check зелёные; UI в браузере не проверялся (смоук клиента из Node против API); NODE_ENV литералом из-за подмены process.env в rsbuild.config; AuthGate вокруг Layout; MeContext. Review dispatched (sonnet).
|
||||||
|
Task 13: review (sonnet) — spec OK; Needs fixes.
|
||||||
|
T13.1 IMPORTANT: apiFetch не оборачивает сетевую ошибку (TypeError: Failed to fetch) — пользователь видит английский текст, будущие экраны получат необработанный reject — fix round 1
|
||||||
|
T13.2 IMPORTANT: apiFetch: response.json() на пустом/не-JSON 2xx теле бросает сырой SyntaxError — fix round 1
|
||||||
|
T13.3 IMPORTANT: DevLogin в prod-ветке теряет `error` (тупик при ошибке /me со старым токеном) — fix round 1 (показать Alert + «Повторить»)
|
||||||
|
T13.4 minor: `void reloadCount;` в useMe — deferred
|
||||||
|
T13.5 minor: на armka:unauthorized `error` не выставляется («Сессия истекла») — включено в round 1 (дёшево)
|
||||||
|
T13.6 minor: email не trim-ится, whitespace проходит required — включено в round 1
|
||||||
|
T13.7 minor: height:'100%' формы входа не работает (#root min-height) — включено в round 1
|
||||||
|
T13.8 minor: токен в памяти параллельно с sessionStorage — deferred (добавить комментарий о приоритете — включено)
|
||||||
|
T13.9 minor: Permission.object_type/action — string, а не union — включено в round 1
|
||||||
|
T13.10 minor: API_BASE при Config.API_URL='/' даст `//api/v1` — включено в round 1 (trim trailing slash)
|
||||||
|
Task 13: fix round 1/5 dispatched (resume a3c5011bf4b5363d7); FIX_BASE=498450b
|
||||||
|
Task 13: fix round 1/5 (3 Important + 7 minors addressed, 0 open — T13.1, T13.2, T13.3, T13.5, T13.6, T13.7, T13.8, T13.9, T13.10 fixed d76ca57; commits 498450b..d76ca57); re-review (sonnet): all addressed, no new Critical/Important. T13.4 deferred.
|
||||||
|
O-10 (re-review): в prod-ветке «Повторить» без токена — no-op (useMe при отсутствии токена не сбрасывает error); сетевой сбой при чтении тела (response.text()) не оборачивается в NETWORK — deferred (редкие случаи)
|
||||||
|
Task 13: complete (commits 6217e4e..d76ca57, review clean after 1 fix round). Решения исполнителя: R-20 (design #31).
|
||||||
|
Task 14: dispatched (opus, implementer aee7f102e715b6674), BASE=a7502f6
|
||||||
|
Task 14: implementer DONE (d3c2f8a). biome/tsc/build/web-check зелёные; UI прогнан в headless Chrome через DevTools (смена ролей, LAST_ADMIN, создание, матрица, partial failure, 403 без запросов /admin/*). Concerns: Redux scaffold 'Store does not have a valid reducer' в консоли (O-11), нет поиска/пагинации >200, чанк antd 887 кБ. Review dispatched (opus).
|
||||||
|
Task 14: review (opus) — spec OK; Needs fixes (1 Important).
|
||||||
|
T14.1 IMPORTANT (I-a): UsersTable.tsx:603 `disabled: !r.is_active` — пользователь с неактивной ролью не может её снять (rc-select: у disabled-опции нет крестика; сервер даст 422 UNKNOWN_ROLE на повторную отправку) — fix round 1 (`disabled: !r.is_active && !user.roles.includes(r.code)`)
|
||||||
|
T14.2 minor (M-a): PermissionMatrix — ответы PUT отбрасываются, при сбое reload после сохранения показывается старое состояние — включено в round 1 (merge RoleOut через setData до reload)
|
||||||
|
T14.3 minor (M-b): неверный doc-комментарий hooks.ts (data при неудачном reload сохраняется) — включено в round 1
|
||||||
|
T14.4 minor (M-c): CreateUserModal без ref-lock от двойного клика — включено в round 1
|
||||||
|
T14.5 minor (M-d): `in`/`draft[code]` по plain object цепляет Object.prototype — включено (Object.hasOwn)
|
||||||
|
T14.6 minor (M-e): memo(Section) бесполезен без useMemo(visibleGroups) — включено
|
||||||
|
T14.7 minor (M-f): множественный выбор ролей = N PUT и N аудит-событий (не оптимистично) — deferred, README (Task 15)
|
||||||
|
O-11: консольная ошибка Redux «Store does not have a valid reducer» — pre-existing каркас (store.ts reducer: {}), не вводилась; deferred (follow-up).
|
||||||
|
Task 14: fix round 1/5 dispatched (resume aee7f102e715b6674); FIX_BASE=d3c2f8a
|
||||||
|
Task 14: fix round 1/5 (1 Important + 5 minors addressed, 0 open — T14.1..T14.6 fixed 4d719ec; commits a7502f6..4d719ec); re-review (sonnet): all addressed, no new Critical/Important. T14.7 deferred (README в Task 15).
|
||||||
|
Task 14: complete (commits a7502f6..4d719ec, review clean after 1 fix round). Решения исполнителя: R-21 (design #32).
|
||||||
|
Task 15: dispatched (sonnet, implementer ad359cdca9bdba5a0), BASE=fda907b; commit A = cleanup T12.1/T12.3/T12.4 (tests), commit B = docs+OpenAPI test+check-full
|
||||||
|
Task 15: implementer DONE (d241c07 cleanup T12.1/3/4; 7b75981 docs+OpenAPI test). check-full зелёный, 0 skipped: unit 764 passed, integration 264 passed, fixtures 266 passed/0 skipped (integration or fixtures); docker-build, web-check OK. Расхождения брифа: (1) неактивная роль с USERS_ADMIN через API = 422 UNKNOWN_ROLE (не 409 LAST_ADMIN); (2) повторный seed ВОЗВРАЩАЕТ права, снятые админом (ON CONFLICT DO NOTHING на вставку) — README описывает реальность. Review dispatched (sonnet).
|
||||||
|
Task 15: review (sonnet) — Approved with notes (документация сверена с кодом, check-full полный и зелёный: unit 764, integration 264, fixtures 266/0 skipped); Critical: none.
|
||||||
|
T15.1 IMPORTANT: design.md строка «Повторный seed не отзывает права» противоречила коду/README (на самом деле возвращает) — fixed (commit chore design.md/tasks.md, controller)
|
||||||
|
T15.2 IMPORTANT: tasks.md 5.1 [x] включал пункт archive и несуществующий get_uow_session — fixed (5.1 переформулирован, archive вынесен в 5.2 [ ] «после merge, человек»)
|
||||||
|
T15.3 minor: README импорт шаг 4 «все строки получают *_EXISTS» неточно (ранее пропущенные строки остаются ошибками) — fix в финальной волне
|
||||||
|
T15.4 minor: test_components_have_no_duplicated_models — count==1 по ключам словаря бессмыслен (реальную защиту даёт проверка "__"/"-") — fix в финальной волне
|
||||||
|
T15.5 minor: exact-set тест маршрутов хрупок по замыслу — rejected (намеренно для спек-ориентированного среза)
|
||||||
|
Расхождения брифа с кодом (приняты, факты в README): повторный seed возвращает права; неактивная роль с USERS_ADMIN через API = 422 UNKNOWN_ROLE.
|
||||||
|
Task 15: complete (commits fda907b..7b75981 + controller doc fix)
|
||||||
|
Pre-final: IntegrityError->coded 409 + T15.3/T15.4 dispatched (sonnet a1c561f3c0871b3bb), BASE=def3d72 (закрывает T6.5, T7.1, T8.2, T12.7 если пройдёт ревью)
|
||||||
|
Pre-final fix (commit 070a398): общий маппинг IntegrityError(23505) → coded 409 (7 ограничений, реестр constraint_conflicts.py), 11 новых тестов гонок; make check 773 unit, integration 275 passed. R-22 (design #33). T6.5 fixed 070a398, T7.1 fixed 070a398, T8.2 fixed 070a398 (для покрытых ограничений; ALIAS_TAKEN/email-в-другом-регистре/external_id остаются без кода — задокументировано в README), T12.7 (EMAIL_EXISTS-часть) fixed 070a398, T15.3 fixed 070a398, T15.4 fixed 070a398.
|
||||||
|
Final review: dispatched (opus a1d658a85175acb98), range 44806c8..811a1d2
|
||||||
|
|
||||||
|
## Финальное ревью (opus): Ready to merge: With fixes. Critical: none.
|
||||||
|
C-/I-/M- маркеры:
|
||||||
|
I-1 IMPORTANT: требование спеки users-admin «администратор MUST NOT снять с себя последнюю роль» vs код/план (запрет только для последнего активного обладателя в системе) — Ruling R-23 (design #34): трактовка системная, спека/D-8/proposal приведены к коду — fixed (commit chore R-23/R-24)
|
||||||
|
M-1: multipart импорта читается до аутентификации (Starlette сбрасывает на диск до 401/403) — fix wave: требование к ingress в README (client_max_body_size) — документировано
|
||||||
|
M-2: README обещает legal_entity_id в INN_EXISTS, код не отдаёт — fix wave (README)
|
||||||
|
M-3: смена имени+филиала ГК — одно событие ASSIGNMENT_CHANGE — Ruling R-24 (design #35): оставлено — deferred
|
||||||
|
M-4: /admin зависит от DIRECTORY_VIEW (список филиалов) — fix wave: документировано в README; GET /admin/regional-offices — deferred
|
||||||
|
M-5: /me устаревает после правки админом себя — deferred (follow-up: reload профиля после правки своей строки/матрицы)
|
||||||
|
M-6: design #22 неверно описывал последствие T8.1 (409 строкой, не 500) — fixed (commit chore R-23/R-24)
|
||||||
|
M-7: в design #28 не названа цена «S3 до commit» — fixed (commit chore R-23/R-24)
|
||||||
|
M-8: CompanyGroupCreate/LegalEntityCreate без extra="forbid" — fix wave
|
||||||
|
T11.3 (bidi/Cf в имени файла) — fix wave (исключить Cf)
|
||||||
|
T9.6 (реальная выгрузка, строка 4034: пустая «Организация» → ЮЛ с name="") — deferred; OQ человеку до боевого импорта
|
||||||
|
Deferred minors triage финального ревью: все прочие T-маркеры — follow-up (см. отчёт ревью); must-fix-before-merge: только I-1 (закрыт рулингом R-23).
|
||||||
|
Final fix wave: dispatched (sonnet a05e1583424ebbad0), BASE=bea295d (M-1, M-2, M-4 README, M-8, T11.3, final make check-full)
|
||||||
|
Final fix wave (commit 7f891dd): M-1 fixed 7f891dd (README: требование к ingress), M-2 fixed 7f891dd, M-4 fixed 7f891dd (README, документировано), M-8 fixed 7f891dd, T11.3 fixed 7f891dd. Scoped re-review (sonnet): all addressed, no new Critical/Important. Final make check-full (HEAD 7f891dd): EXIT 0 — unit 776 passed/0 skipped, integration 277 passed/0 skipped, `integration or fixtures` 279 passed/0 skipped, dbml-check/check-infra пусто, docker-build OK, web-check OK.
|
||||||
|
Final review: clean (no open Critical/Important).
|
||||||
|
|
||||||
|
---
|
||||||
|
## Вывод финального make check-full (сводка, HEAD 7f891dd)
|
||||||
|
|
||||||
|
```
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/ruff check .
|
||||||
|
All checks passed!
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/ruff format --check .
|
||||||
|
138 files already formatted
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/mypy src scripts
|
||||||
|
Success: no issues found in 86 source files(B
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/pytest -m "not integration"
|
||||||
|
platform darwin -- Python 3.11.15, pytest-9.1.1, pluggy-1.6.0 -- /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/python
|
||||||
|
cachedir: .pytest_cache
|
||||||
|
configfile: pytest.ini
|
||||||
|
===================== 776 passed, 277 deselected in 16.21s =====================
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/python scripts/check_dbml_vs_orm.py ../../docs/model/data-model-v5.14.dbml
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/python scripts/check_infra.py
|
||||||
|
cd api && /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/pytest -m integration
|
||||||
|
platform darwin -- Python 3.11.15, pytest-9.1.1, pluggy-1.6.0 -- /Users/andrey.epifancev/projects/awp-ca/.wt/slice-1/arm-core/api/.venv/bin/python
|
||||||
|
cachedir: .pytest_cache
|
||||||
|
configfile: pytest.ini
|
||||||
|
================ 277 passed, 776 deselected in 67.60s (0:01:07) ================
|
||||||
|
#10 exporting to image
|
||||||
|
#10 naming to docker.io/library/arm-core:latest done
|
||||||
|
cd web && npm install
|
||||||
|
cd web && npx biome check src
|
||||||
|
Checked 24 files in 21ms. No fixes applied.
|
||||||
|
cd web && npx tsc --noEmit
|
||||||
|
cd web && npm run build
|
||||||
|
ready built in 0.58 s
|
||||||
|
EXIT 0
|
||||||
|
```
|
||||||
Loading…
x
Reference in New Issue
Block a user