30eec9fb7dd8fa281c8dff68dfd5b700f2d80aff
111 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3f91afd8d7 |
docs(adr): CLAUDE.md v2.3 — register #36-38 architecture-tooling (Task 7)
§3 title 35->38; §1 priority-chain row 2b 35->38; §3.3 +3 rows (#36 adr-kit, #37 mermaid-skill, #38 architecture-patterns); §3.3 footer count 35->38, architecture-tooling as the fifth off-phase subcategory; §0 cross-refs Pravila v1.16->v1.17 / PSR_v1 v3.2->v3.3 / Tooling v2.2->v2.3; §6 +2026-05-17 integration paragraph; header v2.2->v2.3. Via /claude-md-management:claude-md-improver (CLAUDE.md §5 п.10). CHANGELOG_claude_md.md not touched — v2.1/v2.2/v2.3 are inline-only in §9 (CHANGELOG maintenance has been inline since v2.0). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d6f0ff868f |
docs(rules): CLAUDE.md v2.2 — §5 п.10 убран ruflo-routing loophole (Task 4 review fixup)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9929b4a599 |
docs(rules): CLAUDE.md v2.2 — реколлаж ruflo, убран уровень −1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
fff25605d0 | fix(claude_md): §1 two explicit hard-rules + §0 v1.15 cell relabel (review) | ||
|
|
2722f60420 | docs(claude_md): §14 queen-trigger refs — §1/§3.5/§0 (v2.1) | ||
|
|
5ac2961698 |
feat(ruflo): activate runtime — daemon-as-service + hive-mind + real embeddings
Полная активация ruflo runtime (заказчик: «Активировать ruflo runtime» → «Полная (daemon-as-service + hive-mind spawn)»). Закрывает paper/runtime gap из Phase 3-4 нормативной инверсии. Что активировано: - ruflo установлен глобально (npm i -g ruflo — стабильное дерево вместо ephemeral npx-cache; решает module-resolution для embeddings) - Daemon ACTIVE под PM2 (ruflo-daemon, 5 workers); reboot-survival через Windows Task Scheduler PM2-ruflo-daemon (pm2 resurrect onlogon — ruflo native install-supervisor только launchd/systemd, pm2-windows-service deprecated+broken non-interactive → Task Scheduler fallback) - Hive-mind ACTIVE — Queen-led (hierarchical-mesh/byzantine) + 9 worker-агентов - Memory init — sql.js .swarm/memory.db + реальные embeddings Xenova/all-MiniLM-L6-v2 384-dim (sharp/libvips fix: @xenova/transformers hard-deps sharp, prebuilt libvips timeout'ит — curl tarball в npm-cache/_libvips) Repo-изменения: - .gitignore +.swarm/ +ruvector.db (ruflo runtime state, не tracking) - CLAUDE.md §3.5 + §6 «Runtime state» — paper-level → runtime active - Tooling §4.10 «Runtime state» — аналогичный sync Verification: - Phase A gate: Pest 742/739/3sk/0 + Vitest 92f/774/3sk/0 + vue-tsc 0 ✓ - ruflo doctor: 10 passed / 7 warnings (alpha/optional) - Pest --parallel post-activation: 0 регрессий от ruflo. 1 intermittent error классифицирован pest-parallel-debugger агентом (11 runs) как quirk 72 — ruflo grep-подтверждённо не трогает Redis :6379, worker-jitter лишь усиливает частоту pre-existing flake (suite green 739/739/0/3 5× verified) Известные alpha-bugs (документированы): - ruflo memory store CLI не персистит между invocations (in-memory sql.js) - daemon worker-jitter усиливает Pest quirk 72 — пауза pm2 stop ruflo-daemon на baseline regression - $-расход near-zero: ruflo doctor «No API keys found», daemon не делает платных LLM-вызовов; cap $10/день в .env.local + PM2 env как belt Daemon-resurrect helper: C:\Users\Administrator\ruflo-daemon-resurrect.cmd (machine-level, вне репо). Effective state: runtime активен. Related: ruflo big-bang Phase 3-4 нормативная инверсия (9c3057b/d30cbeb/ 5df88a1/f65a8d7/6287561), spec/plan 2026-05-15. |
||
|
|
6287561fce |
docs(sync): Phase 4 cross-refs sync + CHANGELOG_claude_md.md +v2.0 entry — ruflo big-bang Day 4
Ruflo big-bang Phase 4 Task 4.1 — закрывает нормативную инверсию. Изменения: - CHANGELOG_claude_md.md: +v2.0 entry (полное описание Phase 3-4 — 4 normative rewrites Pravila v1.14 / PSR_v1 v3.0 / CLAUDE.md v2.0 / Tooling v2.0 + effective-state candor) - CLAUDE.md §6: «Tooling v2.0 (pending)» → «(commit |
||
|
|
5df88a1310 |
docs(claude_md): §1 +уровень −1 ruflo + §3.5 orchestration + §5 п.10 sub-policy note + §6 ruflo phase — v2.0 (ruflo big-bang Day 3)
Ruflo big-bang Phase 3 Task 3.3. Major bump v1.93 → v2.0: 8-level → 9-level priority chain, ruflo Queen-led routing на уровне −1 (entry-point). Изменения: - Header v1.93 → v2.0 (architectural inversion description + полный legacy tail v1.93→v1.86 preserved) - §0 cross-refs: Pravila v1.13 → v1.14 (commit |
||
|
|
ae20033652 |
docs(claude.md): v1.92 → v1.93 — sync schema header drift 62→63 (Audit #3 P2)
Tail closure of Audit #3 P2 «schema.sql header drift» finding. Schema
source-of-truth was already updated in commit
|
||
|
|
db167c1beb |
docs(meta): CLAUDE.md v1.91 → v1.92 — §3 +#34/#35 sentry+redis (off-phase debug-runtime)
Применены 9 edits через /claude-md-management:claude-md-improver per §5 п.10: - Шапка: v1.91 → v1.92 от 13.05.2026 day +1 - §0 row Pravila: v1.12 → v1.13 (§13.2 +Off-phase MCP debug-runtime) - §0 row PSR_v1: v2.0 → v2.1 (R10.1 Блок 3 +sentry+redis) - §0 row Tooling: v1.16 → v1.17; «33 формализованных» → «35» - §1 priority chain row 2b: «33 инструментов» → «35» - §3 title: «Карта 33» → «Карта 35» - §3.3 table: +#34 Sentry MCP + #35 Redis MCP rows после #33 - §3.3 footer: «Total: 33 = 29+3+1» → «35 = 29+5+1» - §9 история: +v1.92 entry Категория debug-runtime — отдельная от UI-пула (UPM/21st) и от infrastructure (claude-md-management). Не trigger'ит R6.0/R6.1 и не входит в R14 pipeline. READ-ONLY usage обязателен. Связано: Tooling |
||
|
|
19d12c9f95 |
docs(meta): session-end hygiene — CLAUDE.md v1.91 + Pravila v1.12 + audit gap
Capture session-end documentation learnings 13.05.2026 day +1 после CTO-19 ✅ closure (commit |
||
|
|
0832997b6e |
feat(icons): CTO-19 ✅ closed via Lucide migration (Vuetify custom IconSet)
Closes CTO-19 ⏸ from реестр v1.79 — иконочная система портала не была
подключена (`@mdi/font` отсутствовал в `package.json`, все `mdi-*`
рендерились пустыми glyph'ами).
PATH α (aliases-only, brand-compliant) approved заказчиком 13.05.2026
через `superpowers:brainstorming` → `superpowers:writing-plans` →
`superpowers:subagent-driven-development`:
— `npm i lucide-vue-next ^1.0.0` (~25-30 KB gzip tree-shakable)
— `app/resources/js/plugins/vuetify.ts`: custom `IconSet`
(`liderraLucideSet`) с 103-entry `lucideMap`:
· 78 user-grep'нутых mdi-* names из resources/js/**/*.vue
· 25 Vuetify-internal defaults (pagination chevrons, v-checkbox
squares, v-radio circles, v-select dropdown, date picker, paperclip)
— Fallback `HelpCircle` для unmapped
— 51 Vue/TS файл с `icon="mdi-*"` НЕ touched — semantic-ID via Lucide
CLAUDE.md §2 «Иконки: Lucide» бренд-spec compliance achieved.
VERIFICATION (comprehensive, 13.05.2026 day +1):
— vue-tsc 0 errors
— Pest --parallel --recreate-databases: **742/739/0/3**
— Vitest: 88 files / 683 passed / 3 skipped (baseline match)
— Vite build: exit 0, 3.52s
— Visual smoke 8 views via Playwright MCP — все glyph'ы рендерятся
— axe-core a11y scan /admin/billing: **0 iconography violations**
— Pagination + v-checkbox + v-radio fixes (Task 2.b extension)
РЕЕСТР v1.82 → v1.83:
— CTO-19 §3: ⏸ → ✅ (Pravila §2.2 / §7.1 — явное «закрываем» получено)
— Сводка §0 CTO: 17✅/1⏸/1 P2 [?] → 18 ✅/0⏸/0
— Сводка §0 Итого: 70✅/12⏸ → 71 ✅/11 ⏸
— Header v1.82 → v1.83 + новый changelog block
— Footer v1.83 (match header)
CLAUDE.md §0 row sync v1.82 → v1.83 — прямой Edit per «registry version
sync» rationale, не content authoring (CLAUDE.md §5 п.10).
cspell-words.txt +1: «grep'нутых» (Russian-tech jargon).
Path (i) `npm i @mdi/font` REJECTED (250 KB CSS, против бренда).
Path β rename all strings REJECTED (большой diff 51 файл).
Spec: docs/superpowers/specs/2026-05-13-cto-19-lucide-icon-migration-design.md
Plan: docs/superpowers/plans/2026-05-13-cto-19-lucide-icon-migration.md
Quirk 64: app/dev-indices.json attached per Vite watcher auto-regen.
Memory updates — git-untracked, отдельный шаг.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
9bc041992d |
chore(registry): v1.77 → v1.82 catch-up + section ## 13 fix + Сводка counters + CLAUDE.md sync
Реестр Открытые_вопросы имел hidden inconsistency: header v1.77, но footer
trail v1.78/v1.79/v1.80/v1.81 + post-Plan-4 work 12.05–13.05 не отражена.
ИЗМЕНЕНИЯ В docs/Открытые_вопросы_v8_3.md:
— Section ## 13 collision fix: Plan 4 (Billing+CSV+Admin) → ## 14
(Аудит C сохраняется ## 13).
— Header v1.77 → v1.82 + новый changelog block «Что изменилось v1.82 vs
v1.77» с детализацией trail v1.78–v1.81 + post-Plan-4 context.
— Сводка §0 counters update под факт:
· CTO: 16/16✅ → 18/17✅/1⏸ (CTO-19) +CTO-20 закрыт
· Бизнес/продакт: 17/17✅ → 24/17✅/7⏸ (Биз-25..31 Plan 4 deferred)
· +новая строка «Plan 4 (v1.78)» 7/0/7⏸
· Итого продуктовых: 78/69✅/5🟦/4⏸/1P0/3P1/0P2
→ 87/70✅/5🟦/12⏸/1P0/5P1/3P2 +2P3 (Биз-29/30) +1 P2 [?] (CTO-19)
— Сводка строка «Истинные P0-блокеры на 07.05.2026» → «на 13.05.2026
(после v1.82)» с уточнением Plan 4 deferred placeholders нужны до prod.
— Сводка строка «Все P2 закрыты» → актуализирована: 3 open Биз (26/27/31).
— Сводка строка «Открыто 3 P1» → 5 P1 (+Биз-25, +Биз-28).
— Сводка +строка «P3 после v1.82 (2)»: Биз-29, Биз-30 — эмпирические
данные после 1-2 мес эксплуатации.
— Footer v1.81 → v1.82 summary (match header).
ИЗМЕНЕНИЯ В CLAUDE.md:
— §0 row «Открытые вопросы» — v1.77 → v1.82 + удалена post-v1.77 deviation
note (теперь baked в v1.82). Прямой Edit per approved plan «через плагин
ИЛИ ручной Edit с обоснованием 'registry version sync'» (CLAUDE.md §5
п.10 — это registry version-string sync, не content authoring).
PRAVILA §2.2 СТРОГО СОБЛЮДЕНА:
— Ни один новый Q-item не закрыт без явного «закрываем» заказчика.
— Биз-25..31 (Plan 4 deferred) — все остаются ⏸.
— CTO-19 (иконочная система) — остаётся ⏸.
— CTO-20 уже ✅ в v1.81 (ICU collation fix 12.05.2026).
— Plan 5 / Quiet Luxury / Q.DEFER closures / R15 merge — feature delivery
/ audit-internal / regulatory; не Q-items registry.
POST-PLAN-4 CONTEXT (documented в v1.82 changelog):
— Plan 5 frontend Tasks 7-11 delivered (schema v8.20).
— Quiet Luxury portal redesign 20 commits Direction A.
— Portal full audit 2026-05-12 ночь — 10/10 Q-items audit-internal closed
в blocked.md (Q.DEFER.002 sub-B + Q.DEFER.003 sub-A+B+C +
Q.DEFER.004 sub-A+B).
— Audit-cleanup tail 5 commits 54c69a6..d1b2f5d.
— R15 motion-runtime cleanup merge
|
||
|
|
323957ad34 |
chore(merge): R15 motion cleanup origin/main → plan5-frontend-projects
Merge `origin/main` (commits |
||
|
|
a5e99ba0e9 |
docs(claude-md): v1.89 — factual fix §6 + шапка v1.88 (615db99 ≠ Plan 4)
В рамках post-audit continuation session 12.05.2026 ночь обнаружен factual error в v1.88: коммит |
||
|
|
57f0b8e64c |
docs(claude.md): v1.87 → v1.88 audit-driven sync §0/§2/§6/§8
Phase 6 audit found doc-drift: - §0/§2/§8 schema метрики застряли на baseline v8.19 (62/12/117/39/5/13/5). Dev `liderra` factual after migrate:fresh + partitions:create-months: 75 root + 102 partition children + 289 indexes + 39 RLS + 5 user funcs + 19 triggers + 0 dev roles (5 on prod via db/00_create_roles.sql). - §0 row «Открытые_вопросы v1.75» → факт v1.77 (Sprint 4 Audit tail close, 10.05.2026); note про post-v1.77 deviation (Plan 4/5 + Quiet Luxury merged без registry bump). - §6 «Plan 4 ready for FF-merge» → факт «Plan 4 MERGED в origin/main 615db99» + новый параграф про Plan 5 frontend + Quiet Luxury + dev-indices в `plan5-frontend-projects` (85+ commits ahead на 12.05.2026 night). - §8 self-review row: baseline ИЛИ dev-actual disambiguation. - 5 user-функций перечислены поимённо (audit_block_mutation, audit_chain_hash, calc_lead_score, report_jobs_log_export, set_pd_subject_request_deadline). §9 entry для v1.88 описывает полный аудит-сессии: 8 commits Phase 8, 0 регрессий, final baseline Pest 742 / Vitest 614 / Histoire 35 / Vite 1.80s. Через `/claude-md-management:revise-claude-md` (см. blanket approval заказчика «исправь всё что сможешь в моё отсутствие»). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
615db99547 |
chore(rules): remove R15 motion-runtime restrictions (PSR_v1 v2.0)
Conscious rollback of v1.83 audited construction per user decision 12.05.2026. R15 PSR_v1 section deleted entirely; framer-motion remains technical block (React-only peerDep), no longer regulatory rule. Affected: - PSR_v1 v1.7 -> v2.0 (R15, R0.6 p.11, R8 motion, R11.6, R13 motion rows removed; finale + properties reformulated) - CLAUDE.md v1.87 -> v1.88 (#5 p.12 -> marker; #2 motion stack -> guidance) - Tooling v1.15 -> v1.16 (#9.2 reformulated; framer-motion + react-spring marked as technical block, not regulatory) - Pravila v1.10 -> v1.11 (#11.5/#13.2 counts updated; #13.9/#13.10 cross-ref bumps; #13.10 NOT deleted - it governs R14 UPM/21st pipeline, not R15) - CHANGELOG_claude_md.md - v1.88 entry Brainstormed via superpowers:brainstorming. Planned via superpowers:writing-plans. Executed via superpowers:executing-plans + /claude-md-management:claude-md-improver + manual Edit. Spec: docs/superpowers/specs/2026-05-12-remove-r15-motion-restrictions-design.md Plan: docs/superpowers/plans/2026-05-12-remove-r15-motion-restrictions.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8681040479 |
docs: Plan 4 closure — CLAUDE.md v1.87 + Открытые_вопросы v1.78
CLAUDE.md v1.86 → v1.87: - §0 Источник истины row «Схема БД»: schema v8.11 → v8.19, 56 → 62 базовых таблиц, 97 → 117 индексов, 38 → 39 RLS-политик. - §2 Стек строка БД: 4 → 5 ролей БД (+crm_supplier_worker BYPASSRLS из Plan 3 для sharing-flow + Plan 4 ResetMonthlyCountersCommand + CsvReconcileJob). - §6 Текущая фаза: +Plan 4 closure block (15 коммитов на plan4-billing, Pest 687/684+3/0, Vitest 49/428, Histoire 24/31, lychee 0, gitleaks 0). - §8 Self-review триггеры: метрики обновлены до v8.19 = 62/12/117/39/5/13. - §9 История версий: +v1.87 entry с накопленным drift'ом от Plans 1+2+3+4. Через /claude-md-management:revise-claude-md (project rule §5 п.10). Открытые_вопросы v1.77 → v1.78: - +Раздел 13 «Plan 4 — 7 новых открытых вопросов» (Биз-25..31). - Биз-25 P1: дефолтные tier-цены (placeholder в PricingTierSeeder). - Биз-26 P2: rate-limit 1/час/tenant ZeroBalancePausedMail. - Биз-27 P2: tenant видит ВСЕ ступени (transparent). - Биз-28 P1: CSV-схема discovery (BLOCKED Plan 3 Tasks 1-2). - Биз-29 P3: CSV window 25h. - Биз-30 P3: Drift threshold 5%. - Биз-31 P2: pricing-tier повышение цены — единая логика effective 1-е след. мес. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 (
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
a4601fe84b |
phase2(notifications-stage1): NotificationService + new_lead email (P0 этап 1)
Старт closing «Notification delivery» из карты P0. Этап 1/6 плана: NotificationService + Mailable + интеграция в ProcessWebhookJob::chargeNewLead. - App\Services\NotificationService — диспетчер 8 событий × 3 каналов (inapp/push/email) согласно schema.sql:699 users.notification_preferences. Этап 1 реализует только email-канал для new_lead. - App\Mail\NewLeadNotification + emails/new_lead.blade.php — HTML-письмо в Forest-палитре с таблицей phone/contact_name/received_at/deal_id. - ProcessWebhookJob::chargeNewLead — после ActivityLog вызывает notifyNewLead. Throwable от Mail::send проглатывается + Log::warning (отказ канала не должен валить транзакцию). - Pest 11/11 в tests/Feature/Notifications/NewLeadNotificationTest.php: email=true получает / email=false не получает / schema-default не шлёт / inactive не получает / soft-deleted не получает / другой тенант не получает / Биз-19 дубль не дублирует / повторный vid не дублирует / balance=0 не шлёт / subject содержит project_name. - IDE-helper регенерирован (4 модели получили @mixin docblocks). - PHPStan baseline регенерирован (138 ignore.unmatched схлопнулись). Pest 280/280 за 31.27 сек (+11 от 269, 1029 assertions). 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>
|