Commit Graph

693 Commits

Author SHA1 Message Date
Дмитрий 4a1663b426 test(auth): A5 — regression generic fallback ForgotPassword (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 17d9f16b7d feat(auth): A4 — ResetPassword ошибка несовпадения паролей (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий efb0dea5ed feat(auth): A1 — Yandex 360 SSO disabled + tooltip (Sprint 5A) 2026-05-17 02:23:26 +03:00
Дмитрий 120a386f05 feat(map): automation-graph — раздел «Хотелки» (отложенный backlog)
Слой WISHLIST: панель отложенных хотелок развития мозга/портала + кнопка-легенда «💡 Хотелки» в нижней легенде. Засеяно 4 хотелками раздела E8: K7-spike, мост claude-mem→ReasoningBank, claude-mem #1, двухуровневый ремонтник. Аддитивно — режим легенды наравне с «Разделы»; счётчики узлов/рёбер не меняются.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:31:26 +03:00
Дмитрий c64be74992 fix(import): final review — /import в явный список Route::view
Final review (🟢 low): SPA-маршрут /import работал через Route::fallback,
но все остальные app-маршруты перечислены явно в Route::view-блоке
(CLAUDE.md документирует явный список как намеренный паттерн — catch-all
перехватывал бы _test/* runtime-роуты Pest). /import добавлен в список
для консистентности и устойчивости.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:33:15 +03:00
Дмитрий 6a3593de7a fix(import): final review — tenant-изоляция import_unknown_statuses под BYPASSRLS
Final review нашёл: HistoricalImportService::loadStatusOverrides и
persistUnknownStatuses запрашивали import_unknown_statuses без явного
where(tenant_id), полагаясь на RLS через SET LOCAL. Но queue worker на prod
работает под crm_supplier_worker — BYPASSRLS-роль (00_create_roles.sql §5),
SET LOCAL не фильтрует → cross-tenant утечка: импорт тенанта A мог подхватить
resolved-маппинг тенанта B и инкрементировать его occurrences.

Добавлен явный where(tenant_id) в обе выборки (конвенция defense-in-depth
00_create_roles.sql:64 — WHERE-фильтры обязательны под BYPASSRLS). +тест
cross-tenant изоляции (red-green verified: без фикса 'Архив' тенанта A
получал status 'closed' из чужого маппинга).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:31:56 +03:00
Дмитрий de066145d3 feat(import): маршрут /import + сайдбар + инструкция H9
- router/index.ts: добавлен маршрут /import (name=import, layout=app,
  requiresAuth=true, transition=ld-route-fadeup, devIndex=29)
- AppSidebar.vue: пункт «Импорт данных» (mdi-database-import-outline)
  добавлен в группу «Работа» следом за Дашборд
- router.spec.ts: TDD-кейс маршрута /import (layout=app, requiresAuth=true)
- docs/Как_перенести_данные_из_crm-bp-gr.md: инструкция H9 (4 шага + таблица ошибок)
- cspell-words.txt: добавлены формы глагола «замапить»

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:14:04 +03:00
Дмитрий 96cb64f33a refactor(import): Task 10 code-review — POLL_INTERVAL_MS константа
Code-review Task 10 (🟡): магическое число 2000 (интервал polling'а) вынесено
в именованную константу POLL_INTERVAL_MS — паттерн файла (как в DashboardView).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:08:33 +03:00
Дмитрий 59dac9be56 feat(import): ImportView — экран импорта CSV
TDD: spec (3 tests) first, then component.
ImportView.vue: upload form + polling + history table + unknown-statuses banner.
Uses api/imports (uploadImport/listImports/getImport/getUnknownStatuses).
setInterval callback wrapped in named async fn (pollOnce) — no eslint-disable needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:05:15 +03:00
Дмитрий 7f05c4ab16 feat(import): api/imports.ts + UnknownStatusesDialog (wizard маппинга)
- api/imports.ts: типы ImportLogResource/UnknownStatus/StatusMapping,
  функции uploadImport/listImports/getImport/getUnknownStatuses/resolveUnknownStatuses
  (apiClient из ./client, стиль api/dashboard.ts)
- UnknownStatusesDialog.vue: wizard маппинга незамапленных статусов воронки
  (ТЗ §6.4/§6.6), 14 канонических slug, defineExpose(selection, save)
- Vitest 3/3 (tests/Frontend/UnknownStatusesDialog.spec.ts)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:58:33 +03:00
Дмитрий 5d64ca552e test(import): Task 9 code-review — cross-tenant тест ImportController::show
Code-review Task 9 (🟡): добавлен тест защиты show() — пользователь одного
тенанта получает 403 при запросе import_log другого тенанта (покрывает
abort_if defense-in-depth в ImportController::show). phpstan-baseline
регенерирован — инкремент count ложного TestCall-срабатывания (квирк 25).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:52:31 +03:00
Дмитрий a7038367e4 feat(import): ImportController + маршруты /api/imports
Task 9 Sprint 4: ImportController с 5 методами (store/index/show/
unknownStatuses/resolveUnknownStatuses), 2 FormRequest (StoreImportRequest
/ ResolveUnknownStatusesRequest), 5 маршрутов в routes/web.php под
auth:sanctum+tenant. Defense-in-depth: явный where(tenant_id) поверх RLS
(postgres superuser обходит BYPASSRLS на dev — паттерн DealController).
Тест 8/8, Larastan baseline regen (только TestCall false positives).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:45:47 +03:00
Дмитрий 15b53a9b2b feat(import): ImportLeadsJob — queued-обработчик CSV-импорта
ShouldQueue-job: читает CSV через Storage::disk('local'), парсит через
CsvLeadsParser, импортирует через HistoricalImportService (4 аргумента),
обновляет import_log (pending→processing→done|failed), шлёт
ImportCompletedNotification. RLS через SET LOCAL в каждой транзакции.
tries=1 (идемпотентность на уровне строк, повторный прогон искажает
счётчики — авто-ретрай отключён). Larastan: 0 новых ошибок.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:31:19 +03:00
Дмитрий 952263b3e5 feat(import): Mailable ImportCompletedNotification
Task 8 — email-уведомление пользователю по завершении CSV-импорта
исторических лидов (ТЗ §6.6). Два исхода: done (счётчики строк) /
failed (сообщение об ошибке). Blade-шаблон markdown.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:23:00 +03:00
Дмитрий 5416f809a3 fix(import): Task 6 code-review — final-класс + честное имя поля errors
Code-review Task 6 (non-blocking 🟡): HistoricalImportService объявлен final
(симметрия с ImportResult, утилитарный сервис без наследования). Ключ ошибки
upsert'а переименован 'line' → 'source_crm_id' — поле хранит идентификатор из
исходной CRM, а не файловую строку (в отличие от CsvParseResult::errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:17:59 +03:00
Дмитрий 0b9d73018d feat(import): HistoricalImportService — идемпотентный upsert лидов
Реализован HistoricalImportService с ImportResult DTO и 7 feature-тестами
(TDD). Идемпотентный upsert через pg_advisory_xact_lock + webhook_dedup_keys;
создание партиций через MonthlyPartitionManager; напоминания; unknown-статусы
с tenant-переопределениями; dry_run режим; historical_import tx без списания
баланса. Попутный fix CarbonImmutable-петли в MonthlyPartitionManager::ensureRange.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:10:23 +03:00
Дмитрий 29a4d01ff4 fix(import): Task 5 code-review — final-класс CsvLeadsParser + self::EXPECTED_COLUMNS
Code-review Task 5 (non-blocking 🟡): CsvLeadsParser объявлен final (симметрия
с DTO ParsedLeadRow/CsvParseResult, утилитарный класс без наследования);
строка ошибки про число колонок использует self::EXPECTED_COLUMNS вместо
литерала 9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:51:27 +03:00
Дмитрий 8f2b82405a feat(import): CsvLeadsParser + DTO ParsedLeadRow/CsvParseResult
Парсер CSV-выгрузки лидов crm.bp-gr.ru (ТЗ §6.2/§6.3): срезает UTF-8 BOM,
разбирает строки через str_getcsv, валидирует телефон (7XXXXXXXXXX) и даты
(Y/m/d H:i:s), срезает префикс B[123]_ из названия проекта. Невалидные
строки не роняют парсинг — собираются в errors[] с абсолютным номером строки.
Тесты: 5/5 (unit, без DB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:47:15 +03:00
Дмитрий 424987bedb feat(import): сервис StatusRuToSlugMapper (ТЗ §6.4)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:42:49 +03:00
Дмитрий ef4df2925f feat(import): сервис MonthlyPartitionManager + рефактор partitions:create-months
Выносит DDL-логику создания месячных RANGE-партиций из команды
PartitionsCreateMonths в переиспользуемый сервис MonthlyPartitionManager.
Сервис используется командой (DRY) и будет использован HistoricalImportService
для партиций под исторические даты CSV.

- MonthlyPartitionManager::ensureRange(table, from, to) — гарантирует партиции
  под диапазон дат, идемпотентно; отвергает незарегистрированные таблицы
- MonthlyPartitionManager::ensureMonth(table, monthStart) — одна партиция
- PartitionsCreateMonths рефакторена: убраны PARTITIONED_TABLES, partitionExists(),
  use DB; inject MonthlyPartitionManager через handle()
- Test: MonthlyPartitionManagerTest (3 теста, DatabaseTransactions — DDL откат)
- Regression: PartitionsCreateMonthsTest (4 теста) — зелёный, поведение не изменилось

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:37:29 +03:00
Дмитрий 8bc8c53a3b feat(import): Eloquent-модели ImportLog + ImportUnknownStatus
- ImportLog: $attributes зеркалят DB DEFAULT'ов (status/entity_type/dry_run),
  CREATED_AT/UPDATED_AT=null (таблица использует started_at/finished_at),
  casts для mapping_config (array) и dry_run (boolean)
- ImportUnknownStatus: scope unresolved() (whereNull mapped_to_slug),
  BelongsTo tenant
- Фабрики ImportLogFactory + ImportUnknownStatusFactory
- Тест ImportModelsTest (2/2, DatabaseTransactions, idempotent)
- ide-helper:models перегенерирован под новые модели
- phpstan-baseline регенерирован (квирк 25: TestCall::$tenant/$user)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:29:33 +03:00
Дмитрий 98549c52be fix(import): Task 1 code-review — убран фантомный GRANT-блок + усилен UNIQUE-тест
Code-review Task 1: явный per-table GRANT-блок для import_unknown_statuses
использовал несуществующие роли (crm_app_admin / crm_readonly). Реальные роли —
crm_app_user / crm_admin_user / crm_migrator / crm_audit_writer /
crm_supplier_worker (db/00_create_roles.sql). Блок удалён целиком из
db/02_grants.sql и db/schema.sql: import_unknown_statuses — обычная
tenant-scoped таблица, покрыта umbrella GRANT ... ON ALL TABLES +
ALTER DEFAULT PRIVILEGES (как import_log), явный per-table grant не нужен.

ImportSchemaTest: UNIQUE-тест усилен — проверяет состав колонок
(status_ru, tenant_id), а не только наличие constraint'а типа 'u'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:20:12 +03:00
Дмитрий 70f8b210f4 feat(import): H1+H2 — схема import_unknown_statuses + enrichment import_log
Sprint 4 Task 1 (schema delta §6):
- H1: новая таблица import_unknown_statuses (RLS tenant_isolation,
  UNIQUE(tenant_id,status_ru), FK→tenants/import_log/lead_statuses/users)
- H2: +5 колонок import_log (entity_type, source_system, mapping_config,
  unknown_statuses_count, dry_run)
- schema.sql v8.20→v8.21 (64 таблицы / 118 индексов / 40 RLS-политик)
- db/CHANGELOG_schema.md v8.21 entry
- db/02_grants.sql v8.21 section (crm_app_user/crm_app_admin/crm_readonly)
- migrate: hasTable/hasColumn guards (fresh-safe)
- tests: 3 Pest-теста (ImportSchemaTest) + SchemaDeltaTest v8.21 metrics
- ide-helper: _ide_helper.php + _ide_helper_models.php (были отсутствуют
  в worktree, phpstan падал молча из-за missing scanFiles entry)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 17:01:51 +03:00
Дмитрий 4937225da3 docs(plan): Sprint 4 — историческая миграция лидов §6 (H1-H6/H8/H9)
План CSV-импорта исторических лидов из crm.bp-gr.ru. 12 задач: schema delta
(import_unknown_statuses + enrichment import_log), сервисы парсинга/маппинга/
upsert'а, ImportLeadsJob, ImportController, frontend ImportView + wizard
маппинга статусов, маршрут /import + инструкция H9. H7 (импорт проектов)
вынесен — формат CSV проектов не специфицирован в ТЗ §6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:28:30 +03:00
Дмитрий da4d46b0d8 feat(map): automation-graph — полная актуализация по аудиту
Аудит карты против фактического состояния (~/.claude/settings.json,
project .claude/settings.json, .mcp.json, lefthook.yml, .claude/skills,
memory/). +20 узлов (83 → 103):
- плагины 5→9: +skill-creator, claude-code-setup, plugin-dev, context7
- хуки 5→12: +economy-self-check/skill-marker/skill-check/state-guard/
  postcompact/verifier (Stop) + ruflo-queen-hook
- memory 16→24: +audit_B/C, supplier_crm, full_audit_05-12/14, sprint1/2/3
- скилы проекта 2→3: +regression
Квирк 72 устранён (commit 0fa1a73) — 2 конфликта переоценены:
ag_pest↔mcp_redis BLACK→GREEN; ruflo_daemon↔ag_pest → квирки 73/77.
Все 103 узла размечены по разделам; E8 «Самообучение Claude» наполнен
(skill-creator, claude-code-setup). Топология 103 / 106 рёбер /
11 конфликтов (🔴1/3/🟢7). Smoke ✓.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:08:07 +03:00
Дмитрий f9f9fec97d feat(map): automation-graph — раздел E8 «Самообучение Claude»
+1 раздел в блок E «Мета и управление». Итого 40 разделов
(13 наполнены / 27 пусты). E8 — пустой каркас под будущий playbook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:08:07 +03:00
Дмитрий e74e8aa6d6 feat(map): automation-graph — слой функциональных разделов (iter7)
39 разделов деятельности Лидерры (5 блоков A–E) как классификация:
все 83 узла распределены по разделам — 13 наполнены, 26 пусты
(пустые — бизнес-домены, под которые в карте dev-автоматики узлов
ещё нет). Кнопка-панель «📂 Разделы» + строка «Раздел» в Паспорте
узла. Топология карты (83/90/11) и радиальный layout без изменений.
Основа будущего «мозга»: 1 раздел = 1 playbook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:08:07 +03:00
Дмитрий 447ef593fa feat(api): J1 — auth:sanctum+tenant middleware на /api/deals*
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:18:13 +03:00
Дмитрий 9f70d89046 feat(api): J2 — стаб-гейт EnsureSaasAdmin на /api/admin/* 2026-05-16 15:01:07 +03:00
Дмитрий 42a246d633 docs(plan): Sprint 3F — API middleware (J1/J2) 2026-05-16 14:56:11 +03:00
Дмитрий ca0c4d9318 feat(admin): G5/G6 frontend — incident detail view + РКН-notify 2026-05-16 14:09:53 +03:00
Дмитрий 3269434746 feat(admin): G6 backend — incident РКН-notify endpoint 2026-05-16 14:09:53 +03:00
Дмитрий 5e12126d71 feat(admin): G5 backend — incident detail endpoint 2026-05-16 14:09:53 +03:00
Дмитрий 8e3e06f3a4 fix(admin): G4 review — real AxiosError in error test + balance/NaN guards + a11y 2026-05-16 14:09:53 +03:00
Дмитрий c85424968e feat(admin): G4 frontend — billing row-actions menu + dialogs 2026-05-16 14:09:53 +03:00
Дмитрий 00f6611bc1 fix(admin): G4 review — lockForUpdate on refund balance + self-contained tariff tests 2026-05-16 14:09:53 +03:00
Дмитрий adabcf15a4 feat(admin): G4 backend — billing tenant actions (status/refund/tariff)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 14:09:53 +03:00
Дмитрий 3ea86d62ff docs(plan): Sprint 3D — Admin actions (G4/G5/G6) implementation plan
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 14:09:53 +03:00
Дмитрий 9a25e658b3 docs(map): automation-graph — нормативный sync под реколлаж ruflo 16.05
Карта приведена к реколлажу ruflo (Pravila v1.16 / CLAUDE.md v2.2 /
PSR_v1 v3.2 / Tooling v2.2): убраны «уровень −1», «§12 sub-policy»,
«R0 sub-policy delegation pattern».

- 4 узла-правила: лейблы v1.16/v2.2/v3.2/v2.2 + NODE_META.changed 16.05
- nd()-блоки правил: §12 — hard-rule уровня 0, R0 — головной фильтр,
  цепочка 7-уровневая (0–6), §3.5/§4.10 — advisory-подсистема
- ruflo_queen: advisory/automation-подсистема, не entry-point;
  reportsTo → Pravila §14 + CLAUDE.md §3.5/Tooling §4.10
- 4 ребра ruflo_queen→{правило} «перенял sub-policy» → flipped
  {правило}→ruflo_queen (§14 queen-триггер / §3.5 / §4.10 описывают)
- конфликт ruflo_queen↔pravila 🔴🟢 (реколлаж = правило-фикс):
  классификация 🔴2/4/🟢5 → 🔴1/4/🟢6
- §12 sub-policy → hard-rule level 0 в superpowers/hk_economy/mem_sp
  + CONFLICT hk_economy↔superpowers + EDGE_DETAILS

Топология 83/90/11 без изменений (downstream-sync, не iter).
Visual smoke 8/8 PASS (Playwright): 83 узла / 90 рёбер рендерятся,
0 JS-ошибок, легенды отредактированных узлов рендерятся корректно.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:44:14 +03:00
Дмитрий 73d2733522 docs(fix): claude-brain spec — битая ссылка на CLAUDE.md (../../../../../→../../../)
Pre-existing баг: 5×../ перелетал repo-root на 2 уровня. Поймана pre-push lychee реколлажа. Корректный путь от docs/superpowers/specs/ до repo-root CLAUDE.md — 3×../

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий 8b9d9fb029 docs(rules): PSR_v1 R13.1 — счётчик R0.6 «8» → «10 пунктов» (после удаления п.11)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий 9db66e6f27 docs(rules): Task 6 cross-consistency — вычистить остаточные R0→sub-policy cross-refs
Pravila §11.5 + §13.2 содержали живой cross-ref «PSR_v1 v3.0+, R0 → sub-policy под ruflo Queen-led routing» — после реколлажа R0 уже top-of-stack gate, формулировка стала ложной. Task 1 вычистил §13.9/§13.10, но пропустил §11.5/§13.2. + §10 v1.16-row дополнен; PSR_v1 шапка-нарратив +v3.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий 9b6fa50c4c docs(plan): ruflo hierarchy factual recollage — implementation plan
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий d6f0ff868f docs(rules): CLAUDE.md v2.2 — §5 п.10 убран ruflo-routing loophole (Task 4 review fixup)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий 9929b4a599 docs(rules): CLAUDE.md v2.2 — реколлаж ruflo, убран уровень −1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий d84127eaa5 docs(rules): Tooling v2.2 — шапка changelog синхронизирована с §13-записью (Task 3 review fixup)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:42 +03:00
Дмитрий 2def31eea9 docs(rules): Tooling v2.2 — реколлаж §4.10 ruflo entry-point → advisory-подсистема
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:41 +03:00
Дмитрий e6556e5a97 docs(rules): PSR_v1 v3.2 — §14 cross-ref + R0.6 п.11 + опечатка (Task 2 review fixup)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:41 +03:00
Дмитрий 4d807fb9f2 docs(rules): PSR_v1 v3.2 — реколлаж ruflo, R0 sub-policy → top-of-stack gate
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:41 +03:00
Дмитрий 68f341191b docs(rules): Pravila v1.16 — §10 history row + §14.6 cleanup (Task 1 review fixup)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:59:41 +03:00