b9917a90d4b5492c8fe84fc62a72594de85a3ffe
261 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
528fe16166 |
docs(plans): Sprint 6 Phase A — Reports backend implementation plan
6 tasks: 3 providers (managers/sources/billing) + registry extension + S3 disk abstraction + file download endpoint. TDD with Pest 4. Corrects schema column names vs spec (manager_id, type, balance_rub_after) and uses May 2026 test dates (partitions start 2026-05-01). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
2a6f0289db | docs(spec): Sprint 6 Post-MVP backend design (Reports + Notifications + AdminTenantDetail) | ||
|
|
3c13cc5d64 |
docs(rules): третий аудит нормативки — закрытие 13 находок (CLAUDE.md v1.86 + PSR_v1 v1.7 + Tooling v1.15)
P0 (4) — арифметические конфликты в CLAUDE.md, прошли мимо второго аудита: - §3 header «Карта 28 инструментов» → «33» (header застрял с pre-FD эпохи) - §3.4 header «(+5, итого 28)» → «итого 29» (после #30 в фазу 2) - §3.3 footer «из 30 номеров минус #1 = 29» → «33 номеров: 29 phase-active + 3 off-phase + 1 historic» - §6 «Активно: 19 инструментов из 29» (vs раскладка 9+8+7=24) → «24» в обоих местах P1 (5) — sync stale `+`-refs после второго аудита: - PSR_v1 шапка: «CLAUDE.md v1.84+/Pravila v1.9+» → «v1.86+/v1.10+» - Tooling шапка: «Pravila v1.9+/PSR_v1 v1.5+/CLAUDE.md v1.84+» → «v1.10+/v1.7+/v1.86+» - CLAUDE.md §5 п.5 «PSR_v1 v1.5+» → «v1.7+» P2 (2) — внутренние несогласованности: - PSR_v1 line 4/856 «slot уровня 2.5» → «уровня 2b» (описка внутри changelog'а v1.6) - CLAUDE.md §3.3 #33 «вне Pravila §13» → «вне UI-пула §13» (Pravila §13.2 v1.10 включает claude-md-management как infrastructure subsection) Sync-правки в connected files: - README.md + README_АРХИВ_v8_5.md «карта 28 инструментов» → «33» - Tooling §11.5/§12 «не входят в 28 инструментов» → «33 формализованные позиции» Через `/claude-md-management:claude-md-improver` с cross-impact-анализом перед каждой группой правок. Pravila v1.10 без изменений (cross-refs к нему уже актуальны). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
07ed3f1be6 |
docs(rules): второй аудит нормативки — закрытие 15 находок (CLAUDE.md v1.85 + Pravila v1.10 + PSR_v1 v1.6 + Tooling v1.14)
Закрыто 15 находок второго аудита правил использования плагинов и скилов (4 P0 реальных противоречий + 7 P1 stale-refs + 4 P2 структурных). Через `/claude-md-management:claude-md-improver`. P0 — реальные противоречия: - Tooling §10.3 шаг 2: «3 skills» → «14, все запреты сняты Pravila §11» (закрыта внутри-документная контрадикция с §4.1 того же файла) - CLAUDE.md §6: арифметика «33» исправлена (+1 заменённый PG MCP исторически; раньше 19/29 + 3 = 32, не 33) - Pravila §13.2: «v1.4 (15 правил)» → «v1.6 (16 правил R0–R15)» - Tooling §13: добавлены v1.13 + v1.14 entries (раньше история обрывалась на v1.12, хотя шапка описывала v1.13) P1 — массовый stale-refs дрейф v1.4→v1.6 + v1.12→v1.14: - CLAUDE.md §0 cross-refs (Pravila/PSR_v1/Tooling rows), §3.3 #31, #32, §5 п.12 - Pravila §11.5 («10 правил»→«16»), §13.9, §13.10 - Tooling §4.4 («10 правил»→«16»), §4.5, §4.6, §4.7 P2 — структурные: - Tooling Прил. Н добавлен explicit-слотом уровня 2b в priority chain (CLAUDE.md §1, PSR_v1 R0.1 таблица, Tooling §7) — раньше формальная дыра «PSR_v1 R0.1 говорит stack ниже Tooling, но Tooling нет ни в одной priority chain» - PSR_v1 R0.4.A свёрнут до cross-ref на Pravila §12.3 SoT — устранён риск дрейфа формулировок (раньше параллелил список разной формулировкой) - Pravila §0 +note про §11 локальное override-исключение над §2.2/§4.5/§8.4 (раньше §11 формально стоял ниже §9 в цепочке вопреки фактическому override §2.2/§4.5/§8.4 при skill-инвокации) - PSR_v1 R0.6 hard-стопы пронумерованы 1–11 для надёжности cross-refs «пункт 9/10/11» (раньше буллет-list, ссылки молча ломались) Все 4 файла нормативной документации внутренне непротиворечивы. История версий синхронизирована. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ffe34e1b17 |
docs(registry): Sprint 4 «Audit tail» closure record (v1.76→v1.77)
Sprint 4 закрыл 3 оставшихся audit O-* пункта (5 коммитов f77c91d..b912724): - O-perf-04: keyset pagination в DealController::index + 3 Pest-теста - O-refactor-04 хвост: 8 Vue-компонентов >300 строк разделены (AppLayout R0.6 hard-стоп снят явным запросом заказчика) - O-refactor-06: rollup-plugin-visualizer + knip + cleanup ~165 строк dead-code в composables/ Acceptance закрыт за исключением 2 known DealsView/DealDetailDrawer (intentional Sprint 3 defineExpose-контракт). Регрессия: Pest 421 / Vitest 416 / Larastan 0 / vue-tsc 0 / ESLint 0 / build OK. Sprint 0 push выполнен (4f36bd3..8c6374d, 21 коммит). PAT Workflows R&W подтверждена через API-диагностику. CLAUDE.md/Pravila/Tooling/Plugin_stack_rules — параллельно обновляются другой сессией (PSR_v1 v1.4 формализация UPM/21st/R15 motion в локальных коммитах |
||
|
|
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> |
||
|
|
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> |
||
|
|
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:
|
||
|
|
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 (
|
||
|
|
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 (
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
830a652588 |
phase2(trash-bin): GET /api/deals?only_deleted + «Корзина» в DealsView
Расширяет stages 5/6 (soft-delete + 8-сек undo) до постоянного доступа
к удалённым сделкам через отдельный view-mode.
Backend (DealController::index):
- Новый query-param only_deleted=true.
- withTrashed() + whereNotNull('deleted_at') — обход global scope
SoftDeletes + явный фильтр для NO-OP idempotency.
- Все остальные фильтры применимы и в trash-mode.
Pest +3 (DealIndexTest):
- only_deleted=true → только soft-deleted (alive скрыты).
- Без only_deleted → soft-deleted скрыты (default behavior).
- RLS+app-фильтр изолирует чужие удалённые.
Frontend:
- ListDealsParams.onlyDeleted?: boolean + axios mapping.
- DealsView: trashMode ref + toggleTrashMode (clear selected + reload) +
applyBulkRestoreFromTrash (optimistic remove + bulkRestoreDeals + toast).
- UI changes в trash-mode:
- Заголовок «Сделки» → «Корзина».
- Toggle-btn 'mdi-arrow-left К сделкам' (warning-flat) вместо
'mdi-trash-can-outline Корзина' (outlined).
- Скрыты Экспорт + Новая сделка.
- Скрыт chiprow filter-bar.
- Info-alert «Корзина: показаны удалённые сделки».
- Bulk-bar: только Восстановить (mdi-restore success-tonal) + clear;
status/export/delete скрыты.
Vitest +2 (DealsListIntegration):
- toggleTrashMode → trashMode=true + listDeals с onlyDeleted=true.
- applyBulkRestoreFromTrash → bulkRestoreDeals + remove from state +
toast «Восстановлено 2».
PHPStan baseline регенерирован.
Регресс:
- Lint+type-check+format passed.
- Vitest 321/321 за 19.60 сек (+2 от 319).
- Vite build 1.04 сек.
- Pint + PHPStan passed.
- Pest 269/269 за 29.12 сек (+3 от 266, 1009 assertions).
Реестр v1.72→v1.73 / CLAUDE.md v1.63→v1.64.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
01c20e7b6c |
phase2(polling): usePolling composable 30 сек + Page Visibility pause
Закрывает последний unblocked production-TODO «Polling/SSE для real-time».
Manual reload-btn остаётся как fast-path; polling — фоновый автообновитель.
Composable (composables/usePolling.ts):
- usePolling(loader, {intervalMs=30_000, enabled=true}).
- Page Visibility API: при document.hidden=true interval останавливается;
при visibilitychange с возвратом hidden=false — restart + немедленный
loader() (не ждать следующего interval'а).
- Cleanup на onBeforeUnmount: clearInterval + removeEventListener.
- enabled=false — composable не стартует (feature-flag).
Integration:
- DealsView + KanbanView → loadDeals.
- AdminTenantsView → loadTenants.
- AdminBillingView → loadBilling.
- AdminIncidentsView → loadIncidents.
Vitest +6 (usePolling.spec.ts) с vi.useFakeTimers:
- Вызов каждые intervalMs / default 30 сек / skip при document.hidden /
cleanup на unmount / enabled=false → no-op / visibilitychange
pause+resume с немедленным loader.
Регресс:
- Lint+type-check+format passed.
- Vitest 319/319 за 18.67 сек (+6 от 313).
- Vite build 899 ms.
- Pint + PHPStan passed.
- Pest 266/266 за 28.62 сек (backend не тронут).
Реестр v1.71→v1.72 / CLAUDE.md v1.62→v1.63.
ВСЕ unblocked production-TODO закрыты.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
fa11c7b223 |
phase2(admin-tenants-mrr): mrr_rub в /api/admin/tenants (этап 7)
Закрывает gap из v1.66 — mock-форма имеет mrrRub, но API возвращал null. Теперь AdminTenantsView показывает реальную колонку MRR. Backend (AdminTenantsController::index): - Добавлено tariff_plans.price_monthly as tariff_price_monthly в select. - mrr_rub в response: price_monthly (string) если не-trial; иначе null. - Aggregate-формат как у /admin/billing — string чтобы decimal не терял точность при передаче через JSON. Pest +3 (AdminTenantsIndexTest): - mrr_rub='990.00' для активного тарифа не-trial. - mrr_rub=null для trial (даже если тариф есть). - mrr_rub=null если current_tariff_id отсутствует. Frontend: - ApiAdminTenant.mrr_rub: string | null в типе. - mapApiAdminTenant: parseFloat(api.mrr_rub) или null (вместо hardcoded null из v1.66). - AdminTenantsView: formatRub(item.mrrRub) для консистентности с другими ₽-полями. Vitest +2: - mrr_rub строка → number. - mrr_rub=null → mrrRub null. PHPStan baseline регенерирован. cspell-glossary +консистентности. Регресс: - Lint+type-check+format passed. - Vitest 313/313 за 18.83 сек (+2 от 311). - Vite build 947 ms. - Pint + PHPStan passed. - Pest 266/266 за 28.39 сек (+3 от 263, 1001 assertion). Реестр v1.70→v1.71 / CLAUDE.md v1.61→v1.62. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c34d4009d1 |
phase2(restore-flow): POST /api/deals/restore + undo-snackbar (этап 6 — completion of stage 5)
Soft-delete был half-done: пользователь не мог отменить случайное удаление.
Теперь после bulk-delete показывается snackbar «Удалено N · Восстановить»
на 8 секунд.
Backend (DealController::restore):
- POST /api/deals/restore {tenant_id, ids: [1..1000 ints]}.
- withTrashed() обходит global scope SoftDeletes + явный
whereNotNull('deleted_at') для NO-OP idempotency на живых.
- RLS + defense-in-depth where(tenant_id).
- ActivityLog event=deal.restored, context.source='bulk' для каждой
ВОССТАНОВЛЕННОЙ. Константа EVENT_DEAL_RESTORED добавлена в модель.
Pest +7 (DealRestoreTest):
- 422/404 базовые / soft-delete + restore + audit / NO-OP на живых
не пишет audit / defense-in-depth (свой restored, чужой остался) /
после restore видна в GET /api/deals / 422 пустой массив.
Frontend:
- dealsApi.bulkRestoreDeals — POST-helper.
- DealsView::applyBulkDelete: snapshot удалённых сделок (deep-clone
manager.*) сохраняется в lastDeletedSnapshot ref.
- undoBulkDelete() async: optimistic re-insert + bulkRestoreDeals если
auth.user; success → toast «Восстановлено N»; fail → warning.
- v-snackbar bulk-delete: 3→8 сек timeout + #actions слот с кнопкой
«Восстановить» (показ только при snapshot.length > 0). После undo
snapshot очищается → кнопка пропадает.
Vitest +3 (DealsListIntegration):
- bulk-delete + undo восстанавливает обе + bulkRestoreDeals + cleanup
snapshot.
- Undo без tenant_id — НЕ вызывает API + только локально.
- Undo reject → warning toast + локальное восстановление остаётся.
PHPStan baseline регенерирован. cspell-glossary +unshift +партиальный.
Регресс:
- Lint+type-check+format passed.
- Vitest 311/311 за 18.71 сек (+3 от 308).
- Vite build 877 ms.
- Pint + PHPStan passed.
- Pest 263/263 за 27.68 сек (+7 от 256, 998 assertions).
Реестр v1.69→v1.70 / CLAUDE.md v1.60→v1.61.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f0dce283a8 |
phase2(soft-delete): schema v8.9 + DELETE /api/deals (этап 5/5 — авто-план закрыт)
Bulk soft-delete для UI applyBulkDelete. Hard-delete отбракован из-за
CASCADE-FK от webhook_dedup_keys: hard уничтожил бы dedup-ключи и
нарушил идемпотентность webhook §5.5.
Schema v8.8 → v8.9:
- deals.deleted_at TIMESTAMPTZ (NULL = живая).
- Partial index (tenant_id, status) WHERE deleted_at IS NULL —
самый частый UI-фильтр.
- ALTER TABLE на партиционированной deals distributes во все 6
партиций автоматически (PG 14+).
- CHANGELOG +§U с обоснованием soft vs hard.
Backend (DealController::destroy):
- DELETE /api/deals {tenant_id, ids: [1..1000 ints]}.
- Bulk-update deleted_at=NOW() через RLS + defense-in-depth where(tenant_id).
- ActivityLog event=deal.deleted (source='bulk') для каждой ИЗМЕНЁННОЙ.
- NO-OP (уже удалена) не пишет audit.
- Deal model: SoftDeletes trait + deleted_at в fillable/casts. Global
scope автоматически добавляет whereNull('deleted_at') ко всем существующим
query (index/show/transition/update/export).
Pest +8 (DealDestroyTest):
- 422/404 базовые / soft-delete + audit / defense-in-depth (свой
удалён, чужой жив) / NO-OP idempotency / GET скрывает soft-deleted
(list+show 404) / 422 пустой массив.
- Quirk: migrate:fresh --env=testing без .env.testing использует liderra
вместо liderra_testing → решение DB_DATABASE=liderra_testing migrate:fresh.
Frontend:
- dealsApi.bulkDeleteDeals — DELETE-helper с config.data (axios особенность).
- DealsView::applyBulkDelete async: optimistic local-removal +
bulkDeleteDeals если auth.user; success → toast «Удалено N из M.»;
fail → warning toast + локальный update НЕ откатывается.
Vitest +3 (DealsListIntegration):
- bulkDeleteDeals с tenant_id + optimistic + toast.
- Без tenant_id — НЕ вызывается.
- Reject → warning toast + локальный update остаётся.
PHPStan baseline регенерирован.
АВТО-ПЛАН (5 этапов) ЗАКРЫТ ПОЛНОСТЬЮ.
Регресс:
- Lint+type-check+format passed.
- Vitest 308/308 за 20.12 сек (+3 от 305).
- Vite build 973 ms.
- Pint + PHPStan passed.
- Pest 256/256 за 27.75 сек (+8 от 248, 977 assertions).
Реестр v1.68→v1.69 / CLAUDE.md v1.59→v1.60.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
14dc317e2b |
phase2(admin-incidents): GET /api/admin/incidents + AdminIncidentsView API (этап 4/5)
Чтение incidents_log с фильтрами type/severity/unresolved_only + summary
(open/investigating/rkn_pending/total_unresolved).
Backend (AdminIncidentsController::index):
- ORDER BY started_at DESC. Filters: type, severity, unresolved_only=true.
- Derived: incident_id (INC-YYYY-MMDD-NNNN), status (resolved_at!=null →
resolved; detected_at!=null → investigating; иначе open),
affected_tenants_count из BIGINT[] (parsePgArray для '{1,2,3}'),
rkn_deadline_at = detected_at+24h для data_breach без notification.
- summary: open/investigating/rkn_pending/total_unresolved.
Pest +11 (AdminIncidentsIndexTest):
- пустой / incident_id формат / derive status / filter type+severity /
unresolved_only / ORDER BY started_at DESC / rkn_deadline +24h для
data_breach / non-data_breach без deadline / summary.rkn_pending /
limit+offset.
- Quirk: saas_admin_users.full_name (не first/last) + нет updated_at.
Frontend:
- api/admin.ts::listAdminIncidents — типизированный helper.
- AdminIncidentsView: унифицированный IncidentRow (mock-category ↔
API-type, mock-title ↔ API-summary). Reactive rowsState+stats default
= MOCK; loadIncidents() async на onMounted; fetchError + warning
alert + MOCK fallback; reload-btn. РКН pending chip учитывает оба
pdn_breach/data_breach.
Vitest +5:
- listAdminIncidents на mount / replace state+stats + rkn_deadline /
reject → fetchError+alert+fallback / reload-btn x2 / РКН pending chip
виден для data_breach без notification.
PHPStan baseline регенерирован. cspell-glossary +MMDD.
Регресс:
- Lint+type-check+format passed.
- Vitest 305/305 за 20.59 сек (+5 от 300).
- Vite build 1.05 сек.
- Pint + PHPStan passed.
- Pest 248/248 за 28.02 сек (+11 от 237, 951 assertion).
Реестр v1.67→v1.68 / CLAUDE.md v1.58→v1.59.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4532b95d64 |
phase2(admin-billing): GET /api/admin/billing + AdminBillingView API (этап 3/5)
Aggregates пополнений/списаний за текущий месяц по balance_transactions
+ summary с MRR/revenue/overdue/refunds_30d.
Backend (AdminBillingController::index):
- GET /api/admin/billing?search=. Per-tenant SUM с CASE WHEN type IN
('topup','lead_charge') GROUP BY tenant_id; ABS для charges.
- Row: id/subdomain/organization_name/contact_email/status/balance_rub/
tariff_id/tariff_name/mrr_rub (=tariff.price_monthly если не-trial)/
monthly_topups_rub/monthly_charges_rub/last_payment_at/
chargeback_unrecovered_rub.
- summary: total_mrr_rub (SUM не-trial), monthly_revenue_rub (SUM topup),
overdue_count (balance<0 || chargeback>0), refunds_count_30d.
- Quirk: schema-колонка tariff_plans.price_monthly (НЕ price_rub_monthly)
— обнаружено первым прогоном Pest, исправлено сразу.
Pest +9 (AdminBillingIndexTest):
- пустой / поля+tariff JOIN / aggregates за месяц / прошлый месяц не
попадает / overdue / refunds_30d (старые исключены) / total_mrr_rub
(trial исключаются) / search ILIKE / soft-deleted скрыт.
Frontend:
- api/admin.ts::listAdminBilling — типизированный helper.
- AdminBillingView: reactive rowsState+summary default = MOCK,
loadBilling() async на onMounted парсит API-строки → numbers + derive
status (suspended/balance<0||chargeback>0→overdue/active). На fail —
fetchError + warning alert + MOCK fallback. Reload-btn.
- tariffLabel/statusInfo обобщены с fallback'ами на новые slug'и.
Vitest +4:
- listAdminBilling на mount / replace rowsState+summary + string→number
+ status derive / reject → fetchError+alert+fallback / reload-btn x2.
PHPStan baseline регенерирован.
Регресс:
- Lint+type-check+format passed.
- Vitest 300/300 за 18.41 сек (+4 от 296).
- Vite build 925 ms.
- Pint + PHPStan passed.
- Pest 237/237 за 27.69 сек (+9 от 228, 926 assertions).
Реестр v1.66→v1.67 / CLAUDE.md v1.57→v1.58.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6ef9961f5f |
phase2(admin-tenants): GET /api/admin/tenants + AdminTenantsView API (этап 2/5)
AdminTenantsView переходит с mock-данных на live backend.
Backend (AdminTenantsController::index):
- GET /api/admin/tenants?status=&search=&limit=&offset=.
- LEFT JOIN tariff_plans для tariff_name. ORDER BY last_activity_at DESC.
- ILIKE search по organization_name + subdomain + contact_email.
- stats {total, active, trial, overdue} — overdue считает balance<0
ИЛИ chargeback_unrecovered_rub > 0.
- На MVP без auth (saas-admin SSO ⏸ Б-1).
Pest +8 (AdminTenantsIndexTest):
- 200 + пустой / все поля / status filter / search ILIKE /
ORDER BY last_activity_at DESC / stats / soft-deleted скрыт /
limit+offset.
Frontend:
- api/admin.ts::listAdminTenants — типизированный helper.
- composables/adminTenantsMapper.ts::mapApiAdminTenant — converter
API → UI: status derive (is_trial→trial, chargeback>0||balance<0
→overdue), inn='', code=subdomain, tariff clamp на known TenantTariff,
todayActual/mrrRub отсутствуют в API → 0/null, activitySince через
formatRelative.
- AdminTenantsView: reactive tenantsState+stats default = MOCK,
loadTenants() на onMounted → splice replace; на fail — fetchError +
warning alert + MOCK fallback. Reload-btn.
Vitest +13:
- View-integration (4): listAdminTenants на mount / replace state+stats /
reject → fetchError + alert + fallback / reload-btn x2.
- Mapper (9): name/code/inn/status-derives (trial/overdue/suspended) /
balance_rub→number / activitySince + null fallback.
PHPStan baseline регенерирован.
Регресс:
- Lint+type-check+format passed.
- Vitest 296/296 за 18.91 сек (+13 от 283).
- Vite build 1.02 сек.
- Pint + PHPStan passed.
- Pest 228/228 за 25.22 сек (+8 от 220, 906 assertions).
Реестр v1.65→v1.66 / CLAUDE.md v1.56→v1.57.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
7e1bf8b42d |
phase2(deal-patch): PATCH /api/deals/{id} + comment-editor в DealDetailDrawer (этап 1/5)
Drawer из read-only становится editable. ActivityLog event пишется на
каждое изменение поля.
Backend (DealController::update):
- PATCH /api/deals/{id} {tenant_id, comment?, manager_id?, status?}.
- Каждое изменённое поле → ActivityLog:
comment → deal.commented (context.text);
manager_id → deal.assigned (context.from/to + assigned_at=NOW);
status → deal.status_changed (context.from/to/source='manual').
- NO-OP не пишется в audit. Manager FK guard + status slug validation.
- RLS + defense-in-depth where(tenant_id) → 404 для чужой сделки.
Pest +10 (DealUpdateTest):
- 422/404 базовые / 404 чужая сделка / comment+audit / manager+audit+
assigned_at / status+audit / 422 неизвестный slug / 422 чужой manager /
NO-OP не пишет / комбинированно → 2 audit записи.
Frontend:
- api/deals.ts::updateDeal — PATCH helper c ensureCsrfCookie.
- DealDetailDrawer: новая секция «Комментарий» (только при tenantId).
v-textarea auto-grow + counter=5000 + Save-btn → updateDeal →
toast success + reload events (новый deal.commented в timeline).
На fail → warning toast.
Vitest +3 (DealDetailDrawerApi):
- saveComment вызывает updateDeal + toast + reload events (getDeal x2).
- saveComment reject → commentSaveError + warning toast.
- comment-section не рендерится без tenantId.
PHPStan baseline регенерирован.
Регресс:
- Lint+type-check+format passed.
- Vitest 283/283 за 18.13 сек (+3 от 280).
- Vite build 1.12 сек.
- Pint + PHPStan passed.
- Pest 220/220 за 25.64 сек (+10 от 210, 871 assertion).
Реестр v1.64→v1.65 / CLAUDE.md v1.55→v1.56.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e31ea5354a |
phase2(lead-statuses): GET /api/lead-statuses + Pinia-store с snapshot fallback
Заменяет static-снапшот LEAD_STATUSES в коде на live-данные из БД. Custom slug'и (добавленные после deployment'а) теперь видны UI без rebuild'а. Backend: - LeadStatus model (PK=slug string, incrementing=false, timestamps=null). - LeadStatusController::index — GET /api/lead-statuses, ORDER BY sort_order, slug. Таблица глобальная (не tenant-aware), auth не требуется на MVP. Pest +5 (LeadStatusesIndexTest): - 200 + не пустой / все 14 системных slug'ов из seed / все нужные поля / sort_order ASC / кастомный slug после INSERT появляется в endpoint'е. Frontend: - api/leadStatuses.ts::listLeadStatuses — GET helper. - stores/leadStatuses.ts::useLeadStatusesStore — Pinia setup-store: statuses default = LEAD_STATUSES snapshot (UI работает без fetch'а), load(force=false) идемпотентен, bySlug computed Map, findBySlug helper. На fail — snapshot остаётся, fetchError=true. - DealsView/KanbanView/DealDetailDrawer переехали со static-импорта LEAD_STATUSES на store. KanbanView использует safe-access dealsByStatus[slug] || [] (защита от custom slug'а из API без seeded column). load() в onMounted у обоих view'ов. Vitest +7 (leadStatusesStore.spec.ts): - initial snapshot / findBySlug existing & null / load success replace + loaded / load reject — fetchError + snapshot fallback / load идемпотентен / load(force=true) refetch. - 2 spec'а DealDetailDrawer получили setActivePinia(createPinia()) в beforeEach (без этого Pinia store-injection в jsdom падает). PHPStan baseline регенерирован. Регресс: - Lint+type-check+format passed. - Vitest 280/280 за 19.44 сек (+7 от 273). - Vite build 1.17 сек. - Pint + PHPStan passed. - Pest 210/210 за 24.59 сек (+5 от 205, 840 assertions). Реестр v1.63→v1.64 / CLAUDE.md v1.54→v1.55. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cba76c5d18 |
phase2(deal-show): GET /api/deals/{id} + DealDetailDrawer на реальный ActivityLog
Закрывает gap «timeline в drawer'е показывает hard-coded MOCK_EVENTS» —
теперь drawer fetch'ит реальные activity-events на open из tenant-filtered
activity_log. Без tenant_id — fallback на MOCK_EVENTS как раньше.
Backend (DealController::show):
- GET /api/deals/{id}?tenant_id={id} — возвращает {deal, events}.
- Deal extended (project_name, manager_name/initials, comment, assigned_at).
- Events — последние 50 записей activity_log по (tenant_id, deal_id)
ORDER BY created_at DESC, с актором (user через belongsTo).
- RLS-обёртка + defense-in-depth where(tenant_id) — 404 если чужая.
Pest +8 (DealShowTest):
- 422/404 базовые / 404 чужая сделка / deal-relations / events ORDER BY +
actor + actor=null для system-event / RLS+app-фильтр изоляция событий /
лимит 50 событий.
Frontend:
- api/deals.ts::getDeal — типизированный helper c ApiDealEvent/Detail/Response.
- composables/dealsApiMapper.ts::mapApiDealEvent — converter ApiDealEvent →
DealEvent: clamp event-slug на known types с fallback на 'deal.viewed';
detail зависит от type (status_changed: 'from → to'; created: source;
остальные: JSON-сводка context).
- DealDetailDrawer: optional tenantId prop, watch([open, deal.id, tenantId])
с immediate=true → loadEvents() на open. Reject → eventsFetchError +
v-alert warning + MOCK_EVENTS fallback.
- DealsView/KanbanView передают :tenant-id="auth.user?.tenant_id".
Vitest +4 (DealDetailDrawerApi.spec.ts):
- Без tenantId — getDeal не вызывается + MOCK_EVENTS видны.
- С tenantId — getDeal + events замещены + 'new → paid' виден.
- reject → fetchError + alert + MOCK_EVENTS fallback.
- open=false → getDeal не вызывается.
PHPStan baseline регенерирован.
Регресс:
- Lint+type-check+format passed.
- Vitest 273/273 за 20.76 сек (+4 от 269).
- Vite build 1.12 сек.
- Pint + PHPStan passed.
- Pest 205/205 за 24.19 сек (+8 от 197, 812 assertions).
Реестр v1.62→v1.63 / CLAUDE.md v1.53→v1.54.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a1ea003642 |
phase2(xlsx-export): PhpSpreadsheet 5.7 + format=csv|xlsx на /api/deals/export
Закрыт TODO «реальный XLSX-export» из v1.51. Russian users prefer .xlsx
(1С/Excel) — заменяет CSV как default. CSV остаётся через format=csv.
Backend (DealController::export):
- Body теперь: {tenant_id, ids, format?: 'csv' | 'xlsx'}; default 'csv'.
- buildXlsx: Spreadsheet + setTitle 'Сделки' + setCellValue A1..G1
headers + bold(A1:G1) + setAutoSize всех колонок A..G. Writer пишет
через ob_start/php://output для возврата бинарной строки.
- Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+ Content-Disposition с .xlsx.
Quirk: PhpSpreadsheet 5.x удалил deprecated setCellValueByColumnAndRow —
мигрировал на A1-нотацию (setCellValue('A2', $val)).
Pest +4 (DealCreateTest):
- xlsx binary с Content-Type + magic bytes "PK\x03\x04" + size >2KB.
- IOFactory::createReader('Xlsx') распаковывает: sheet «Сделки» +
A1='ID' bold + A2/B2/C2 — реальные данные сделки.
- 422 на неизвестный format.
- Default (без format) — backward-compat CSV.
Frontend:
- api/deals.ts разделён: exportDeals (CSV string) + exportDealsXlsx
(Blob, responseType='blob').
- applyBulkExport(format='xlsx' | 'csv') в DealsView — default 'xlsx'.
XLSX → triggerBlobDownload (новый helper). CSV → старый CSV-helper.
На fail — fallback на local CSV.
Vitest +3 (DealsListIntegration):
- xlsx default → exportDealsXlsx + Blob download + toast «XLSX».
- 'csv' → exportDeals + toast «CSV».
- xlsx reject → fallback на local CSV + toast «Backend недоступен».
PHPStan baseline регенерирован (удалена unmatched ignore-запись для
setCellValueByColumnAndRow). cspell-glossary +дефолтит +vnd +spreadsheetml.
Регресс:
- Lint+type-check+format passed.
- Vitest 269/269 за 18.49 сек (+3 от 266).
- Vite build 982 ms.
- Pint + PHPStan passed.
- Pest 197/197 за 26.05 сек (+4 от 193, 784 assertions).
Реестр v1.61→v1.62 / CLAUDE.md v1.52→v1.53.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ac186593f2 |
phase2(bulk-transition+reload): POST /api/deals/transition + reload-btn
Закрывает gap «UI меняет статус, но изменения не сохраняются на backend»
из v1.51. Reload-btn заменяет polling/SSE до прихода long-poll'а на prod.
Backend (DealController::transition):
- POST /api/deals/transition {tenant_id, ids: [1..1000 ints], status}.
- Валидация status — exists в lead_statuses (глобальная таблица).
- RLS-обёртка SET LOCAL + defense-in-depth where(tenant_id) для
partial-update: чужие id остаются в исходном статусе.
- ActivityLog event=deal.status_changed с context={from, to, source: 'bulk'}
для каждой ИЗМЕНЁННОЙ сделки. NO-OP (старый==новый) не пишется в audit.
- Ответ: {updated, requested, status}.
Pest +7 (DealTransitionTest):
- 422 missing fields / 404 unknown tenant / 422 неизвестный slug + не апдейт /
batch update 3 сделок + 3 ActivityLog с правильным context /
NO-OP не пишет ActivityLog / defense-in-depth (2 tenant'а — обновляется
только свой) / 422 пустой массив ids.
Frontend:
- dealsApi.transitionDeals — типизированный helper с ensureCsrfCookie.
- applyBulkStatus в DealsView переписан async: optimistic local-update +
backend-вызов если auth.user.tenant_id. На success — toast «Обновлено
N из M.», на fail — warning toast + локальный update НЕ откатывается.
Без auth.user — только optimistic (legacy local-mode сохранён).
- reload-btn в DealsView и KanbanView — outlined «Обновить» mdi-refresh,
привязан к loadDeals. В DealsView :loading="loading" во время fetch'а.
Vitest +5:
- reload-btn (Deals + Kanban) — listDeals вызывается дважды.
- applyBulkStatus с tenant_id — transitionDeals + optimistic + toast.
- applyBulkStatus без tenant_id — НЕ вызывается transitionDeals.
- applyBulkStatus reject — toast warning + локальный update остаётся.
PHPStan baseline регенерирован. cspell-glossary +апдейт*.
Регресс:
- Lint+type-check+format passed.
- Vitest 266/266 за 18.16 сек (+5 от 261).
- Vite build 1.06 сек.
- Pint + PHPStan passed.
- Pest 193/193 за 23.27 сек (+7 от 186, 767 assertions).
Реестр v1.60→v1.61 / CLAUDE.md v1.51→v1.52.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
339e8ea53b |
phase2(deals-list-api): GET /api/deals + замена MOCK_DEALS на fetch с fallback
Закрыт TODO (c) из v1.50: backend-эндпоинт для списка сделок и его
интеграция в DealsView/KanbanView вместо статичного MOCK_DEALS.
Backend (DealController::index):
- Query-params: tenant_id (required, 422/404), status_in[] (whereIn),
project_id, manager_id, search (ILIKE по phone+contact_name),
limit clamp [1..500] default 100, offset default 0.
- ORDER BY received_at DESC, id DESC. Eager-load project + manager.
- RLS-обёртка SET LOCAL app.current_tenant_id + defense-in-depth
where(tenant_id) — на тестах через postgres superuser RLS обходится
BYPASSRLS, app-фильтр гарантирует изоляцию.
- Ответ: {deals: [...], total, limit, offset}; manager_name/initials
форматируются через ManagerController::formatName/formatInitials.
Pest +12 (DealIndexTest):
- 422/404, пустой список, relations (project_name+manager_name+initials),
RLS-изоляция, ORDER BY, status_in[], project_id, manager_id, search
ILIKE, limit+offset, manager=null edge case.
Frontend:
- api/deals.ts::listDeals — типизированный helper c ApiDeal/ListDeals*.
- composables/dealsApiMapper.ts::mapApiDeal — converter ApiDeal→MockDeal:
contact_name fallback на phone, manager.name='Не назначен' /
initials='—' при null, project='—' при null, cost=0,
receivedMinutesAgo=max(0, …) от clock-skew.
- DealsView/KanbanView: onMounted(loadDeals) async-вызывает listDeals
если auth.user.tenant_id, на success replace через splice, на fail
fetchError=true + v-alert warning, MOCK_DEALS как fallback.
Vitest +14:
- dealsApiMapper.spec.ts (8): 1:1, fallback'и, edge cases.
- DealsListIntegration.spec.ts (6): без tenant_id — НЕ вызывает API,
с tenant_id — replace state, reject → fetchError + alert + fallback;
для DealsView и KanbanView.
PHPStan baseline регенерирован. cspell-glossary +ILIKE +DTO.
Регресс:
- Lint+type-check+format passed.
- Vitest 261/261 за 19.62 сек (+14 от 247).
- Vite build 989 ms.
- Pint + PHPStan passed.
- Pest 186/186 за 22 сек (+12 от 174, 742 assertions).
Реестр v1.59→v1.60 / CLAUDE.md v1.50→v1.51.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
eb8ca65c5d |
phase2(supplier-resolver): вынос дублированной логики lookup в App\Services\SupplierResolver
Закрыт TODO (a) из v1.49: общая логика выбора активного supplier'а через
project_suppliers m2m была дублирована между ProcessWebhookJob (webhook-flow)
и DealController (manual-create) — 11 одинаковых строк query-builder'а на
2 файла. Теперь — единственный источник истины + DI через app() (тот же
паттерн, что у DuplicateDetector в v1.23).
App\Services\SupplierResolver:
- resolveForProject(Project): ?int — точная копия прежней query
(project_suppliers JOIN suppliers, is_active+is_active, ORDER BY
sort_order, id).
- costRubSnapshot(int $supplierId): string — вынесенный snapshot цены
для записи в supplier_lead_costs.
ProcessWebhookJob и DealController:
- Удалены private resolveSupplierId() (по 14 строк).
- Удалены локальные DB::table('suppliers')->value('cost_rub').
- Используют app(SupplierResolver::class) внутри handle()/store().
Pest +8 в tests/Feature/Services/SupplierResolverTest.php:
- null без связей / единственный активный / пропуск inactive supplier /
пропуск inactive m2m / ORDER BY sort_order / null если все inactive /
изоляция по project_id / costRubSnapshot формат '137.50'.
PHPStan baseline регенерирован.
Регресс:
- Pint + PHPStan passed (baseline регенерирован).
- Pest 174/174 за 21.46 сек (+8 от 166, 708 assertions).
- Vitest 247/247 за 17.53 сек (нетронут — backend-only refactor).
Реестр v1.58→v1.59 / CLAUDE.md v1.49→v1.50.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
515114cff5 |
phase2(lookups+integrity): GET /api/managers+projects + manager FK guard + SupplierLeadCost для manual
3 интеграционных доработки после backend-completion v1.57.
(1) GET /api/managers + /api/projects + manager FK guard:
- ManagerController::index — active users тенанта (is_active+deleted_at IS NULL).
Формат {id, email, first_name, last_name, name, initials} с
formatName/formatInitials helpers (fallback на email).
- ProjectController::index — active projects (is_active=true).
- Оба endpoint'а: tenant_id query-param, 422 без, 404 unknown, RLS-обёртка.
- DealController::store FK guard: manager_id должен принадлежать tenant'у +
is_active. Иначе 422 (закрывает security-gap чужого менеджера).
- Pest +8 в LookupsTest.
(2) Replace MOCK_MANAGERS / MOCK_PROJECTS на API в NewDealDialog:
- projectOptions/managerOptions ref'ы с MOCK fallback.
- loadLookups через Promise.all([listProjects, listManagers]) на open
диалога с tenantId.
- managerIdByName Map name→id для submit'а.
- Silent fallback на mock при network-error.
- Vitest +2.
(3) SupplierLeadCost для manual-leads:
- В DealController::store после Deal::create — resolveSupplierId (копия
логики ProcessWebhookJob: project_suppliers JOIN suppliers + ORDER BY
sort_order). Если supplier найден — SupplierLeadCost с snapshot cost_rub
+ supplier_lead_id=NULL (manual: нет внешнего id).
- Manual по-прежнему НЕ списывает баланс (Ю-2 reseller-модель — charge
только при webhook'е); cost-аналитика всё равно нужна.
- Pest +2.
- TODO: рефактор resolveSupplierId в App\Services\SupplierResolver чтобы
Job + Controller разделяли логику.
Старый тест manager_id=42 переписан под FK guard через User::factory.
PHPStan baseline регенерирован (+28 ignored Pest TestCall warnings).
Регресс: lint+type-check+format ✅; vitest 247/247 за 16.32 сек (+2);
vite build 951 ms; Pint+PHPStan passed; Pest 166/166 за 22.11 сек
(+10 от 156, 699 assertions). Реестр v1.57→v1.58, CLAUDE.md v1.48→v1.49.
Production TODO остаточные:
- resolveSupplierId → SupplierResolver service.
- XLSX-export через PhpSpreadsheet.
- GET /api/deals для replace MOCK_DEALS в DealsView/KanbanView.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
83bb9de2bb |
phase2(backend-completion): POST /api/deals + webhook_hmac_required + POST /api/deals/export
3 backend-completion после tightening v1.56.
(1) POST /api/deals — manual create endpoint:
- DealController::store. Project firstOrCreate (type='manual'). Deal с
source_crm_id=NULL. RLS-обёрнутая транзакция.
- Manual НЕ списывает баланс / НЕ дедуп / НЕ SupplierLeadCost.
ActivityLog с context.source=manual.
- NewDealDialog получил optional tenantId prop. С tenantId — POST → backend-id;
на error fallback на local-id + warning + dialog open.
- DealsView/KanbanView передают auth.user?.tenant_id.
- Pest +8.
(2) webhook_hmac_required flag в system_settings:
- Seed-row в db/schema.sql (default false backward-compat).
- WebhookReceiveController::isHmacRequired private helper.
- При true: запрос без X-Webhook-Signature → 401.
- Pest +3.
(3) POST /api/deals/export — backend CSV:
- DealController::export. Валидация ids[1-10000]. RLS-обёрнутый whereIn.
- Excel-friendly CSV: BOM "\u{FEFF}" PHP-литерал, ; разделитель, \r\n.
- text/csv attachment headers.
- Frontend applyBulkExport: backend → fallback на client-side
(buildLocalCsv вынесен).
- Pest +4.
Vitest +3 (всего 245/245).
PHPStan убрал лишнюю Deal->id===null проверку (Eloquent int).
DealsView/KanbanView spec'ы получили setActivePinia.
Регресс: lint+type-check+format ✅; vitest 245/245 за 17.07 сек (+3);
vite build 1.04 сек; Pint+PHPStan passed; Pest 156/156 за 20.27 сек
(+15 от 141, 675 assertions). Реестр v1.56→v1.57, CLAUDE.md v1.47→v1.48.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4a385b1df7 |
phase2(prod-tightening): HMAC+rate-limit webhook / fetch system_settings / CSV export
3 production-tightening после 7-фичного пакета v1.55. (1) HMAC + per-token rate-limit для webhook receive endpoint: - WebhookReceiveController::receive: tenant lookup → rate-limit → HMAC → payload validation. - HMAC: опциональный X-Webhook-Signature: sha256=<hex> через hash_hmac + hash_equals (constant-time). Backward-compat: header missing → 202. - Per-token rate-limit: RateLimiter с decay 60 сек. Лимит из system_settings.webhook_rate_limit_rps × 60. На превышении 429 + Retry-After. Hit ставится ДО валидации payload — иначе обходимо 422. - Pest +5: HMAC valid/invalid 401/missing 202; rate-limit 60+1=429; ключ изолирован per-token. (2) Реальный fetch system_settings в AdminSystemView: - onMounted → adminApi.listSystemSettings() → splice replace. - На fetch-error → fallback на mock + warning v-alert. - Кнопка «Обновить» — ручной reload. - Vitest +3: mount fetch / reload / error fallback. (3) Реальный CSV-export для bulk-actions DealsView: - applyBulkExport → CSV через Blob+a[download]. - 8 колонок, ; разделитель, \r\n, BOM через String.fromCharCode(0xFEFF) (литеральный U+FEFF блокируется ESLint no-irregular-whitespace). - Filename deals_export_YYYY-MM-DD.csv. - Empty selection → toast без download. - Vitest +2: spy createObjectURL+anchor.click; empty без blob. PHPStan baseline регенерирован. Регресс: lint+type-check+format ✅; vitest 242/242 за 15.82 сек (+4); vite build 903 ms; Pint+PHPStan passed; Pest 141/141 за 17.8 сек (+5, 627 assertions). Реестр v1.55→v1.56, CLAUDE.md v1.46→v1.47. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
768628d914 |
phase2(7-features): bulk-actions / new-deal / tenant-card / system-edit / webhook / smart-filters / impersonation-list
7-фичный auto-mode пакет согласно «карте что осталось» (после v1.54).
(1) Bulk-actions DealsView:
- dealsState reactive-копия MOCK_DEALS (deep-clone) для безопасного bulk-edit.
- Bulk-bar (sticky, теало-нуар, theme=dark) при selected.length > 0:
count + Сменить статус (v-menu × 14 lead_statuses) + Экспорт (snackbar) +
Удалить (v-dialog confirm) + ✕ clear.
- На production: smart status-transition с проверкой allowed-переходов;
soft-delete (архив 30 дней); реальный CSV/XLSX export через xlsx-lib.
(2) NewDealDialog (used in DealsView+KanbanView):
- 6 полей: name/phone/project (MOCK_PROJECTS) / manager (MOCK_MANAGERS) /
cost / status (default 'new' или presetStatus). Phone-валидация ≥10 цифр.
- emit('created', deal) → DealsView push в начало dealsState; KanbanView push
в правильную колонку по statusSlug + totalDeals++.
(3) AdminTenantDetailView (/admin/tenants/:code):
- 4 KPI cards (Баланс/runway / Тариф+MRR/мес / Лиды сегодня+неделя+месяц /
Средняя цена). 4 v-tabs: Финансы (balance-history) / Пользователи /
Проекты / Активность с event-кодами.
- Кнопка «Войти как клиент» (использует ImpersonationDialog из v1.54).
404-fallback. composables/mockTenantDetail.ts с expandTenantDetail.
- AdminTenantsView получил @click:row → router.push.
(4) Edit-flow AdminSystemView (audit-log + 2-step):
- Backend: SystemSetting + SaasAdminAuditLog Eloquent (append-only,
payload_before/after JSONB casts).
- AdminSystemSettingsController с GET (list) + PUT (update в DB::transaction
+ INSERT в saas_admin_audit_log; hash-chain trigger BEFORE INSERT
заполняет log_hash).
- Type-validation: int/decimal/bool/json. Reason ≥30 chars. No-op → 422.
- Frontend SystemSettingEditDialog — 3-step (edit → confirm с diff
before/after → done).
(5) Webhook receive endpoint (POST /api/webhook/{token}):
- WebhookReceiveController::receive. Token = tenants.webhook_token.
- 404 unknown / 422 bad payload / 202 success + dispatch ProcessWebhookJob.
- Stub-INSERT в webhook_log через DB::table обёрнут в DB::transaction +
SET LOCAL app.current_tenant_id для RLS.
- CSRF-исключение для api/webhook/* в bootstrap/app.php.
- На prod: + HMAC X-Webhook-Signature + per-token rate-limit.
(6) Smart-filters:
- DealsView: multi-select v-select Проект+Менеджер с auto availableProjects/
availableManagers computed.
- AdminTenantsView: filterStatuses (4 STATUS_OPTIONS) + filterTariffs
(computed availableTariffs).
- Кнопка «Сбросить» появляется только когда фильтры активны.
(7) AdminImpersonationView (/admin/impersonation):
- Backend +2 GET endpoints: /active (used_at != null AND session_ended_at
== null) + /recent (last 20 завершённых с duration_seconds через
abs(diffInSeconds) — Carbon signed по умолчанию).
- ImpersonationToken получил belongsTo(Tenant).
- Frontend view: 2 секции (Активные с end-кнопкой / Недавно завершённые
read-only) + refresh + onMounted load.
- Маршрут /admin/impersonation + 5-й nav-пункт «Impersonation» в AdminLayout.
Vitest +48 (всего 238/238 за 15.31 сек).
Pest +16 (всего 136/136 за 15.8 сек, 495 assertions).
PHPStan baseline регенерирован (0 errors после фикса nullsafe.neverNull).
Регресс: lint+type-check+format ✅; vite build 937 ms; Pint+PHPStan passed;
Pest 136/136. Реестр v1.54→v1.55, CLAUDE.md v1.45→v1.46.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
61afa72591 |
phase2(impersonation-ui): UI dialog для Ю-1 в AdminTenantsView (frontend)
Закрывает TODO из v1.44 — frontend для Impersonation backend (`1963694`).
api/admin.ts:
- impersonationInit/Verify/End — типизированные axios-helpers для трёх
endpoint из v1.53. Все три — ensureCsrfCookie + apiClient.post.
На prod автоматически перейдут под middleware('auth:saas-admin').
components/admin/ImpersonationDialog.vue — 4-step state-machine:
- step 1 «reason»: v-textarea ≥30 chars + counter + hint «Ещё N символов».
- step 2 «verify»: info-alert email клиента + 6-digit input
(autocomplete=one-time-code) + dev-banner с _dev_plain_code.
- step 3 «active»: success-alert + кнопка «Завершить сессию».
- step 4 «done»: финальный success.
- persistent dialog (нельзя закрыть кликом за пределами — audit trail).
- watch(modelValue) сбрасывает state при каждом открытии.
AdminTenantsView:
- 8-я колонка actions (width=56) с v-tooltip + icon-btn mdi-account-switch.
- :disabled на suspended (по ТЗ §22.7 — только активные tenant'ы).
- @click.stop, data-testid=impersonate-btn-{id}.
- ADMIN_USER_ID=1 заглушка (на prod удалится — backend возьмёт из auth).
Vitest +11 (всего 190/190 за 13.23 сек):
- ImpersonationDialog.spec.ts (7): hide когда modelValue=false; step-1 mount;
reason<30 показывает counter; init→step2 (email+dev-banner); verify→step3
(end-btn); 5-digit code не вызывает API; end→step4; Cancel emit.
- AdminTenantsView.spec.ts (+4): impersonate-btn в каждой строке; suspended
disabled; click открывает диалог с правильным tenant; props.requestedBy=1.
Vitest quirk: v-dialog/v-tooltip требуют layout-injection — stub'ы
VDialog как passthrough <div v-if="modelValue"><slot/></div>, VTooltip как
<div><slot name="activator" :props="{}"/></div>. ImpersonationDialog
stub'ится в AdminTenantsView spec. api/admin + helpers extractValidationErrors/
extractErrorMessage мокаются через vi.mock — axios.isAxiosError(plain Error)
в jsdom возвращает false (паттерн из auth-store.spec.ts).
Production TODO: SaaS-admin auth (Yandex 360 SSO, Б-1) → middleware,
two-person approval (CTO-15/Ю-9), MailService → _dev_plain_code исчезает,
live cookie-swap session, страница «Активные impersonation-сессии».
Регресс: lint+type-check+format+build OK (924 ms; AdminTenantsView lazy-chunk
20.68 KB включает inline ImpersonationDialog); Vitest 190/190 за 13.23 сек;
Pest 120/120 за 15.69 сек (нетронут). Реестр v1.53→v1.54, CLAUDE.md v1.44→v1.45.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1963694778 |
phase2(impersonation): backend Ю-1 (init-token + verify + end)
- ImpersonationToken Eloquent (UPDATED_AT=null) + isExpired/isUsable helpers
- ImpersonationController 3 endpoints: init / verify / end
- init: reason ≥30 chars, 6-значный random_int код, bcrypt-hash, TTL 15 мин
- verify: Hash::check; failed_attempts+=1; ≥5 → invalidated_at
- end: session_ended_at; 422 если не было verify
- _dev_plain_code в response (prod заменит email клиенту)
- Routes /api/admin/impersonation/{init,verify,end} (без middleware на MVP)
- TODO: saas-admin auth (Yandex 360 SSO) + two-person approval (CTO-15/Ю-9) + email клиенту + UI dialog в AdminTenantsView
- Pest +9 ImpersonationTest (120/120 за 15.62с, 443 assertions)
- Регресс: lint+type+format OK; build 846ms; Pint+Stan passed
- CLAUDE.md v1.43→v1.44, реестр v1.52→v1.53
- Все 9 пунктов списка v1.46 закрыты (кроме #6 SSO ⏸ Б-1 и #7 browser-mode отложен инфра)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f65b2ca8d8 |
phase2(admin-views): AdminBilling/Incidents/System — реальные display-views
- AdminBillingView: 4 stats (MRR, Выручка, Просрочка, Возвраты) + v-data-table 7 колонок (Тенант с ИНН / Тариф / Баланс с error-color / пополнения / списания / MRR / Статус-chip) + поиск
- AdminIncidentsView: 3 stats + 5 фильтров статуса + v-list с incident_id (INC-YYYY-MMDD-NNNN) + severity/status/РКН-pending chips + дедлайн 24ч по 152-ФЗ
- AdminSystemView: read-only warning + поиск + v-list 7 system_settings (webhook_rate_limit, login_max_attempts, retention и т.д.) с type-chip и updated_at
- composables/mockAdmin.ts: AdminBillingTenantRow + AdminIncidentRow + AdminSystemSetting + mock-данные
- Router: /admin/{billing,incidents,system} → реальные views (не placeholder)
- Vitest +13 (179/179 за 11.98с)
- TODO: edit-flow для system_settings + backend /api/admin/* endpoints
- Регресс: lint+type+format OK; build 743ms; story:build 21/28 за 31.5с
- CLAUDE.md v1.42→v1.43, реестр v1.51→v1.52
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
2e91469a07 |
phase2(suspicious-login): email при 3 неудачах login (ТЗ §22.4.4 п.3)
- App\Mail\SuspiciousLoginNotification Mailable + emails.suspicious_login Blade - maybeNotifySuspiciousLogin срабатывает ровно при count==3 (защита от спама на 4-5) - Для unknown email — skip (некому) - На dev MAIL_MAILER=log → письмо в storage/logs ТЗ §22.4.4 анти-брутфорс закрыт полностью: - email-rate-limit 5/15мин (v1.36) - IP-lockout 10/час (v1.41) - email-notify при 3 неудачах (this commit) - Pest +4 SuspiciousLoginNotificationTest (111/111 за 14.32с, 401 assertions) - Регресс: Pint+Stan passed - CLAUDE.md v1.41→v1.42, реестр v1.50→v1.51 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d1e4237e2f |
phase2(ip-lockout): auth_log записи + 10 неудач/час с IP → 429 (ТЗ §22.4.4 п.2)
- AuthController::isIpLockedOut: count login_failed за час с IP, ≥10 → 429 + Retry-After: 3600
- logAuthEvent: 3 ветки failure_reason (invalid_password / unknown_email / account_locked)
- DB::table('auth_log')->insert; hash-chain trigger заполняет log_hash (OPEN-И-15)
- Защита поверх email-rate-limit: один IP не сможет перебирать множество email'ов
- Pest +6 IpLockoutTest (107/107 за 13.86с, 380 assertions)
- Регресс: Pint+Stan passed
- CLAUDE.md v1.40→v1.41, реестр v1.49→v1.50
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|