b9917a90d4b5492c8fe84fc62a72594de85a3ffe
506 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
27dec3d459 |
docs(rules): аудит нормативки — закрытие 14 находок (CLAUDE.md v1.84 + Pravila v1.9 + PSR_v1 v1.5 + Tooling v1.13)
Audit конфликтов и запутанностей между CLAUDE.md / Pravila / PSR_v1 / Tooling выявил 14 находок (3 🔴 high, 6 🟡 medium, 5 🟢 low). Все правки — через paired stack: writing-plans → executing-plans → claude-md-improver (для CLAUDE.md по §5 п.10) → verification-before-completion с grep-evidence. Ключевые правки: 1. claude-md-management формализован #33 в Tooling §4.7 — пятый включённый плагин (категория «инфраструктурная», вне UI-пула). Tooling §0 счётчик 31 → 33 (3 off-phase tools). 2. Tooling §7 + PSR_v1 уровнем 3 — иерархия source of truth расширена с 5 до 7 уровней, sync с CLAUDE.md §1. 3. Tooling §6 +5 конфликтов v1.4 — UPM↔FD, 21st↔Vuetify, 21st↔App*, framer↔motion-v, UPM↔21st (с 5 до 10 строк). 4. Pravila §12.3 объявлен Single Source of Truth для exclusions §12; PSR_v1 R0.4.A + CLAUDE.md §5 п.11 — cross-ref сюда. 5. Pravila §13.6 +tier-таблица hard-rule (explicit / transitive / standard) — снимает скрытую иерархию между §12 и §13.9/§13.10. 6. PSR_v1 R10.1 разбит на 3 блока: enabledPlugins / built-in skills Claude Code / MCP-серверы — раньше всё было одним списком. 7. PSR_v1 R8 +тай-брейкер FD↔21st (последовательно, FD ведущий). 8. PSR_v1 R10.4 + R14.7 — tier-метки transitive hard-rule с явным указанием, что Pravila §9 «Отступления» к ним не применяется. 9. Scope-метки приоритетных цепочек — Pravila §0 (внутрипараграфный), CLAUDE.md §1 (межфайловый), PSR_v1 R0.1 (scope головенства stack'а). Снимает путаницу 4-х представлений. 10. CLAUDE.md §5 п.5 свёрнут до 2 строк со ссылкой на PSR_v1 R14 (был копией PSR_v1 на 12 строк). 11. Tooling §4.6 — settings.json → ~/.claude.json (где реально лежит API-ключ 21st). cspell-words.txt: +внутрипараграфный, внутрипараграфные, скилов (новые термины из scope-меток и plan-файла). Намеренно оставлено: R0.6 пункт 11 ⊂ пункт 6 (motion-специальный flow); Pravila §13.10 формально избыточен (явная запись лучше транзитивного). Plan: docs/superpowers/plans/2026-05-10-rules-audit-fixes-plan.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b912724cf7 |
chore(frontend): Sprint 4 Phase C — bundle analyzer + dead-code cleanup (audit O-refactor-06)
- rollup-plugin-visualizer + script `npm run build:analyze` (env BUILD_ANALYZE=1)
Output: storage/bundle-analyze.html (gzip + brotli sizes), gitignored.
- cross-env установлен для Windows-совместимости env-переменной build:analyze.
- knip + knip.config.ts (entry app.ts + router/index.ts; ignore *.story.vue + tests/).
ВАЖНО: knip падает с oxc-parser ArrayBuffer fail на этой машине
(Windows quirk feedback memory) — конфиг сохранён для будущих запусков на
Linux/macOS CI. Dead-code search выполнен вручную через grep по composables/.
- Удалены 4 unused exports + 4 private helpers, инициируемых только ими:
* mockReports.ts: MOCK_JOBS, QuotaInfo (interface), MOCK_QUOTA
* reportsMapper.ts: reportTypes()
* mockTenantDetail.ts: expandTenantDetail() + 4 SAMPLE_* consts
(SAMPLE_USERS/SAMPLE_PROJECTS/SAMPLE_BALANCE_HISTORY/SAMPLE_ACTIVITY)
* useCsvDownload.ts: csvEscape() — снят `export` (используется внутри файла)
ESLint + vue-tsc + Vitest 416/416 + build — зелёные.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
79ff60ffd9 |
refactor(frontend): Sprint 4 Phase B/3 — split 2 utility views (audit O-refactor-04 хвост)
ErrorView 320→178 (+ ErrorBrand 54 + ErrorIllustration 31 + ErrorActions 55 + ErrorMeta 102).
DashboardView 302→84 (+ DashboardPageHead 65 + DashboardKpiRow 97 + DashboardBalance 124).
State (config, errorCode в ErrorView; range/kpis/balance в DashboardView) остаётся
в parent ради единого route.meta-driven flow + future API-fetch'а (Phase B/1 паттерн).
DashboardPageHead использует Vue 3.5 defineModel<T>() для двусторонней привязки range.
Sub-components читают только props — без Pinia stores (mock-data flow).
Все sub-components <250 строк (acceptance threshold). Shell line counts: 178/84.
ЗАМЕЧАНИЕ по acceptance «0 components >300»: НЕ закрыто полностью. 2 файла остались
выше порога — DealsView 560 + DealDetailDrawer 386. Зафиксировано в Sprint 3 Phase C
commit
|
||
|
|
849bc73290 |
refactor(frontend): Sprint 4 Phase B/2 — split 3 user views (audit O-refactor-04 хвост)
BillingView 416→114 (+ BalanceCard 155 + TransactionsTable 113 + InvoicesTable 90 + billingFormatters 51 composable: formatPlain/formatCost/statusChipColor/ statusLabel/formatLabel/formatIcon/txAmountClass). SecurityTab 354→39 (+ ChangePasswordCard 17 + TwoFactorCard 218 + RecoveryCodesCard 104 + SessionsTable 66; auth-store читается напрямую в каждом sub-component). RemindersView 345→183 (+ RemindersFilters 51 + RemindersList 173; ReminderDialog уже отдельный с прошлой фазы — служит как ReminderForm). State (`activeTab`, `editingReminder`, `deletingReminderId` в RemindersView) остаётся в parent ради единого reload-flow + confirm-dialog'ов. Auth-store читается напрямую в TwoFactorCard/RecoveryCodesCard через useAuthStore() — без prop-drilling. Reminders-store читается напрямую в RemindersFilters/ RemindersList. Все sub-components <250 строк (acceptance threshold). 3 view-shells: 114/39/183. Регрессия: ESLint 0 + vue-tsc 0 + Vitest 416/416 + build OK 968 ms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
30ef61dff8 |
refactor(frontend): Sprint 4 Phase B/1 — split 3 admin/layout views (audit O-refactor-04 хвост)
3 view'а с >300 строк разделены на shell + sub-components: AdminTenantsView 377→155 (+ TenantsStatsHeader 82 / TenantsFilters 93 / TenantsTable 116). AdminTenantDetailView 436→109 (+ TenantDetailHeader 158 / TenantDetailTabs 176 + adminTenantDetailFormatters 43 composable). AppLayout 466→78 (+ AppSidebar 155 / AppTopbar 269; R0.6 hard-стоп снят явным запросом заказчика 10.05.2026). State (filterStatuses, tenantsState, activeTab, tenant, drawerOpen) остаётся в parent view'ах ради `defineExpose`-контракта Vitest тестов. Sub-components читают Pinia stores напрямую (auth + notifications + reminders) — без prop-drilling. AppTopbar 269 строк <300 — acceptance threshold выдержан (можно дальше split на NotificationsDropdown + UserMenu в отдельном flow, не критично). Регрессия: ESLint 0 + vue-tsc 0 + Vitest 416/416 + build OK 1.17 сек. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cf1aabb32e |
chore(cspell): добавлены UPM, gsap, LLM в словарь проекта
Сопровождает v1.83 docs-патч (PSR_v1 v1.4 формализация UPM + 21st Magic MCP). Без этих записей pre-commit hook cspell блокирует коммит на CLAUDE.md / PSR_v1 / Pravila / Tooling / CHANGELOG_claude_md, где термины используются. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
833e3e6083 |
docs(rules): PSR_v1 v1.4 — формализация UPM + 21st Magic MCP + R15 motion-системы
Триггер: пользователь спросил «хочу добавить плагины 21st, framer motion, UI UX max — проанализируй конфликты». Проверка показала: UPM (skill) и 21st Magic MCP (`magic` сервер) уже фактически включены в ~/.claude/settings.json и ~/.claude.json, но в правилах не описаны. Framer Motion — React-only runtime npm-библиотека, не Claude-плагин, физически не работает в Vue. Через цикл brainstorming → 3 варианта → итерации согласовано: формализовать UPM + 21st; для motion — двухуровневая R15 (framer-motion hard-запрет навсегда + motion-v узкое окно по 4 условиям). PSR_v1 v1.3 → v1.4 (главный артефакт): - R6 → R6.0 универсальная таблица фильтра для FD/UPM/21st - R6.1 hard-override Forest расширен на все три плагина - R10.1 +21st row, ослабление UPM (FD молчит ИЛИ R12 третий вариант) - R11.5 (новое) активация UPM в R12 архитектурном - R11.6 (новое) иерархия 7 motion-источников - R0.6 +3 hard-стопа (App* через 21st, Vuetify-эквивалент, motion-v) - R13 +9 строк matrix'а - R14 (новое, 7 подразделов) pipeline UPM + 21st - R15 (новое, 7 подразделов) motion-системы - R8 +7 тай-брейкеров Pravila v1.7 → v1.8: §13 расширен на расширенный пул, §13.10 hard-link на R14. Tooling Прил. Н v1.11 → v1.12: #31 UPM + #32 21st (off-phase tools), §9.2 motion-runtime denylist (framer-motion + react-spring R15.1; motion-v + gsap + anime + lottie + popmotion R15.2/R15.7). 31 формализованных позиций (19/29 активных по фазам + 2 off-phase). CLAUDE.md v1.82 → v1.83: §0 cross-refs, §2 +Animation default stack, §3.3 +#31 UPM +#32 21st, §5 п.5 расширен, §5 п.12 motion-runtime новый, §6 обновлён, §9 +entry. cspell-words.txt: +UPM +gsap +LLM (валидные термины проекта). Через /claude-md-management:claude-md-improver. 6 файлов, 0 изменений в коде проекта (resources/js/, app/, db/ нетронуты), 0 npm install. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f77c91d5fa |
feat(backend): Sprint 4 Phase A — keyset pagination в DealController::index (audit O-perf-04)
Опциональный query-параметр `cursor` (base64-encoded JSON {r:received_at, i:id}).
При cursor — keyset через PG row constructor `(received_at, id) < (?, ?)`
с использованием существующего индекса (received_at DESC, id DESC).
O(1) на любой глубине, без COUNT(*) (total не возвращается в keyset-режиме).
Без cursor — backward-compat OFFSET-путь: total + offset для существующего
frontend. Оба режима возвращают next_cursor (NULL = последняя страница).
Trick "+1 fetch" — узнаём про следующую страницу одним SELECT'ом без COUNT.
3 новых Pest-теста: keyset-навигация через cursor, 422 на невалидный cursor,
next_cursor flow. Pest 421/421 (419 + 2 skipped browser, +3 от 418 baseline).
phpstan-baseline.neon регенерирован: +2 occurrences pattern
`received_at?->toIso8601String()` (cursor build) + 7 occurrences тестовых
helper-properties Pest TestCall — все известные ignored patterns, не реальные ошибки.
Frontend integration в useDealsList/DealsView — отдельным шагом
(не блокирует backend deploy, OFFSET путь жив).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8c6374d278 |
docs(spec-roadmap): fix broken link to memory/project_state.md (outside repo)
lychee pre-push hook поймал invalid markdown link на memory/project_state.md — файл живёт в C:\Users\Administrator\.claude\projects\... (Claude auto-memory), не в репозитории. Заменено на plain-text упоминание. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
823daf4f9f |
docs(plan-sprint4): implementation plan «Audit tail» + revised spec scope
Sprint 4 — закрытие 3 оставшихся audit O-* пунктов:
- Phase A: O-perf-04 keyset pagination в DealController::index
- Phase B (1+2+3): O-refactor-04 хвост, split 8 Vue-компонентов >300 строк
- Phase C: O-refactor-06 dead-code detection через rollup-plugin-visualizer + knip
Spec ревизия: Sprint 4 уменьшен с 8 до 3 пунктов после факт-чека —
O-perf-07/O-refactor-05/O-refactor-07/O-stack-02/O-stack-03 уже закрыты
в Sprint 1–2 (Sprint 1 ESLint vuetify rule, Sprint 2 Phase A: Pest browser
scaffold, infection mutation, lazy-loading, Larastan cache; CLAUDE.md
header фактически 30 строк после
|
||
|
|
77b018dff8 |
docs(spec-roadmap): дизайн roadmap'а до production — 3-track структура (A/B/C)
Покрывает категории A (audit-хвосты), B (Phase 3 tooling), C (Post-MVP backend),
D (Б-1-зависимые фичи), E (production deploy в YC). Категория F (юр. тексты) —
external; G (push 18 коммитов) — Sprint 0 precondition.
Двухдорожечная структура:
- Track A (Sprint 4–6): не зависит от Б-1, стартует сразу после push
- Track B (Sprint 7–9): после получения реквизитов ООО — YC infra + CI/CD + SSO + лендинг + hardening + soft-launch
Definition of done «production launch» в §1, per-sprint acceptance в §5,
9 рисков с mitigations в §6, 5 open questions для будущей детализации в §7.
Базовый HEAD:
|
||
|
|
6c2f0ce682 |
refactor(frontend): Sprint 3 Phase C — Top-3 Vue components split (audit O-refactor-04)
Sprint 3 Phase C. Закрытие audit O-refactor-04 (частичное — Top-3 из 12): - DealsView.vue: 852 → 560 строк. Выделены: DealsFilters (123), DealsBulkBar (150), DealsTable (165). CSV-utilities (csvEscape/triggerCsvDownload/triggerBlobDownload/ buildCsvString) вынесены в composables/useCsvDownload.ts. - ReportsView.vue: 592 → 261 строк. Выделены: ReportRequestForm (тип отчёта, даты, фильтры, формат, submit/reset), ReportJobsList (список заданий со статусами и actions retry/cancel/delete). - DealDetailDrawer.vue: 580 → 386 строк. Выделены: DealDetailHero (header + phone-link + status-chip), DealDetailTimeline (activity log с MOCK_EVENTS). Comment- и Reminders-секции оставлены inline — связаны с API и defineExpose. DealsView и DealDetailDrawer остались выше 350-целевого уровня: bulk-action функции (applyBulkStatus/applyBulkDelete/applyBulkExport/undoBulkDelete/ applyBulkRestoreFromTrash) и comment/reminders fetch — экспонируются через defineExpose в Vitest-тестах напрямую, дальнейшая декомпозиция требует изменения тест-контракта (отдельным flow). Layout-структуры (AppLayout 466, AuthLayout, AppShell) НЕ ТРОНУТЫ — R0.6 hard-стоп. Остальные 9 components >300 строк (AdminTenantDetailView, BillingView, AdminTenantsView, SecurityTab, RemindersView, ErrorView, DashboardView, ImpersonationDialog, далее) — вне scope Sprint 3, отдельным flow по запросу. vue-tsc: 0 errors. ESLint: 0. Vitest: 416/416 PASS. Build: success (1.15s). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cd13e6c8bb |
refactor(backend): Sprint 3 Phase B — AuthController split (3 classes by analogy)
Sprint 3 Phase B. Закрытие audit O-refactor-02:
- O-refactor-02: AuthController (595 строк) → split на 3 класса (по аналогии с
Sprint 3 Phase A DealController split):
* AuthController (343) — core auth: login/register/logout/me +
updateNotificationPreferences + helpers (loginThrottleKey, isIpLockedOut,
maybeNotifySuspiciousLogin, logAuthEvent, lockoutResponse, userResource)
* TwoFactorController (217, новый) — 2FA verify + recovery codes use
(login-flow продолжение). Setup/enable/disable остались в
TwoFactorSetupController (с Sprint 1).
* PasswordResetController (146, новый) — forgot/reset password
API endpoints без изменений (только routing — controller@method обновлён в web.php):
- POST /api/auth/2fa/verify → TwoFactorController@verifyTwoFactor
- POST /api/auth/2fa/recovery-use → TwoFactorController@useRecoveryCode
- POST /api/auth/forgot → PasswordResetController@forgotPassword
- POST /api/auth/reset-password → PasswordResetController@resetPassword
Helpers lockoutResponse и userResource дублируются между классами (1:1) — по
принципу Phase A: «копируй методы 1:1, не оптимизировать на ходу». Будущая
итерация может вынести в trait/base controller, если появится 4-й класс.
Pest: 416/416 PASS + 2 skipped.
Larastan: 0 errors.
Pint: passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
86dc930915 |
refactor(backend): Sprint 3 Phase A — DealController split + N+1 fix + export streaming
Sprint 3 Phase A. Закрытие audit O-refactor-01 + O-perf-01 + O-perf-05.
O-refactor-01: DealController (802 строки) → split на 3 контроллера
по ответственности:
* DealController (466 строк) — single-resource CRUD (index/show/store/update).
* DealBulkActionController (264 строки, новый) — bulk transition/destroy/restore.
* DealExportController (120 строк, новый) — export() с streaming.
API endpoints без изменений; в routes/web.php обновлены только controller@method.
O-perf-01: N+1 в bulk-actions устранён в новом DealBulkActionController.
* transition: SELECT (id, status) → filter NO-OP → bulk-UPDATE
whereIn(changed)->update(status) + ActivityLog::insert(массив).
100 сделок: ~200 SQL → 2 SQL (после SELECT 1 UPDATE + 1 INSERT).
* destroy/restore: SELECT id'шников живых/удалённых → bulk-UPDATE
deleted_at + ActivityLog::insert. Аналогично 2 SQL вместо N.
Defense-in-depth where(tenant_id) сохранён — DealTransitionTest
«не апдейтит чужие сделки» проходит.
O-perf-05: export() переписан на OpenSpout streaming (composer require
openspout/openspout ^5.3). PhpSpreadsheet строил весь .xlsx в памяти
(10K сделок ≈ 100+ MB RAM). Теперь:
* Writer::openToFile('php://output') + Row::fromValues + chunkById(500).
* StreamedResponse → пик памяти O(1) от размера экспорта.
* CSV: OpenSpout Options(FIELD_DELIMITER=';', SHOULD_ADD_BOM=true) —
Excel-friendly RU-локаль сохранена.
* XLSX: getCurrentSheet()->setName('Сделки'), header через
Row::fromValuesWithStyle с (new Style)->withFontBold(true).
DealCreateTest.php (4 теста про export): getContent() → streamedContent()
для StreamedResponse + getCell()->getFormattedValue() для inline-string
ячеек, которые OpenSpout пишет как RichText (PhpSpreadsheet writer писал
как plain shared-string). Логика тестов и assertion'ы не меняются.
Verification:
Pest: 416/416 passed (+ 2 skipped), 1388 assertions, 47.5s.
Larastan: 0 errors above baseline.
Pint: passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f52402fabe |
docs(handoff): Sprint 2 Phase C — Google Fonts API v2 + @font-face fallback strategy (audit O-stack-10)
Sprint 2 Phase C (modernization). Закрытие audit O-stack-10: - DEVELOPER_HANDOFF §4.5 — Google Fonts API v2 + @font-face fallback strategy (расширение раздела о Sprint 1 Phase E font-display стратегия §4.4). - Документировано: когда нужен fallback (расширенная аудитория Chrome 50-99 / Safari 12-14), шаблон @font-face блоков, migration path (скачивание + размещение), trade-offs. - Текущее решение Лидерры: только API v1 + display=swap (целевая аудитория Chrome 100+). - Решение пересмотреть при GDPR/audit/статистике старых браузеров. O-stack-06 (FD plugin в ~/.claude/settings.json) — manual user step, выводится в финальном отчёте Sprint 2 (файл вне git). O-refactor-07 (CLAUDE.md §0 reorg) — уже фактически реализовано в §9 (полная история перенесена в docs/CHANGELOG_claude_md.md ранее, оставлены 2 последние версии в шапке). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2d7d7d1188 |
feat(frontend): Sprint 2 Phase B — Vue 3.5 defineModel + Vuetify 3.12 typed slots + lazy-imports + ESLint check
Sprint 2 Phase B (modernization). Закрытие audit O-stack-04/05/07 + O-perf-06:
- O-stack-04: Vue 3.5 defineModel() в 3 диалогах (NewDealDialog,
ImpersonationDialog, ReminderDialog) — boilerplate −5 строк/файл.
+ useTemplateRef() в TwoFactorView (input v-for refs).
- O-stack-05: Vuetify 3.12 типизированные слоты VDataTable
(DealsView + AdminTenantsView) — inline-аннотации `{ item }: { item: T }`
на 6+7 scoped-slot bindings; vue-tsc проверяет доступ к полям статически.
- O-stack-07: ESLint flat-config verified — header-comment добавлен
в eslint.config.js. Legacy .eslintrc.json не используется.
- O-perf-06: defineAsyncComponent() для тяжёлых диалогов в 3 местах:
DealsView (DealDetailDrawer + NewDealDialog), DealDetailDrawer
(ReminderDialog), RemindersView (ReminderDialog). KanbanView оставлен
sync — async-загрузка приводила к EnvironmentTeardownError в jsdom
(KanbanView.spec.ts), see in-file comment. Сборка показывает chunk'и
ImpersonationDialog (7.61 kB), DealDetailDrawer (11.12 kB), NewDealDialog
и ReminderDialog как отдельные lazy-bundles.
vue-tsc: 0 errors. ESLint: 0. Vitest: 416/416 PASS. Build: success.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
20d4682e21 |
feat(backend): Sprint 2 Phase A — Pest 4 browser scaffold + mutation + lazy-loading + Larastan cache
Sprint 2 Phase A (modernization). Закрытие audit O-stack-01/02/03 + O-perf-07:
- O-stack-01: Pest 4 browser-tests scaffold. testsuite Browser в phpunit.xml,
Pest.php extend(...)->in('Browser'), tests/Browser/SmokeTest.php
(login-flow + deal-create-flow). На Windows native PHP плагин
pest-plugin-browser требует ext-sockets (отсутствует в стандартной
сборке) и при автозагрузке вызывает socket_create_listen() ДО старта
тестов, что ломает весь Pest. Поэтому плагин НЕ в require-dev,
тесты помечены ->skip(...) с инструкцией активации на Linux CI.
Скелет тестов в комментариях — на CI достаточно
composer require pestphp/pest-plugin-browser --dev + npx playwright
install + раскомментировать тела.
- O-stack-02: infection/infection ^0.32.7 в require-dev + app/infection.json
(minMsi=50, source: Http/Models/Services). composer mutation script.
Запуск отдельной задачей в CI (медленный). allow-plugins для
infection/extension-installer.
- O-stack-03: Laravel 13 string-based lazy-loading в routes/web.php —
убран блок use App\Http\Controllers\Api\* (16 импортов), все
ссылки заменены на строки 'App\Http\...\X@method'. Контроллеры
не загружаются автозагрузчиком при boot route'ов; класс резолвится
только при матче маршрута. Использован строковый синтаксис, а не
FQN-class — Pint default preset (fully_qualified_strict_types fixer)
сворачивает FQN-class обратно в use, на строки не действует.
- O-perf-07: Larastan result cache через tmpDir: .phpstan-cache в
phpstan.neon (cache-dir не дублируется флагом — phpstan не принимает
оба источника). lefthook job 6 (larastan) использует этот же
tmpDir автоматически. Ускорение инкрементальных pre-commit прогонов:
на правке одного файла — переанализ только зависящих, не всего
графа классов. .phpstan-cache в .gitignore.
Pest: 416/416 PASS + 2 skipped (browser smoke pending Linux CI).
Larastan: 0 errors above baseline.
Infection: vendor/bin/infection --version → 0.32.7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
42b3f3f9e4 |
docs(spec-sprint2): дизайн Спринта 2 «Modernization» — 12 O-* находок
Scope: 8 O-stack + 2 O-perf + 2 O-refactor (low/medium risk, без архитектурных рефакторингов). Default-выборы для design-решений зафиксированы в spec'е. 4 фазы: A.Backend (Pest 4 browser + mutation + Laravel 13 lazy + Larastan cache) → B.Frontend (Vue 3.5 + Vuetify 3.12 + lazy-imports + ESLint check) → C.Docs (Google Fonts + CLAUDE.md §0 reorg + FD plugin checklist) → D.Hygiene (dead-code report). Не входит — 6 Sprint 3 пунктов (high risk, R0.6 hard-стопы). Бюджет: 5-6 часов агентов. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
97bdb784d6 |
docs(registry): новые открытые вопросы по аудиту P1-10/11/09
Phase F Спринта 1 «Hygiene». Закрытие аудита 2026-05-09 (
|
||
|
|
e4673bea65 |
docs(handoff): status-slug mapping table + axe-claim documentation + font-display strategy (audit P1-04/05 + O-stack-09)
Phase E Спринта 1 «Hygiene». Закрытие аудита 2026-05-09 ( |
||
|
|
e2de8bf8a2 |
docs(narrative): sync versions + Histoire 21/43 + cross-refs (audit P1-01/03/06/08 + P2-03 + post-A)
Phase D Спринта 1 «Hygiene». Закрытие аудита 2026-05-09 (
|
||
|
|
cc6e1cba72 |
fix(configs): Windows-fix format:sql:check + lychee/pa11y/composer/ESLint hygiene + npm-outdated CI (audit P0-03 + 5 P1/O)
Закрытие аудита 2026-05-09 (
|
||
|
|
56195c8a59 |
fix(backend): tenant middleware на auth-routes + HasPasswordRules trait + test password (audit P0-01 + O-refactor-03 + P2-01)
Закрытие аудита 2026-05-09 (
|
||
|
|
e01caa3e38 |
fix(db): RLS на impersonation_tokens + 2 missing FK indices (audit P0-02 + O-perf-02/03)
schema.sql v8.10 → v8.11. Закрытие аудита 2026-05-09 (
|
||
|
|
93b1af40d0 |
docs(plan-sprint1): implementation plan для Спринта 1 «Hygiene»
По spec'у 2026-05-09-sprint1-hygiene-design.md (
|
||
|
|
08bbe4d9c8 |
docs(spec-sprint1): дизайн Спринта 1 «Hygiene» исправления аудита 2026-05-09
Spec через superpowers:brainstorming. Scope: 22 правки + 3 записи в реестр: - 3 P0 (RLS impersonation_tokens, SetTenantContext middleware, format:sql:check Windows) - 10 P1 (P1-10/P1-11 → реестр) - 3 P2 (test password, орфо, F-K расшифровка) - 6 low-risk O-* (2 FK indices, password rules trait, ESLint anti-vuetify, npm CI, font-display docs) 6 фаз / 6 коммитов: A.DB → B.Backend → C.Configs → D.Docs(narrative) → E.Docs(handoff) → F.Registry. Зависимости: B/D зависят от A (CHANGELOG schema метрики). Бюджет: 2.5-3.5 часа агентов в субагентном режиме. CLAUDE.md/Pravila/Tooling правки в Фазе D — обязательно через claude-md-management:claude-md-improver (CLAUDE.md §5 п.10). Не входит: Спринт 2 (modernization) и Спринт 3 (big refactors) — отдельные spec'и. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b6ae8dd641 |
docs(audit): сводный отчёт аудита проекта на 2026-05-09 — P-дефекты + O-возможности
По spec v1.1 ( |
||
|
|
fb7334af05 |
docs(plan-audit): implementation plan для аудита проекта на 2026-05-09
По spec v1.1 (
|
||
|
|
b0343014db |
docs(spec-audit): пересмотр под Plugin_stack_rules_v1 v1.3 — spec v1.0 → v1.1
§12 «Плагины и MCP» переписана с полным inventory и R-обоснованиями: - 12.1 Superpowers-skills (6 применяемых + 8 из карты §12.2 не релевантны) - 12.2 Встроенные плагины: FD plugin НЕ призывается (R10+R13: аудит ≠ дизайн); claude-md-management вне DoD аудита - 12.3 Внешние плагины (R10): simplify/security-review/review/init/ui-ux-pro-max — только по явному /команде, не призываются - 12.4 MCP: laravel-boost (D2/D4), playwright опционально (D3), github не используется - 12.5 CLI-тулы — не плагины, регулируются Tooling Прил. Н В §3 расширен P0 stack-нарушениями (Pravila §13.9 hard-link на R10). В §6 D1 включает Pravila/Plugin_stack_rules_v1/Tooling в зону аудита; D3 проверяет R11 иерархию источников истины UI/UX; D5 проверяет R6 стек-фильтр Vue+Vuetify и R6.1 Forest. В §7 добавлен общий R-CHECKS блок поверх P/O чеклиста. В §10 DoD self-review проверяет «0 R-нарушений в самом отчёте»; code review мета-проверяет соблюдение Plugin_stack_rules_v1 v1.3. В §13 добавлены риски misuse FD plugin / priority chain / правок Pravila. Новая §15 — карта соответствия Plugin_stack_rules_v1 v1.3 → spec (13 R-правил + Pravila §12/§13.9 + CLAUDE.md §1/§5 п.10/п.11 + Tooling Прил. Н). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4f36bd3601 |
docs(plugin-stack-rules): CLAUDE.md v1.77→v1.81 + Pravila v1.4→v1.6 + Прил. Н v1.9→v1.10 + Plugin_stack_rules_v1 v1.3 (новый) — снятие запрета Frontend Design plugin + paired stack со Superpowers + 23 закрытых трения
Frontend Design plugin (anthropics/frontend-design) подключён через ~/.claude/settings.json paired stack'ом со Superpowers v5.1.0. Координация — через новый docs/Plugin_stack_rules_v1.md (10 правил R0-R9 + R10-R13 + 6 патчей F-K, всего 23 закрытых конфликта). v1.78: снят запрет CLAUDE.md §5 п.5 на Frontend Design plugin. Создан Plugin_stack_rules_v1.md (10 правил, 8 первичных конфликтов закрыты). Pravila +§13 «paired stack». Tooling +#30 в фазе 2. v1.79: PSR v1.0→v1.1 — 5 патчей по реальным трениям A-E (R6.1 hard-override Forest, R1 дезамбигуация компонент=UI-фича, R7 deployable+Pa11y, R0.6 hard-стоп список из 8 триггеров, runtime-заметка о skill list = constant per conversation). v1.80: PSR v1.1→v1.2 — принцип-аксиома «stack — головной» + R10 (внешние плагины как tools, реестр 11 плагинов с ролями: ui-ux-pro-max=резерв, claude-md-management=инструмент, review/security-review/init/simplify=только по /имя), R11 (иерархия 6 источников истины UI: Brandbook→ТЗ+schema→FD→Boost→UPM→Vue/Vuetify docs), R12 (три паттерна дизайн-решений), R13 (decision matrix Auto+§12+R0.6 на 14 типов задач). v1.81: PSR v1.2→v1.3 + Pravila v1.5→v1.6 — 6 трений второго порядка F-K. F: R12 архитектурное override §4.5 через явный brainstorming. G: R12 тактическое split на «с альтернативами» (A/B/C под user-стиль «а/б») и «без» (одна BOLD от FD). H: R13 строка про новую UI-фичу разделена — «вне ТЗ И не в Открытые_вопросы» = hard-стоп (Pravila §7). I: R11.4 fallback при технической недоступности уровней источников. J: R10.4 смягчение + Pravila §13.9 hard-link (нарушение R10 = нарушение §13). K: R0.1 точный scope «головенства» через таблицу priority chain. Skill list = constant per conversation — для активации FD требуется новый чат. cspell-words.txt +5 (инвокация, инвокирован, инвокируемые, инвокируются, головенство). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3d936d0da8 |
docs(spec-audit): добавлен дизайн подробного аудита проекта на 2026-05-09
Spec через superpowers:brainstorming. Гибридный подход (тулы + субагенты): 4 этапа (sweep тулами → 6 семантических субагентов → консолидация → self-review), P0/P1/P2 для дефектов и O-perf/O-refactor/O-stack для возможностей улучшения. Реализация — отдельным implementation plan через superpowers:writing-plans. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4cac61d748 |
docs(superpowers-hard-rule): CLAUDE.md v1.76→v1.77 + Pravila v1.3→v1.4 + Прил. Н v1.8→v1.9 — Superpowers как hard rule §12
Заказчик 09.05.2026 ввёл правило явной командой: «Создай правило, что ты всегда в первую очередь пользуешься superpowers. При этом ты не можешь игнорировать и обходить это правило». Pravila §12 — единственное hard-правило документа: §9 «Отступления» к нему не применяется. Pravila v1.4: - Добавлен §12 «Superpowers — приоритет первого выбора (hard rule)», 8 подсекций. - §12.1 Принцип: skill инвокируется ПЕРВЫМ перед любой содержательной задачей; обычный flow только если skill отсутствует. - §12.2 Карта 14 задач → 14 skills (TDD/debug/plan/parallel/review/ verify/brainstorm/worktree/finishing PR/subagent/writing-skills/ using-superpowers). - §12.3 Когда правило НЕ применяется: чтение/grep/glob, тривиальные правки, справочные ответы, документация §4, открытые вопросы (§7). - §12.4 Hard-rule статус: §9 не применяется; единственная отмена — явный запрос «не используй superpowers сейчас» только на текущее действие; рационализация — нарушение уровня §5 ПДн. - §12.5 §12 имеет приоритет над §11. - §12.6 §5/§7/§3.6 не override-ятся даже §12. - §12.7 Нарушения: фиксация в feedback memory. - §12.8 Ревизия: откат только явным запросом. - §0 priority расширен — §12 встаёт перед §1. CLAUDE.md v1.77 (через /claude-md-management:claude-md-improver): - §1 priority chain +уровень 0: «Pravila §12 — Superpowers hard rule». - §0 — Pravila v1.3 → v1.4. - §3.3 строка №19 дополнена пометкой «§12 hard rule». - §5 +п.11 «Не пропускать инвокацию Superpowers skill'а». Прил. Н v1.9: шапка + §4.1 callout про hard rule. cspell-words.txt: +5 слов второй порцией (инвокацию/инвокируется/ инвокирует/инвокировать/банами/банов). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4993790fd5 |
docs(superpowers-unblock): CLAUDE.md v1.75→v1.76 + Pravila v1.2→v1.3 + Прил. Н v1.7→v1.8 — снятие запрета на Superpowers
Заказчик 09.05.2026 принял вариант "A. Полное снятие, включая Pravila". Плагин obra/superpowers v5.1.0 (14 skills) подключён через ~/.claude/settings.json (extraKnownMarketplaces.superpowers-dev + enabledPlugins.superpowers@superpowers-dev). Декларативная установка — Claude Code сам подтянул кэш на рестарте. Pravila v1.3: - Добавлен §11 «Superpowers plugin — снят запрет, override §2.2/§4.5/§8.4». - §11.1: brainstorming/writing-plans/executing-plans/dispatching-parallel-agents могут перевешивать §4.5/§8.4/§2.2 при явном вызове skill'а. - §11.2: §1/§3.6/§5/§7/§9 не override-ятся. - §11.3: using-git-worktrees физически нестабилен на кириллическом пути — это факт среды, не правил. - §11.4: ревизия — заказчик может откатить §11 одной правкой. - Приоритет в §0 расширен до §11. Таблица версий +v1.3. CLAUDE.md v1.76 (через /claude-md-management:claude-md-improver по §5 п.11): - §5 п.4 удалён (запрет на 5 Superpowers skills). - Перенумерация 5→4..11→10. Перекрёстные ссылки переподписаны (п.11→п.10 в шапке п.7; пп.8→пп.7 внутри п.10). - §3.3 строка №19: «3 skills» → «v5.1.0, все 14 skills, override §2.2/§4.5/§8.4 разрешён, см. Pravila §11». - §0 — Pravila v1.2+ → v1.3 от 09.05.2026. Прил. Н v1.8 (Tooling_v8_3.md): - §4.1 «Поведенческий слой — Superpowers» полностью переписан: 3 skills → 14 skills, установка декларативная (settings.json), а не /plugin install. - Снятые ранее запреты сохранены как историческая запись. - §11.2 «Git worktrees»: запрет снят, но Windows + кириллица остаются враждебной средой, skill сам обрабатывает ошибки. cspell-words.txt: +5 слов (Лидерре, obra, override, ятся, ится). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f596e26153 |
docs(admin-tenant-detail-epic): фиксация Post-MVP AdminTenantDetailView закрытия
- Реестр v1.75→v1.76: запись о закрытии эпика, метрики Pest 416/416 + Vitest 416/416. - CLAUDE.md в этот раз не правлю: §5 п.11 — синхронизация при следующем /claude-md-management:claude-md-improver run'е. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cab1f87efd |
phase2(admin-tenant-detail-frontend): replace mock на real API в AdminTenantDetailView
- api/admin.ts +getAdminTenantDetail(subdomain) + 5 типов (ApiTenantUser/Project/
BalanceTx/ActivityEvent/Metrics + AdminTenantDetailResponse).
- composables/adminTenantDetailMapper.ts: mapAdminTenantDetail (API → mockTenantDetail
format). code=subdomain, deriveStatus (trial/overdue/suspended), deriveTariff
(Trial fallback), users (fullName из first+last||email, role='manager' хардкод —
schema users role нет, расширим в Post-MVP), projects (slug=tag), balanceHistory
(id префикс TX-, type-mapping для chargeback_*/trial_bonus/historical_import →
ближайший UI-эквивалент), activity (actor=actor_email||system, summary из
context.from→to), activitySinceText (relative time из last_activity_at).
- AdminTenantDetailView.vue: replace mock-lookup на async loadTenant + 3 ветки
template (loading / notFound / fetchError) + watch(code) для реактивной
навигации. inn/contact_phone/legal_address скрываются через v-if (нет в schema).
- AdminTenantDetailView.spec.ts переписан с MOCK на vi.mock('api/admin'):
13 тестов (вызов API с subdomain / organization_name+tariff / 4 KPI / KPI Лиды
todayActual/desired / Финансы tab / Пользователи tab / Проекты tab / Активность
tab с actor+summary / Войти как клиент / suspended disabled / 404 fallback /
500 fetch-error / overdue Просрочка / trial без оплаты).
- adminTenantDetailMapper.spec.ts +20 тестов: code/name/inn-empty/balanceRub
parse/mrrRub trial-null/status (4 ветки)/tariff (deriveTariff+fallback)/today
Actual+Desired/users (fullName / fallback)/projects/balanceHistory (TX- prefix +
chargeback type mapping)/activity (actor+summary)/metrics (4 поля)/activitySince.
- Vitest +23 (всего 416/416, +23 от 393).
Этап B эпика AdminTenantDetailView (frontend) ЗАКРЫТ. Эпик закрыт целиком (2 этапа).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f9d8926945 |
phase2(admin-tenant-detail-backend): GET /api/admin/tenants/{subdomain} с 4 секциями
- AdminTenantsController +show($subdomain): возвращает tenant base + users +
projects + balance_history + activity + computed metrics (leads_today/week/
month, avg_lead_cost_rub, runway_days). Lookup по subdomain (естественный
URL slug) + whereNull deleted_at. Без auth-middleware (saas-admin SSO ⏸ Б-1).
- 4 private fetch'ера + computeMetrics:
- fetchUsers: ORDER last_active_at DESC, LIMIT 50, поля email/first/last/
is_active/totp_enabled/last_active_at/last_login_at.
- fetchProjects: LEFT JOIN sub-queries для suppliers_count + leads_today
(deals в текущем дне). Поля name/tag/is_active/daily_limit_target.
- fetchBalanceHistory: ORDER created_at DESC, LIMIT 30. Поля type/amount_rub/
amount_leads/balance_rub_after/description/created_at.
- fetchActivity: LEFT JOIN users (actor_email), LIMIT 20. context json_decode.
- computeMetrics: один SELECT с FILTER для leads counts; AVG cost_rub за
30 дней; runway_days = balance / (month_spend / 30).
- routes/web.php: GET /api/admin/tenants/{subdomain} where [a-z0-9_-]+.
- Pest +13 в AdminTenantShowTest.php (всего 416/416, +13 от 403, 1388 assertions):
404 unknown / 404 soft-deleted / базовые поля / 4 секции + metrics keys в response /
users изоляция / projects suppliers_count + leads_today / balance_history ORDER+LIMIT 30 /
balance_history изоляция / activity actor_email LEFT JOIN (user + system events) /
metrics leads_today/week/month / metrics runway_days computed / tariff_name+mrr_rub /
mrr_rub null для trial.
- phpstan-baseline регенерирован.
Этап A эпика AdminTenantDetailView (backend) закрыт. Этап B: frontend
integration + Vitest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
b8f1c16ce2 |
docs(claude-md): v1.74→v1.75 — currency refresh через claude-md-improver
Application of skill /claude-md-management:claude-md-improver Phase 5 после повторного audit'а (88/100, A−). 4 stale места — все currency-only, не меняют правила/состав инструментов: (1) §2 «Стек проекта» — БД метрики 55/12/92/36 → 56/12/95/37 (v8.10 от 09.05.2026, прежде стояло v8.6). (2) §6 hero-параграф — заголовок «Фаза 2 ⏸ (08.05.2026)» → «Post-MVP (09.05.2026)»; добавлен полный backend-coverage (auth/deals/reminders/notifications/admin/impersonation/webhook); метрики Pest 403/403 + Vitest 393/393 + Histoire 21/28. (3) §3.1 строка #1 (PostgreSQL MCP) — strikethrough + пометка «заменён #10 Boost (см. §3.2)». Заголовок секции «Фаза 0 — сейчас» → «Фаза 0 — документация». (4) §8 self-review триггеры — пример schema-метрик 54/12/91/34 (v8.5) → v8.10 56/12/95/37 + текст «сверять с текущей версией». Шапка v1.74 → v1.75. §9 «История версий» — добавлен entry v1.75. Pravila/Tooling sync не требуется (§5 п.8 — currency, не правила). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5b13c95180 |
docs(claude-md): refactor v1.73→v1.74 — вынос changelog + правило single-entry plugin
(1) Структурный refactor — 250+ строк changelog v1.1→v1.73 вынесены из шапки CLAUDE.md в новый docs/CHANGELOG_claude_md.md (CLAUDE.md 380→234 строк, −39%; conciseness score 5/15→14/15 по quality-criteria плагина claude-md-management). (2) Введено правило §5 п.11 — все правки CLAUDE.md только через плагин claude-md-management из anthropics/claude-plugins-official: /claude-md-improver (audit + targeted updates) и /revise-claude-md (capture session-learnings). Прямые Edit/Write — нарушение. §5 п.8 дополнен оговоркой о применимости внутри flow п.11. В шапке добавлена строка про владельца. (3) cspell-words.txt +23 слова — техжаргон из перенесённого changelog'а (anthropics/CWD/маппится/бампается/etc); часть из них опечатки в исторических записях, не правлю исторический контент. Содержательно правила/состав 28 инструментов/метрики не тронуты — синхронизация Pravila/Tooling не требуется. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ab6a74052c |
docs(reports-epic): фиксация Post-MVP Reports backend закрытия
- Реестр v1.74→v1.75: запись о закрытии эпика, метрики Pest 403/403 + Vitest 393/393. - CLAUDE.md v1.72→v1.73: подробные изменения по 4 этапам. - cspell-words.txt: +«реструктура». Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e0ffe7e686 |
phase2(reports-stage4): frontend integration ReportsView (replace mock)
- api/reports.ts: типизированные axios-helpers (listReportJobs/createReportJob/
retryReportJob/cancelReportJob/deleteReportJob) + ApiReportJob/Status/Format/
Counts/Quota interfaces. ensureCsrfCookie на mutating-вызовах.
- composables/reportsMapper.ts: mapApiReportJob (API → UI mock format с конверсией
pending→queued / processing→running). title строится на frontend'е (тип + period
с RU-месяцами «апр 2026» или диапазон «мар 2026 — апр 2026»). sizeText форматирует
bytes (B/KB/MB). timeText зависит от status (в очереди / в работе · Nс / N мин назад).
uiTypeToApi (deals → deals_export и т.д.).
- ReportsView.vue полностью переписан под API:
- onMounted → loadJobs (replace MOCK_JOBS на data из listReportJobs).
- usePolling 30 сек (фоновый авто-refresh).
- Submit → createReportJob → reload + success-alert + error-alert (validation+
общие ошибки извлекаются через extractValidationErrors/extractErrorMessage).
- canSubmit computed: disable если квота заполнена (active >= max).
- Reset-btn возвращает форму к defaults.
- Reload-btn (manual fast-path).
- Retry/Cancel/Download/Delete-кнопки → соответствующие API-вызовы;
Delete через v-dialog persistent confirm.
- fetch-error-alert на listReportJobs reject.
- Empty-state «Нет отчётов» когда jobs.length=0.
- canRetry проверяет retry_count<3 (max attempts CTO-6).
- Vitest +24 (всего 393/393, +24 от 369):
- reportsMapper.spec.ts +14: status mapping (pending/processing/done/failed) /
title (один месяц / диапазон) / format / sizeText (B/KB/MB/null) / attempt /
error / timeText (pending / processing / done «10 мин назад» / «только что») /
uiTypeToApi 4 slug'а / progress=50 для running.
- ReportsView.spec.ts переписан с MOCK_JOBS на vi.mock('api/reports') +12:
mount + listReportJobs called on mount / 4 type cards / default Сделки active /
4 формата / quota-banner из API / empty-state / done с Готов+Скачать /
failed с Ошибка+Повторить / failed retry_count=3 НЕ показывает Повторить /
pending с Отменить / Submit вызывает createReportJob+reload / Submit error →
submit-error-alert / Submit-btn disabled при квоте 3/3 / Reset / Reload-btn /
fetch-error-alert / Retry-btn / Cancel-btn / Delete confirm-dialog +
deleteReportJob.
Этап 4/4 эпика Reports backend ЗАКРЫТ. Эпик закрыт целиком.
Backend: 1 type (deals_export) × 4 формата (CSV/XLSX/JSON/PDF-stub).
Этап 2b (3 оставшихся типа: managers_summary/sources_summary/billing_summary)
— расширение через добавление 3 новых Provider-классов без изменений в архитектуре,
вынесено в Post-MVP backlog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
9765ed760d |
phase2(reports-stage3): retry/cancel/destroy + reports:cleanup-expired cron
- ReportJobController +3 endpoints под auth:sanctum:
- POST /api/reports/jobs/{id}/retry — CTO-6: только owner+failed, max 3 попытки
(parameters.retry_count), окно 7 дней с created_at, квота CTO-7 учитывается;
создаёт НОВЫЙ ReportJob (parameters.retry_of=original.id) + dispatch.
- POST /api/reports/jobs/{id}/cancel — только owner+pending; status=failed +
error_message=«Отменено пользователем.» + finished_at=NOW.
- DELETE /api/reports/jobs/{id} — только owner+terminal (done|failed); удаляет
файл из disk('local') + row.
- toResource +3 поля: is_expired (expires_at < NOW), retry_count, retry_max=3.
- App\Console\Commands\ReportsCleanupExpired (cron `reports:cleanup-expired`):
где status='done' AND expires_at < NOW AND file_path IS NOT NULL → delete file
+ UPDATE file_path=NULL. CTO-10: status='done' СОХРАНЯЕТСЯ. failed-jobs
игнорируются. --dry-run + --limit=1000. Запуск ежесуточно через Task Scheduler.
- routes/web.php: новые 3 routes под существующим prefix /api/reports/jobs.
- Pest +21 в ReportLifecycleTest.php (всего 403/403, +21 от 382, 1343 assertions):
retry 8 (404 unknown/foreign / 403 не владелец / 422 не failed / success+new+
retry_count=1+retry_of / 422 max retries / 422 окно 7 дней / 422 квота 3) +
cancel 4 (404 / 422 не pending / success / 403 не владелец) + destroy 5
(404 / 422 pending / 403 не владелец / success+file / success+file_path=NULL)
+ index +1 (is_expired/retry_count/retry_max в response) + cron 3 (удаление
expired+CTO-10 status сохраняется / --dry-run / failed игнорируются).
- phpstan-baseline регенерирован.
Этап 3/4 эпика Reports backend (закрыт). Этап 4: frontend integration —
заменить mock в ReportsView на реальный API + UI кнопки retry/cancel/delete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1a6a74c1a0 |
phase2(reports-stage2): provider+formatter архитектура + XLSX/JSON/PDF-stub
- Реструктура Services/Reports: вместо `Generator` per (type×format) комбинации (16 классов) разделено на 4 Providers + 4 Formatters (8 классов). - App\Services\Reports\Providers\ReportDataProvider interface + DealsExportProvider (вынесен из старого DealsExportCsvGenerator; возвращает headers + rows). - App\Services\Reports\Formatters\ReportFormatter interface + 4 реализации: - CsvFormatter — Excel-friendly (BOM + ; + \r\n + escape). - XlsxFormatter — PhpSpreadsheet 5.x (A1-нотация + bold headers + auto-size cols). - JsonFormatter — pretty + UNESCAPED_UNICODE (кириллица в исходном виде). - PdfStubFormatter — Post-MVP, throw RuntimeException. - ReportGeneratorRegistry перепаспортирован: provider(type) + formatter(format). - GenerateReportJob: вызывает provider->headers/rows + formatter->format вместо старого generator->generate. - Удалено: DealsExportCsvGenerator, ReportGenerator interface, GenerationResult DTO. - Pest +3 (всего 382/382, +3 от 379, 1297 assertions): xlsx → done с XLSX-magic-bytes PK\x03\x04; json → done + decoded ['rows', 'headers']; pdf → failed «Post-MVP»; managers_summary (не реализован) → failed. - phpstan-baseline регенерирован. Этап 2/4 эпика Reports backend (закрыт). Этап 2b: 3 оставшихся типа провайдеров (managers_summary / sources_summary / billing_summary) — каждый × 4 формата без изменений в архитектуре. Этап 3: retry/cancel/delete + retention cron. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
19f319cd5d |
phase2(reports-stage1): ReportJob model + GenerateReportJob + API + deals_export csv
- ReportJob Eloquent (schema §13.5 report_jobs): status pending/processing/done/failed,
parameters JSONB (format/date_from/date_to/project_id?/manager_id?), constants
TYPES + FORMATS, helpers isActive/isDone/isFailed.
- ReportJobFactory + states processing/done/failed.
- App\Services\Reports\* пакет: ReportGenerator interface, GenerationResult DTO,
ReportGeneratorRegistry с резолвом по (type,format), DealsExportCsvGenerator
(Excel-friendly CSV: BOM, ; separator, \r\n, escape; deals JOIN projects/users/
supplier_lead_costs за date_from..date_to, soft-deleted скрыты).
- App\Jobs\GenerateReportJob: tries=1 (auto-retry отключён, retry через UI кнопку
CTO-6); меняет status pending → processing → done|failed, заполняет file_path/
file_size/generation_seconds/finished_at/expires_at (=NOW+30д).
- App\Http\Controllers\Api\ReportJobController под auth:sanctum:
- GET /api/reports/jobs?status=&limit=&offset= → jobs+total+counts+quota
- GET /api/reports/jobs/{id}
- POST /api/reports/jobs (квота CTO-7: max 3 active per tenant → 422)
- dispatch GenerateReportJob (sync на dev → файл готов сразу).
- Storage local-disk на dev (storage/app/reports/{tenant_id}/{job_id}.csv);
на prod заменим на s3 (Yandex Object Storage) отдельным коммитом.
- Pest +20 в tests/Feature/Reports/ReportJobControllerTest.php (всего 379/379,
1280 assertions): 401 без auth / GET пустой+only-own+ORDER+filter+counts+limit/
show success+404 own/foreign / store 422 (без полей/неизвестный type/date_to<from)/
dispatch / sync queue → done с file (BOM проверен) / unsupported format → failed/
квота 3 → 422 на 4-м / квота не считает done+failed / квота per-tenant.
- phpstan-baseline регенерирован (+1 ignored для Factory typing).
Этап 1/4 эпика Reports backend (закрыт). Этап 2: 4 типа × 4 формата.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
07ff7c8bb2 |
docs(mvp-closed): фиксация финала фазы 2 — Claude-зона MVP закрыта
- Реестр v1.73→v1.74: запись о закрытии MVP, остаточные blocked-only. - CLAUDE.md v1.71→v1.72: финал-метрики Pest 359/359 + Vitest 369/369. - cspell-words.txt: +«бэкенда» (творительный падеж). - Дальнейшая работа возобновляется по триггерам (Б-1, юр., фаза 3, deploy). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4c33323f0e |
phase2(notifications-stage6): low_balance + zero_balance + topup + invoice email/inapp
P0 этап 6 — 4 оставшихся email-события. Авто-план P0 (6 этапов) закрыт полностью: все 8 schema-default событий имеют рабочую интеграцию (new_lead/reminder/low_balance/zero_balance/topup_success/invoice_paid + заглушки для new_device_login/marketing). Backend: - 4 новых Mailable: LowBalanceNotification (threshold), ZeroBalanceNotification, TopupSuccessNotification (amountRub, amountLeads?), InvoicePaidNotification (amountRub, invoiceNumber?, tariffName?). - 4 blade-шаблона в emails/ (Forest-палитра, таблицы balance/amount/invoice). - NotificationService +4 методов: notifyLowBalance / notifyZeroBalance / notifyTopupSuccess / notifyInvoicePaid. Все шлют email + inapp по prefs. Интеграция в ProcessWebhookJob: - chargeNewLead после lead_charge: notifyLowBalance при пересечении порога сверху-вниз (balance_after <= threshold AND (balance_after+1) > threshold). Иначе спам при каждом lead_charge при balance < threshold. - logRejection(zero_balance): notifyZeroBalance ТОЛЬКО если в последний час не было другого RejectedDealsLog с тем же reason (anti-spam 1 email/час). Защита от self-just-inserted через id!= (timestamp-сравнение ненадёжно из-за PG microsecond precision). - topup_success / invoice_paid — service-методы готовы, integration после появления endpoints для пополнения (ЮKassa-webhook) и оплаты тарифа. - lowBalanceThreshold() читает system_settings.low_balance_threshold_leads (default 10, schema seed). Pest +12 в BalanceNotificationsTest (359/359 за 41.37 сек, 1233 assertions): - low_balance: пересечение порога / уже < threshold / > threshold / prefs.email=false (только inapp). - zero_balance: первое отклонение / 2-е в час не дублирует / >1ч снова шлёт. - topup_success / invoice_paid: notify создаёт email+inapp / prefs=email:false. - balance events изолированы между tenants. NewLeadNotificationTest: «balance=0 не шлёт» обновлён — Mail::assertNotSent(NewLeadNotification) вместо Mail::assertNothingSent (ZeroBalanceNotification теперь шлётся при balance=0 — новое поведение). PHPStan baseline регенерирован. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dc1457a008 |
phase2(reminders-frontend): RemindersView + DealDetailDrawer + nav-badge
P0 этап 5 — frontend для reminders (после backend-этапа 4). Пользователь может создавать/просматривать/завершать/удалять напоминания из UI с inline-create в DealDetailDrawer. Frontend: - api/reminders.ts: типизированные helpers для 5 endpoints + ensureCsrfCookie для mutating. Types ReminderFilter/ApiReminder/ReminderCounts. - stores/reminders.ts: Pinia с items/counts/currentFilter + load/refreshCounts/create/update/complete/remove. Optimistic для complete/remove с revert на reject. - components/reminders/ReminderDialog.vue: dual-mode (create/edit) modal с native datetime-local input. Props dealId?/reminder? (edit), ISO-конверсия при submit. - views/RemindersView.vue: page-head с stats (active/overdue) + reload-btn; 4 tabs (today/upcoming/overdue/completed) с counts на бейджах (overdue=error color); v-list с complete-btn / dropdown Изменить/Удалить с confirm-dialog; empty-state. - router: /reminders маршрут (lazy). - AppLayout: nav-badge «Напоминания» биндит count из store (replace static «12»); скрыт при count=0; polling 60 сек для refreshCounts. - DealDetailDrawer: секция «Напоминания» (только при tenantId+deal): inline + create-btn / список / complete / встроенный ReminderDialog. Vitest +20 (369/369 за 21.20 сек): - reminders-store 11: initial / load+reject / refreshCounts / create+reject / complete optimistic+revert / remove+reject / reset. - RemindersView 7: mount / 4 tabs / counts / empty-state / список / reload-btn / filter=today default. - AppLayout +2: бейдж скрыт при count=0 / показывает count при >0. Pest 347/347 (без изменений — backend нетронут). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
39b6127bce |
phase2(reminders-backend): CRUD + cron-диспетчер + email/inapp (P0 этап 4)
Закрыт пункт «Reminders ⏸ no-view» из AppLayout nav-tree. Schema-таблица
reminders уже была в v8.10 §17.5 — теперь работает целиком backend-side.
Backend:
- App\Models\Reminder — Eloquent с casts/relations + isCompleted/isOverdue.
- ReminderFactory с states overdue/completed/sent.
- App\Http\Controllers\Api\ReminderController под auth:sanctum:
GET ?filter=&deal_id=&limit= (active/today/upcoming/overdue/completed,
окно ±1 день, counts для UI badges);
POST {deal_id, text?, remind_at, assignee_id?} (FK guard на assignee);
PATCH {id} (при смене remind_at сбрасывает is_sent+sent_at для retrigger);
POST {id}/complete (idempotent);
DELETE {id}.
RLS-обёртка + defense-in-depth where('tenant_id').
- App\Mail\ReminderDueNotification + emails/reminder.blade.php (Forest,
TZ из recipient.timezone).
- NotificationService::notifyReminder(Reminder) — recipient = assignee_id
?? created_by (если active+!deleted). Каналы email+inapp по prefs.
payload {reminder_id, deal_id} для UI deep-link.
- App\Console\Commands\RemindersDispatchDue — cron reminders:dispatch-due
{--dry-run} {--limit=500}. По одному reminder в DB::transaction (SET
LOCAL app.current_tenant_id нельзя переключать). После notifyReminder
ставит is_sent=true даже если recipient deactivated (защита от retry-spam).
Pest +32 (347/347 за 41.21 сек, 1203 assertions):
- ReminderControllerTest 21: 401 / RLS / 5 filter'ов / counts / deal_id /
store + FK guard / update text+remind_at сбрасывает is_sent / complete
idempotent / delete + 404 чужой.
- RemindersDispatchDueTest 11: due → email+inapp / future skip / completed
skip / уже sent / assignee вместо created_by / deactivated user (is_sent
всё равно) / только inapp при email=false / --dry-run / --limit / RLS.
PHPStan baseline регенерирован. IDE-helper для всех моделей.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f55b91cfa4 |
phase2(notifications-stage3): NotificationsTab schema-aligned + prefs API
Закрывает архитектурное расхождение v1.28 — Tab сохранял prefs только локально без API. Backend events не совпадали с handoff'ом. Backend: - PATCH /api/auth/me/notification-preferences под auth:sanctum. - Replace-семантика: незадекларированные events/channels отбрасываются. - userResource расширен: notification_preferences + sound_enabled. - UserFactory с schema-default JSON (Eloquent не перечитывает после INSERT, DB-DEFAULT JSONB виден как null без явного override). - Pest +10: 401 / replace / неизвестные events/channels отбрасываются / 422 без prefs / sound_enabled опционален / bool-cast 1/'1' / replace- семантика (отсутствующие events исчезают). Frontend: - api/auth.ts: типы NotificationChannel/EventKey/Preferences + updateNotificationPreferences helper. AuthUser получил optional поля. - NotificationsTab.vue переписан под schema: 8 событий (new_lead/reminder/low_balance/zero_balance/topup_success/ invoice_paid/new_device_login/marketing) × 3 канала (inapp/push/email, НЕ sms). Sync-init prefs (без onMounted — иначе v-if блокирует рендер и тесты mount-then-find падают). dirty через computed-сравнение с originalPrefs snapshot. save async + success/error alerts. - SettingsView.spec.ts: legacy event-имена → schema-aligned. - Vitest +10: 8 schema events / 3 channels (НЕ sms) / legacy отсутствуют / читает prefs из user / save calls API + alerts / Отменить возвращает. cspell-words: +prefs. PHPStan baseline регенерирован. Pest 315/315 (+10) за 36.73 сек, 1130 assertions. Vitest 349/349 (+10) за 20.42 сек. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
508de4eaf3 |
phase2(notifications-stage2b): API + Pinia + bell в AppLayout (P0 этап 2b)
Закрывает этап 2 P0 целиком (UI bell с unread badge + polling).
Backend:
- App\Http\Controllers\Api\InAppNotificationController под auth:sanctum:
GET /api/notifications?unread_only=&limit= (1..100 default 50);
PATCH /api/notifications/{id}/read (idempotent);
POST /api/notifications/mark-all-read (bulk + count);
DELETE /api/notifications/{id}.
- Route::middleware('auth:sanctum')->prefix('/api/notifications') в web.php.
- DB::transaction + SET LOCAL app.current_tenant_id для RLS.
- Защита от кражи чужого id через where('user_id', $auth->id).
- Pest +14 (305/305 за 34.71 сек, 1099 assertions).
Frontend:
- api/notifications.ts — типизированные axios-helpers + ensureCsrfCookie.
- stores/notifications.ts — Pinia: items/unreadCount/total/loading +
optimistic markRead/markAllRead/remove с revert на reject.
- AppLayout: bell-icon → v-menu offset=8 location=bottom-end:
pip badge показывает unreadDisplay (1..99 / 99+ / hidden);
v-list последних 10 из sortedItems с event-icon + formatRelative;
Mark-all-read btn только при unreadCount > 0;
click на item → markRead + router.push('/deals') если deal_id.
- usePolling(loadNotifications, {intervalMs: 30_000}) с Page Visibility.
- loadNotifications no-op без auth.user.
- Vitest +18 (339/339 за 20.03 сек): store 12 + AppLayout +6
(bell-btn / pip скрыт при 0 / pip count / 99+ / listNotifications
на mount с user / no-op без user).
PHPStan baseline регенерирован (50 Pest false-positives подавлены).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
7f5ff874a8 |
phase2(notifications-stage2a): in_app_notifications + notifyInApp (schema v8.10)
P0 этап 2a — backend-фундамент bell-icon канала. UI bell + API endpoints — этап 2b отдельным коммитом. Schema v8.9 → v8.10: - Новая таблица in_app_notifications (после reminders в schema): id/tenant_id/user_id/event/title/body/deal_id/payload/read_at/created_at. - 2 индекса: unread (user_id, created_at DESC) WHERE read_at IS NULL + recent (user_id, created_at DESC). - RLS tenant_isolation. - Метрики: 55→56 таблиц, 93→95 индексов, 36→37 RLS. - CHANGELOG_schema.md +§T. Backend: - App\Models\InAppNotification — Eloquent с UPDATED_AT=null. - NotificationService::notifyInApp — INSERT через DB::transaction + SET LOCAL app.current_tenant_id для RLS. Throwable + Log::warning. - notifyNewLead шлёт два канала параллельно: email + inapp. Pest +11 (291/291 за 32.94 сек, 1060 assertions): - inapp=true/false; schema-default (inapp=true в схеме); - 2 user'а получают / inactive не получает / RLS изоляция; - дубль Биз-19 / повторный vid / оба канала / payload deal_id; - notifyInApp напрямую с reminder. PHPStan baseline регенерирован. IDE-helper для InAppNotification. cspell-words: +inapp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |