Commit Graph

1153 Commits

Author SHA1 Message Date
Дмитрий 5745917efe feat(register): утилита телефона — нормализация + маска +7 (XXX) XXX-XX-XX 2026-05-21 19:26:29 +03:00
Дмитрий 564d984f2a fix(auth): registerResend — общий часовой лимит отправок (review)
Backend-ревью: register/resend применял только cooldown 60с, но не часовой
лимит 5/час (spec §7.6) — можно было слать код 1/мин бесконечно. Добавлен
RateLimiter по ключу email|ip (как в registerStart). +тест throttle для start.
Larastan baseline перегенерирован (новый тест добавил postJson-вызовы).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:21:14 +03:00
Дмитрий fdff36c553 refactor(auth): убрать одношаговый register (заменён start/verify)
Удалён старый POST /api/auth/register (метод + роут + RegisterRequest + 3 теста);
регистрация теперь только через register/start → register/verify. SPA-роут
страницы /register сохранён. Larastan baseline перегенерирован (counts
AuthControllerTest 9→8 / 14→10 после удаления тестов).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:13:11 +03:00
Дмитрий 3711a92958 feat(auth): регистрация — подтверждение email кодом + обязательный телефон (backend)
PhoneNormalizer (RU-телефон → 7XXXXXXXXXX) + Mailable RegisterEmailVerificationCode
с 6-значным кодом + эндпоинты register/start|verify|resend: pending-регистрация
в сессии (паттерн 2FA), email_verified_at=now() при verify, rate-limit на start +
cooldown 60с на resend, лимит 5 попыток ввода кода. Телефон обязателен, нормализуется
в 7XXXXXXXXXX. deptrac: разрешён Request→Service. Старый одношаговый register пока
сохранён (удаляется отдельной задачей Task 6).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:54:23 +03:00
Дмитрий 6e36c2455d docs(plan): регистрация — подтверждение email кодом + обязательный телефон
12 задач TDD: PhoneNormalizer, Mailable+шаблон, register/start|verify|resend,
удаление старого register, фронт (утилита телефона, API, store, двухшаговый
RegisterView, тесты), полная регрессия + живая проверка SMTP (blocked на пароль).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:24:47 +03:00
Дмитрий 4c2f4da664 docs(spec): регистрация — подтверждение email кодом + обязательный телефон
Дизайн: 6-значный код на email до создания аккаунта (pending в сессии,
паттерн 2FA), обязательный телефон по маске +7 (XXX) XXX-XX-XX (только
сбор, без SMS), реальная доставка через Яндекс SMTP. Без правок схемы БД.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:10:36 +03:00
Дмитрий 1df353ae51 fix(supplier): SyncSupplierProjectJob → pgsql_supplier (BYPASSRLS) — иначе queue-воркер падает 42704
Джоб создания/правки проекта запускается из очереди, где SetTenantContext не
отрабатывает (нет app.current_tenant_id GUC). Под боевой ролью crm_app_user первый
же Project::find() падал SQLSTATE 42704 (unrecognized configuration parameter
app.current_tenant_id) за ~2мс — до контакта с поставщиком: проект у поставщика не
создавался, в UI вечный «Sync pending». На dev не всплывало (postgres superuser
обходит RLS). Единственный supplier-flow джоб, который был на дефолтном подключении.

Фикс: const DB_CONNECTION = 'pgsql_supplier' + все DB-операции через ::on()/
DB::connection() — как у SyncSupplierProjectsJob/DeleteSupplierProjectJob/CsvReconcileJob.

Тесты: SupplierConnectionTest +constant-assert; SyncSupplierProjectJobTest
+поведенческий connection-assert (DB::listen → projects-запросы на pgsql_supplier);
Plan5/SyncSupplierProjectJobTest +SharesSupplierPdo (джоб теперь пишет через
pgsql_supplier → нужен shared PDO под DatabaseTransactions).

Проверено вживую на тест-сервере: проекты 14/15 синхронизированы, 6 доноров у
crm.bp-gr.ru (12742042-44 / 12766120-22), aggregateSyncStatus=ok.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:02:40 +03:00
Дмитрий 47cf202226 chore(gitleaks): ignore Nuclei docs curl-auth-user false-positive (05437ba)
Cross-branch FP: gitleaks-full-history сканит все refs; чужой коммит a8-infosec
Nuclei docs -u http://... ловится curl-auth-user (не аутентификация).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 14:34:08 +03:00
Дмитрий 888ead3264 docs(etalon): тест-сервер YC + 3 канала поставщика настроены
§5 факт о тест-сервере (доступ, демо-логины, info@lkomega.ru), §6 нить
каналов миграции, §1 git push feat/test-deploy. +2 слова cspell.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 14:32:31 +03:00
Дмитрий dcc1040f73 docs(deploy): test-server runbook — supplier migration channels section
3 канала проверены вживую на тест-сервере (webhook/CSV reconcile/export),
+предпосылки (Node20+Playwright+Chromium под /var/www/.cache, PlaywrightBridge 180s),
secret/allowlist/supplier-portal/HTTPS TODO. +2 слова в cspell.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 14:28:09 +03:00
Дмитрий b873c53aad fix(supplier): PlaywrightBridge timeout 75->180 for weak-VM Chromium cold-start
На 2 vCPU/2GB YC VM холодный старт Chromium в refresh-session ~65s wall-clock,
не укладывался в прежние 75s (60s Node + 15s buffer). Поднято до 180s.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 14:27:11 +03:00
Дмитрий bf4ed65d0e docs(deploy): runbook — crm_app_user + auth-friendly RLS, isolation verified, 4 test clients
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 12:05:08 +03:00
Дмитрий 3b2096b4cb docs(deploy): test-server runbook (access, services, update, HTTPS, teardown)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 11:42:25 +03:00
Дмитрий 2f4cf433cd docs(etalon): bump после supplier dead-donor/UI-бейдж fix + баннер 18:00 (83613b4+68f42ad)
§1 git: HEAD origin/main 68f42ad, ветка дрейфнула на feat/test-deploy, push через cherry-pick worktree. §6: +нить supplier-синк fix. cspell +3 слова.
2026-05-21 11:28:06 +03:00
Дмитрий 5fef4647c1 feat(projects): информационный баннер о сроке изменений до 18:00 МСК
Закрывается крестиком, закрытие запоминается в localStorage. Чисто фронтенд (информация, без блокировок, без бэкенда). +3 Vitest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 11:17:04 +03:00
Дмитрий 815f0a2dcd docs(deploy): test-deploy Yandex Cloud spec + plan (single VM, nginx/php/pg/redis, real RLS roles)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 11:02:17 +03:00
Дмитрий e6752b5e4c feat(deploy): temporary SAAS_ADMIN_TEST_BYPASS flag for test server (off by default)
Allows SaaS-admin area in non-local/testing envs only when SAAS_ADMIN_TEST_BYPASS=true.
Default false -> production unaffected. Remove after Yandex 360 SSO (Б-1 + DO-4).
TDD: tests/Feature/Middleware/EnsureSaasAdminTest.php (2 passing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 11:00:16 +03:00
Дмитрий 1220bddf3e fix(supplier): recreate deleted donor + fill legacy FK in online sync
handleOnline/syncGroup: сверка external_id со списком живых проектов портала (listProjects); пересоздание удалённых на портале доноров in-place без удаления записей (на supplier_projects могут висеть лиды/списания). online-режим заполняет supplier_b1/b2/b3_project_id, чтобы UI sync-бейдж не залипал в pending. +3 Pest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 10:59:37 +03:00
Дмитрий 8c350572df docs(etalon): bump после фичи удаление-вместо-архива + дедуп + человеческие ошибки (22e81cc)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 08:56:08 +03:00
Дмитрий 22e81cc896 chore(gitleaks): allowlist Nuclei docs false-positive (curl-auth-user)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 08:50:44 +03:00
Дмитрий 3bbd7787d8 feat(projects-ui): replace archive with delete, drop archived filter
- Remove archived_at from Project interface; rename store.archive → store.del
- BulkActionsBar: archive button → delete (testid, icon, confirm text)
- ProjectCard: archive menu item → delete (emit + icon)
- ProjectDetailsDrawer: confirm text + store.del call
- ProjectsView: @delete binding, remove 'Архивные' status filter entry
- vuetify.ts: add mdi-delete → Trash2 mapping
- All specs/stories updated: archived_at removed, archive → del renamed
- New test: del() calls DELETE /api/projects/{id}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:37:26 +03:00
Дмитрий 07d73870ba refactor(projects): remove archive feature, drop archived_at column (schema v8.27) 2026-05-21 08:24:25 +03:00
Дмитрий 7408bc4232 feat(projects): hard delete with deals-guard, replace archive
- ProjectService: add delete() with DB-level deals check (bypasses SoftDeletes
  scope via DB::table), captures supplier pivot IDs before cascade, dispatches
  DeleteSupplierProjectJob; add bulkDelete() private method; replace archive
  match arm with delete; remove archive() method
- ProjectController: destroy() calls delete() not archive(); update docblocks
- BulkProjectActionRequest: replace 'archive' with 'delete' in Rule::in for action
- Tests: ProjectDeleteTest (2 new TDD tests), ProjectsActionsTest updated
  (destroy → hard delete, 409-already-archived → 422-has-deals, bulk archive → bulk delete)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:08:33 +03:00
Дмитрий 9d68fc0ad6 feat(supplier): delete/re-sync donor on project delete respecting sharing
DeleteSupplierProjectJob: если после удаления Лидерра-проекта у донора
(supplier_project) не осталось других потребителей (pivot
project_supplier_links) — удаляет его у поставщика и локально;
если потребители есть — НЕ удаляет, диспатчит SyncSupplierProjectsJob.
2 Pest-теста (no-consumers / remaining-consumers) GREEN.
phpstan-baseline: +once() Mockery chain (аналог andThrow baseline).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 07:50:11 +03:00
Дмитрий e2fb20ef05 feat(projects): source+name dedup on update 2026-05-21 07:35:11 +03:00
Дмитрий 5427cdc740 feat(projects): source+name dedup with human messages on create 2026-05-21 07:01:46 +03:00
Дмитрий f3250ce178 feat(errors): global QueryException handler returns human message 2026-05-21 06:42:38 +03:00
Дмитрий 472ea8c75c docs(plan): project delete + source dedup + human errors implementation plan
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 06:31:45 +03:00
Дмитрий b053796182 docs(spec): project delete (vs archive) + source dedup + human errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 06:27:59 +03:00
Дмитрий 3b6992d8e9 Merge remote-tracking branch 'origin/main' into feat/project-migration-redesign
# Conflicts:
#	docs/observer/STATUS.md
#	docs/observer/episodes-2026-05.jsonl
2026-05-21 06:20:38 +03:00
Дмитрий 233f9984fc chore(observer): backfill chain_ref on live May episodes (working branch) 2026-05-21 06:19:51 +03:00
Дмитрий 54b1de78b8 chore(observer): retrofill chain_ref on existing committed May episodes 2026-05-21 06:06:29 +03:00
Дмитрий ee5bc56f2d docs(brain-retro): fill L1-L13+ hit rate template section 2026-05-21 06:06:28 +03:00
Дмитрий df2d091174 feat(status-md): surface C6 chain-map sync row 2026-05-21 06:06:28 +03:00
Дмитрий 4c9a1e9ccb feat(brain-retro): aggregate chain_ref into factorMatrix (multi-chain axis) 2026-05-21 06:06:27 +03:00
Дмитрий 65c2c5e471 feat(observer): one-shot chain_ref retrofill script (idempotent, atomic) 2026-05-21 06:06:27 +03:00
Дмитрий f6ba9bc1e7 chore(lefthook): wire C6 observer-chain-map-checker (job 16, blocking) 2026-05-21 06:06:26 +03:00
Дмитрий 05076c4f1d feat(observer): C6 chain-map-checker (JSON vs routing-off-phase.md sync) + L14 coverage 2026-05-21 06:06:26 +03:00
Дмитрий f943b229c0 feat(observer): emit chain_ref in primary_rationale 2026-05-21 06:06:25 +03:00
Дмитрий 28671cb012 feat(observer): chain-map JSON + chainsFor detector (L1-L13 attribution) 2026-05-21 06:06:25 +03:00
Дмитрий d86d375ce4 docs(observer): chain attribution L1-L13 spec + plan + brain-retro #2
Brain-retro #2 (весь май) → кандидат: атрибуция canonical chains L1-L13.
Spec + 9-task TDD plan (chain_ref в primary_rationale, C6 sync-контролёр,
ретрофилл). Исполнение разблокировано — epic observer-instrument-expansion
влит в main. +cspell словарь.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 06:06:24 +03:00
Дмитрий 4f5cf263f6 docs(observer): chain attribution L1-L13 spec + plan + brain-retro #2
Brain-retro #2 (весь май) → кандидат: атрибуция canonical chains L1-L13.
Spec + 9-task TDD plan (chain_ref в primary_rationale, C6 sync-контролёр,
ретрофилл). Исполнение разблокировано — epic observer-instrument-expansion
влит в main. +cspell словарь.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 04:42:41 +03:00
Дмитрий af15f24de7 feat(map): A1 backend-tooling — NODE_DETAILS + NODE_META для #64-67
Узлы rector/php_insights/backend_patterns/nightowl теперь в панелях описания (nd())
и теплокарте использования (NODE_META, uses:0 новые). Дополняет 5d82fdd (NODES/EDGES/
NODE_SECTION в data.js). Browser-smoke: 141 узел, NODE_META+NODE_DETAILS у всех 4, 0 JS-ошибок.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:36:27 +03:00
Дмитрий b757f22b97 docs(etalon): bump после сквозного чек-листа портала + 6 фиксов (b7466eb)
§1 git HEAD a0e18a1→b7466eb + push a0e18a1..b7466eb (4 commits FF).
§5 schema header drift v8.25→v8.26 устранён (commit 95ee664).
§6 +нить «сквозной чек-лист + 6 фиксов»; «deferred 3 RED теста» → ИСПРАВЛЕНЫ.
cspell-words +2 (захардкоженным, смердженных).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:29:24 +03:00
Дмитрий 31b53557ac style(backend): pint concat_space fix in rector.php
lefthook pint (root:app/ + repo-relative {staged_files}) не обработал rector.php
при 058b239 — known pint-paths quirk. Ручной composer pint исправил concat_space.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:21:27 +03:00
Дмитрий be27713f6e feat(map): +4 A1 backend-tooling nodes + L14 chain (137->141 nodes, 155->165 edges)
NODES +rector/php_insights/backend_patterns/nightowl (все A1); EDGES +10 (реестр-связи
+ L14 backend-quality chain Rector->PHP Insights->Larastan + reuse Boost/billing-audit/Sentry).
Версии-метки v1.35/v2.22/v3.19/v2.19 + router-procedure v1.2. Browser-smoke: 141 узла /
165 рёбер, A1=7 узлов, 0 JS-ошибок (favicon 404 безвреден).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:21:27 +03:00
Дмитрий 60dd3e70b1 docs(normative): A1 backend-tooling #64-67 — Tooling v2.19 / PSR v3.19 / Pravila v1.35 / CLAUDE v2.22
Атомарный version-bump-набор (cross-ref-checker C2 STRICT). 16-я off-phase подкатегория
backend-tooling (раздел A1): #64 Rector + #65 PHP Insights (Composer dev-deps) + #66
laravel-backend-patterns (self-authored) + #67 NightOwl (DEFERRED). Счётчик 63→67 (87 total).
Tooling §4.39-4.42 (9-attribute blocks) + §0; PSR R10.1 Блок 1 note + R15.6; Pravila §13.2
абзац; CLAUDE §3.3/§6/§9/§0. ADR-013. cross-ref-checker + l1-watcher: 0 drift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:21:26 +03:00
Дмитрий 54967147d7 docs(router): +4 backend nodes routing + L14 chain (routing-off-phase v1.3, router-procedure v1.2)
routing-off-phase v1.3: +4 строки routing #64-#67 (NightOwl DEFERRED) + связка L14
backend-quality chain (Rector->PHP Insights->Larastan->deptrac); scope §4.11-§4.42; #31-#67.
router-procedure v1.2: changelog +backend-tooling узлы в реестр step 3. ADR-013.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:21:26 +03:00
Дмитрий 1a02b4b5f2 docs(adr): ADR-013 backend-tooling boundaries (BT1-BT9) + NightOwl deferred spike
ADR-013: 4 узла A1 (#64-67) + границы BT1-BT9 + постуры. NightOwl DEFERRED
(native-Windows нет pcntl/posix + OSS без MCP + hosted 152-ФЗ) -> Linux/Б-1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:21:26 +03:00
Дмитрий 76ea9bbb04 feat(backend): Rector (#64) + PHP Insights (#65) install + configs
Rector: rector/rector ^2.4 + driftingly/rector-laravel ^2.3; app/rector.php
  (deadCode+codeQuality, conservative). composer rector / rector:fix scripts.
  dry-run baseline=16 files -> manual/CI posture, NOT blocking lefthook (ADR-013).
PHP Insights: nunomaduro/phpinsights; app/config/insights.php — SyntaxCheck removed
  (Windows subprocess crash + redundant), style not gated (Pint owns, BT4),
  security-check off. Baseline Code80/Complexity81/Arch75; floors set; composer insights -> 0.
allow-plugins += dealerdirect/phpcodesniffer-composer-installer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:21:26 +03:00