c94de99c3276fa5625533aedebc031ad16114a3e
2630 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c39d555e6f |
phase2(recovery-code): POST /api/auth/2fa/recovery-use + UseRecoveryCodeView
- AuthController::useRecoveryCode перебирает unused codes через Hash::check, нормализация (lowercase + remove dash/space)
- UserRecoveryCode Eloquent (UPDATED_AT=null — schema без updated_at)
- Rate-limit auth:recovery:{pending_user_id}|{ip} (5/15мин)
- Returns recovery_codes_remaining для UI-warning'а (sessionStorage на frontend)
- UseRecoveryCodeView.vue → POST /api/auth/2fa/recovery-use, /recovery-use route, autocomplete=one-time-code
- TwoFactorView "резервный код" ссылка /recovery → /recovery-use
- Pest +6 RecoveryCodeTest (91/91 за 12.77с, 319 assertions)
- Vitest +6 (166/166 за 11.47с)
- TODO: #3 2FA setup wizard (после этого /recovery view получит реальный source данных)
- Регресс: lint+type+format OK; build 849ms; story:build 21/28 за 30.36с; Pint+Stan passed
- CLAUDE.md v1.38→v1.39, реестр v1.47→v1.48
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
9c488122a1 |
phase2(reset-password): POST /api/auth/reset-password + ResetPasswordView + DB timezone fix
- AuthController::resetPassword через Password::reset() (callback пишет password_hash)
- ResetPasswordRequest: token + email + password (min 10 по ТЗ §22.4.1) + confirmed
- Rate-limit auth:reset:{sha256(token)[0..16]}|{ip} (5/15мин)
- ResetPasswordView для deep-link /reset/:token?email=...; pre-fill email из query; success → redirect /login через 3 сек
- Vue Router /reset/:token (guestOnly); web.php /reset SPA-path
- DB FIX: config/database.php pgsql.timezone=UTC — без него PG TIMESTAMPTZ +03 терялся при Carbon::parse и tokenExpired ошибочно срабатывал
- Pest +6 ResetPasswordTest (85/85 за 11.50с, 291 assertions)
- Vitest +7 (160/160 за 11.02с)
- Регресс: lint+type+format OK; build 784ms; story:build 21/28 за 30.74с; Pint+Stan passed
- CLAUDE.md v1.37→v1.38, реестр v1.46→v1.47
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
170382878b |
phase2(forgot-password): POST /api/auth/forgot + ForgotPasswordView интеграция
- AuthController::forgotPassword использует Password::sendResetLink (anti-enumeration: всегда 200)
- AUTH_PASSWORD_RESET_TOKEN_TABLE=password_resets — указывает на нашу таблицу из schema v8.7
- Rate-limit 5/15мин по auth:forgot:{email}|{ip} — hit ставится ДО sendResetLink (защита перебора через unknown email)
- Frontend: authApi.forgotPassword, auth-store.requestPasswordReset, ForgotPasswordView success-state
- Pest +6 в ForgotPasswordTest (79/79 за 10.55с, 273 assertions)
- Vitest +4 (153/153 за 11.11с)
- TODO: POST /api/auth/reset-password + UI-форма new_password (deep-link)
- Регресс: lint+type+format OK; build 862ms; story:build 21/28 за 32с; Pint+Stan passed
- CLAUDE.md v1.36→v1.37, реестр v1.45→v1.46
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
75897b1636 |
phase2(rate-limit): login + 2FA verify (5/15min) + frontend lockout
- AuthController: RateLimiter::hit/clear на login + verifyTwoFactor по ключу email|ip / pending_user_id|ip - 429 + Retry-After header + JSON retry_after (lockoutResponse helper) - ТЗ §22.4.4: 5 попыток / 15 мин; success чистит throttle; inactive user тоже расходует попытки - extractRateLimitRetry в api/client.ts; auth-store.lockoutSeconds; v-alert в LoginView/TwoFactorView - Pest +6 в RateLimitTest.php (73/73 за 8.07с, 246 assertions) - Vitest +4 в auth-store + LoginView (149/149 за 12.31с) - Quirk: wrong-password в тестах ≥8 символов (LoginRequest::min:8) — иначе валидация падает до controller - Quirk: vi.mock api/client в auth-store.spec — иначе axios.isAxiosError в jsdom возвращает false для plain Error - TODO (отдельные коммиты): IP-lockout 10/час через auth_log + email при 3 неудачах - Регресс: lint+type+format OK; build 886ms; story:build 21/28 за 37.19с; Pint+Stan passed - CLAUDE.md v1.35→v1.36, реестр v1.44→v1.45 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1d1353931d |
phase2(user-chip): реальный user в AppLayout/AdminLayout + Logout-menu
- AppLayout: userInitials/userShortName computed из auth-store, fallback цепочка → email → '?' / 'Гость'
- AdminLayout: тот же паттерн с админ-defaults 'АО' / 'Админ Оператор'
- v-menu offset=8 на user-chip: email + Настройки/Выйти из админки + Выйти
- handleLogout async: auth.logout() (swallows API errors) → router.push('/login')
- Vitest +3 в AppLayout.spec.ts (всего 145/145): store-mock + null-user + email-fallback
- AppShell.spec.ts получил createPinia в plugins
- Регресс: lint+type+format OK, vitest 145/145 за 11.01с, build 855ms, story:build 21/28 за 32.11с, Pest 67/67 за 6.16с
- CLAUDE.md v1.34→v1.35, реестр v1.43→v1.44
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
374724a7a3 |
phase2(auth-2fa): TOTP-verify endpoint + TwoFactorView интеграция
- pragmarx/google2fa@^9.0 для TOTP RFC 6238.
- AuthController::login изменён: при totp_enabled=true НЕ делает Auth::login,
сохраняет auth.pending_user_id+pending_remember в session, возвращает
requires_2fa=true. /me=401 пока 2FA не пройдена.
- AuthController::verifyTwoFactor: читает pending_user_id, верифицирует TOTP
через Google2FA::verifyKey($secret, $code, window: 1) (окно ±1 = 30s).
Success → Auth::login + regenerate + clear pending + last_login_at.
- VerifyTwoFactorRequest: regex /^\d{6}$/.
- /api/auth/2fa/verify публичный (нет session-auth до verify).
Frontend:
- auth-store::login: при requires_2fa=true user остаётся null (иначе
isAuthenticated=true и guard пустит на /dashboard минуя 2FA).
- auth-store::verifyTwoFactor action.
- api/auth.ts::verifyTwoFactor(code).
- TwoFactorView: onMounted redirect на /login если нет pending state;
submit → verify → /dashboard; на error - clear code + focus first cell.
userEmail из auth.user?.email.
Pest +6 (всего 67/67 за 6.97s, 194 assertions): login для 2FA НЕ создаёт
session + verify success/неверный код/без login/валидация формата +
после verify /me=200.
Vitest +3 (всего 142/142 за 10.75s): login pending vs success state +
verifyTwoFactor success/reject. TwoFactorView spec получил setActivePinia
+ requires2fa=true для bypass onMounted-redirect.
PHPStan baseline +26 Pest TestCall warnings (накопительно).
Регресс: pint+stan passed; vitest 142/142; vite build 908ms;
story:build 21/28 за 31.28s; Pest 67/67 за 6.97s.
CLAUDE.md v1.33->v1.34, реестр Открытых_вопросов v1.42->v1.43.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
59299d3c2b |
phase2(auth-frontend): axios + Pinia + auth-store + auth-guards + form integration
- axios@^1.16 + pinia@^3.0 (--legacy-peer-deps). - api/client.ts: axios с withCredentials+withXSRFToken (Sanctum SPA auto-XSRF). ensureCsrfCookie() + extractValidationErrors/Message helpers. - api/auth.ts: типизированные login/register/me/logout с AuthUser interface. - stores/auth.ts: Pinia composition-store (user/loading/requires2fa + isAuthenticated computed + login/register/fetchMe/logout actions). logout() catch-swallow - UI всегда выходит локально. - LoginView/RegisterView: useAuthStore интеграция, real POST через store, errors из 422 на v-text-fields, redirect на /dashboard или /2fa, :loading на btn'ах. - Auth-guard в router.beforeEach: meta.requiresAuth на 10 routes (6 app + 4 admin), meta.guestOnly на login/register/forgot. При первом переходе fetchMe() restore-session. Unauth → /login?redirect=<original>. - / redirect → /dashboard (auth-guard перехватит если не залогинен). - Pinia в app.ts через app.use(createPinia()). - cspell-words.txt: мокаем. Vitest +10 (всего 139/139 за 10.11s): - auth-store 7 (initial state + login success/reject + register + fetchMe success/401 + logout swallow). - router 5 переписан (login.guestOnly + 6 protected + admin layout + 3 error без auth + unauth /dashboard → /login?redirect). - LoginView/RegisterView/router тесты получили createPinia в plugins. - vi.mock api/auth в router+auth-store specs. Регресс: lint+type+format OK; vitest 139/139; vite build (main app-chunk 105→153.64 KB +axios+pinia+auth gzipped 54.54 KB) 806ms; story:build 21/28 за 31.73s; Pest 61/61 за 5.86s. CLAUDE.md v1.32->v1.33, реестр Открытых_вопросов v1.41->v1.42. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
04b90afda4 |
phase2(auth-backend): Sanctum SPA mode + AuthController + 13 Pest tests
- laravel/sanctum@^4.3 install. SPA mode (cookie-based session, не tokens). personal_access_tokens migration удалена (для SPA не нужна). - AuthController (Api/): login + register + me + logout с детальной валидацией + кастомные русские error-messages. - LoginRequest + RegisterRequest Form Requests. Register требует accept_offer:accepted + accept_pdn:accepted (по ТЗ §1.5/§4.1, БЕЗ маркетингового click-wrap'а - расхождение #2 handoff vs ТЗ). - User::fillable += last_login_at, last_active_at. - Auth-routes в web.php (НЕ api.php): Sanctum SPA нуждается в session-cookie middleware из web-группы (laravel.com/docs/sanctum#spa-authentication). - cspell-words.txt: pdn, залогинен. Pest +13 (всего 61/61 за 6.22s): - login success + 2FA-flag + invalid pass + missing email + blocked + format validation + last_login_at update + register success/duplicate/без accept + me 401/200 + logout 200. - Logout-test упрощён до 200+message - Pest cookie-jar держит session между запросами теста, full flow через browser-mode (отдельный коммит). - phpstan-baseline: +25 ignored Pest TestCall warnings (Larastan+Pest quirk). Регресс: pint+stan passed; vitest 129/129 за 9.59s; vite build 802ms; story:build 21/28 за 30.39s; Pest 61/61 за 6.22s. CLAUDE.md v1.31->v1.32, реестр Открытых_вопросов v1.40->v1.41. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
da65cf4bf7 |
phase2(admin): AdminLayout + AdminTenantsView - админка SaaS (12/13 концептов)
- AdminLayout: отдельный sidebar теало-нуар с под-брендом ADMIN (red error 10px JBM uppercase) + 4 nav (Тенанты 142 / Биллинг / Инциденты 3 / Система) + topbar с crumb «Админка → currentPage» + admin-user-chip с error-color avatar. - AdminTenantsView (/admin/tenants): page-head + 5-stats + Экспорт + search/Статус/Тариф фильтры + v-data-table 7 колонок (Тенант с двухстрочным name+inn / Статус-chip 4 цвета / Тариф / Баланс ₽ с error-color при <0 / Желаем×факт / MRR с «—» / Активность). - mockTenants.ts соответствует schema v8.7 §3: 4 статуса × 5 тарифов, 7 mock с разнообразием (active/trial/overdue/suspended) + AdminStats (142/128/9/5/ 1 248 600 ₽). - AdminPlaceholderView универсальный для Биллинг/Инциденты/Система с описаниями ссылающимися на schema v8.7 (incidents_log §9 / system_settings §10). - AppShell расширен meta.layout='admin'. Router: /admin redirect на /tenants + 4 admin-route'а с lazy-imports. Web.php fallback покрывает /admin/*. - cspell-words.txt: Екб. Vitest +11 (всего 129/129 за 10.02s): - заголовок + 5 stats (regex nbsp в 1 248 600 ₽) + 7 columns + 7 rows + Окна Москва ИНН + overdue −1 200 + trial 4 дня + suspended + search filter «Натяжные» → 1 row + Экспорт/Статус/Тариф кнопки. Регресс: lint+type+format OK; vitest 129/129; vite build (admin views в lazy-chunks; main 104.99 KB); story:build 21/28 за 30.32s; Pest 48/48 за 4.89s. CLAUDE.md v1.30->v1.31, реестр Открытых_вопросов v1.39->v1.40. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
034657788d |
phase2(errors): ErrorView 404/403/500 + Laravel fallback
- ErrorView универсальный с конфигурацией через route.meta.errorCode
(404/403/500). По v8_errors.html: full-bleed теало-нуар bg, top-brand,
err-code 96px JBM с accent на средней цифре, title/desc, 2 actions,
опциональные status-list (500) и err-id с copy-btn (403/500).
- AppShell: meta.layout='error' → RouterView напрямую (ErrorView сам
предоставляет v-app).
- Router: /403, /500, catch-all /:pathMatch(.*)* → ErrorView с meta.errorCode.
- web.php: явные Route::view + Route::fallback (срабатывает после Pest
runtime-routes, не ломает SetTenantContextTest).
- cspell-words.txt: резолвится, роуты.
Vitest +8 (всего 118/118 за 9.39s):
- 404 default + 403 с REQ-ID + 500 с INC-ID + status-list (API/Telegram/YooKassa) +
404 actions (На дашборд + Назад) + 403 mailto-link + 500 status-link +
brand-блок + 404 НЕ содержит REQ/INC/status-list (regression-guard).
- stubs:{VApp/VMain} как passthrough — обходим Vuetify layout-injection в jsdom.
Регресс: lint+type+format OK; vitest 118/118; vite build (ErrorView lazy-chunk;
main app-chunk 101.01KB упал на 7KB благодаря shared chunk'ам); story:build
19/26 за 30.96s; Pest 48/48 за 4.88s (fallback не сломал runtime-routes).
CLAUDE.md v1.29->v1.30, реестр Открытых_вопросов v1.38->v1.39.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4de39e70b2 |
phase2(reports): ReportsView - асинхронная генерация отчётов с очередью
- ReportsView (/reports): form-card (Запросить) + jobs-list panel. Form: 4 type-cards radio-grid (Сделки/Менеджеры/Источники/Биллинг) + date-range + Проект/Менеджер v-select + 4 fmt-кнопки (CSV/XLSX/JSON/PDF) + quota-banner alert (CTO-7: 2/3 одновременных + CTO-6: 3 попытки/7 дней) + Запустить/Сброс. - Jobs-list: 5 mock-rows × 4 статуса (done/running/queued/failed) с icon + meta JBM (FORMAT · size · rows · timeText) + status-chip + actions (Скачать done / Повторить failed && attempt<3 / Отменить queued / Удалить done|failed). v-progress-linear для running 62%. - composables/mockReports.ts: type unions (4×4×4) + 5 mock-jobs + MOCK_QUOTA (CTO-6/7 значения). - Маршрут /reports (meta.layout=app, lazy-import) в router + web.php. Vitest +12 (всего 110/110 за 9.38s): - заголовок + page-stats + 4 type-cards + дефолт active + 4 формата + quota-banner («2 из 3» / «3 попыток retry» / «7 дней») + 5 job-rows + done-«Готов»+Скачать-aria + running-«62%»+progressbar role + queued-Отменить + failed-«Ошибка»+«S3 timeout»+Повторить-aria + клик-переключение active. Регресс: lint+type+format OK; vitest 110/110; vite build (ReportsView lazy-chunk; main 108.19 KB); story:build 18/25 за 30.77s; Pest 48/48 за 4.58s. CLAUDE.md v1.28->v1.29, реестр Открытых_вопросов v1.37->v1.38. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
394663597f |
phase2(settings): SettingsView - 8 вкладок (4 реализованы, 4 placeholder)
- SettingsView (/settings): sidebar tabs-rail (md=3, 8 v-list-item с mdi-icon) + content-pane (md=9 v-card outlined min-height 480px). activeTab ref переключает рендер вкладки. Реализованы: - ProfileTab: avatar 80px + 5 form-fields (имя/email disabled/телефон/TZ/роль). - SecurityTab: 3 cards (Пароль / 2FA включена + recovery codes + Отключить / Активные сессии 3 mock с Завершить-btn). - ApiTab: API-ключ password+eye-toggle + Webhook (URL + signing secret HMAC). Текст про дедуп (tenant_id, source_crm_id) 24ч и антифрод по phone (§10.8.1). - NotificationsTab: матрица 8x3 (events × channels) соответствует schema v8.7 §4 users.notification_preferences JSONB. 8 событий (new_lead, duplicate_detected, low_balance, tariff_charge, reminder_due, manager_assigned, webhook_failed, monthly_report) × 3 канала (email/sms/in_app). + sound_enabled switch. Placeholder: - PlaceholderTab универсальный с props title/description + v-alert «В разработке». - Используется для Проекты / Команда / Интеграции / Тихие часы. Маршрут /settings (meta.layout=app, lazy-import) в router + web.php. .gitleaks.toml: settings/*.vue в allowlist (фиктивный профиль). cspell-words.txt: смыслово. Vitest +8 (всего 98/98 за 8.42s): - 8 nav-tabs + все названия + дефолт «Профиль» + Проекты → «В разработке» + Уведомления показывает «События × каналы» + 5 событий матрицы + Безопасность: 2FA + сессии + API: API-ключ + Signing secret HMAC. Регресс: lint+type+format OK; vitest 98/98; vite build (SettingsView lazy-chunk; main app-chunk 107.85KB); story:build 17/24 за 31.7s; Pest 48/48 за 5.03s. CLAUDE.md v1.27->v1.28, реестр Открытых_вопросов v1.36->v1.37. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c8012896e3 |
phase2(billing): BillingView - финансовый экран биллинга и тарифов
- BillingView (/billing): page-head со stats (кошелёк/лиды/runway-дни) + pending banner v-alert info («1 платёж в обработке через ЮKassa, auto-cancel 30 мин») + 3 wallet-cards (Кошелёк ₽ primary card теало-нуар + LIVE; Баланс лидов ГЦК; Тариф «Команда» 990₽/мес + 3 фичи) + transactions panel (4 tabs + v-data-table 5 колонок: Дата/Операция/ID/Статус-chip/Сумма ± JBM tnum) + invoices list (PDF + 1С 8.3 XML). - composables/mockBilling.ts соответствует схеме v8.7 §4.4-4.5: 8 mock транзакций (types: topup/lead_charge/refund/tariff_charge; statuses: pending/completed/rejected) + 4 invoices (pdf/xml_1c83) + pending payment. - Маршрут /billing (meta.layout=app) в router + web.php. Format helpers: «+ N ₽» / «− N ₽» / «— 0 ₽» rejected; Intl.NumberFormat ru-RU. Vitest +11 (всего 90/90 за 7.96s): - заголовок + page-stats nbsp regex + pending banner + 3 wallet-cards + 3 фичи тарифа + 4 tabs + дефолт «Все» 8 строк + format «+/−» + rejected «— 0 ₽» + 4 invoice rows + PDF/1С 8.3 XML labels. Регресс: lint+type+format OK; vitest 90/90; vite build (BillingView lazy-chunk; VDataTable вынесен в общий chunk 79.84KB - shared с DealsView); story:build 16/23 за 32.16s; Pest 48/48 за 4.89s. CLAUDE.md v1.26->v1.27, реестр Открытых_вопросов v1.35->v1.36. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
45239f6602 |
phase2(deal-drawer): DealDetailDrawer - правая панель с деталями сделки
- DealDetailDrawer (v-navigation-drawer right temporary 480px):
- hero (#id eyebrow + name h5 + close + tel:link + clock + status-chip)
- section Параметры (2-col grid: Проект/Стоимость/Менеджер/Источник)
- section Активность (timeline 6 events с iconified vertical-line)
- mockDealEvents.ts: 6 mock-events (created/balance_charged/assigned/viewed/
status_changed/commented) - соответствуют ActivityLog event-константам v8.7.
- Интеграция в DealsView (@click:row) и KanbanView (через @open-deal от карточки).
- cspell-words.txt: iconified, мапы, резолвятся, резолвером, stub'ить, инлайнен.
Vue3 quirk: v-navigation-drawer требует layout-injection от v-app/v-layout,
но в Vitest vite-plugin-vuetify auto-import не работает. Решение:
- DealsView/KanbanView тесты: stubs:{DealDetailDrawer:true}
- DealDetailDrawer тесты: stubs:{VNavigationDrawer:passthrough-div}
Vitest +8 (всего 79/79 за 7.57s):
- DealDetailDrawer 8 (open=false скрытие, deal=null no-content, hero+id,
tel:link, status-chip, params, timeline 6 items, emit update:open(false)).
Регресс: lint+type+format OK; vitest 79/79; vite build (drawer инлайнен в
DealsView+KanbanView lazy-chunks); story:build 15/22 за 31.55s; Pest 48/48.
CLAUDE.md v1.25->v1.26, реестр Открытых_вопросов v1.34->v1.35.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c8131a39c8 |
phase2(kanban-dnd): vuedraggable@4 - drag-and-drop карточек между 14 колонками
- vuedraggable@^4.1.0 + sortablejs@1.14.0 (--legacy-peer-deps). - KanbanColumn: <draggable v-model="localDeals" group="kanban-deals" item-key="id" ghost-class="ghost-card" drag-class="drag-card" animation="150"> + #footer empty-state «пусто · перетащите сюда». - DraggableChangeEvent типизирован (added/removed/moved discriminated union). - KanbanView: const → reactive<Record<slug, MockDeal[]>> (vuedraggable v-model требует независимые arrays); shallow-clone {...d} чтобы не мутировать MOCK_DEALS. - onColumnChange: при event.added → element.statusSlug = targetSlug. TODO: POST /api/deals/{id}/transition с проверкой allowed-переходов. - cspell-words.txt: vuedraggable, симулируется. Visual: ghost-card opacity 0.4 + ivory-tint bg, drag-card rotate 1deg. Vitest +1 (всего 71/71 за 7.48s): эмулирует $emit('change', {added}) → проверяет statusSlug update. Полный mouse-DnD не симулируется — JSDOM не умеет drag-events, но event-handler логика покрыта. Регресс: lint+type+format OK; vitest 71/71; vite build (KanbanView lazy-chunk вырос до 180.53KB - SortableJS-обёртка, грузится только на /kanban); story:build 14/20 за 30.45s; Pest 48/48 за 4.88s. CLAUDE.md v1.24->v1.25, реестр Открытых_вопросов v1.33->v1.34. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d39934c8d9 |
phase2(kanban): KanbanView - 14 колонок по lead_statuses (БЕЗ DnD)
- KanbanCard: компактная карточка (name/phone/project/cost/manager-avatar),
emit('open',id) на click для будущего DealDetailDrawer.
- KanbanColumn: header с border-top по colorHex статуса (--accent CSS-var) +
name+count+total ₽; body с v-for карточек + empty-state «пусто».
- KanbanView: orchestrator, 14 колонок (по LEAD_STATUSES) с группировкой
MOCK_DEALS по statusSlug, horizontal-scroll с custom scrollbar.
- Маршрут /kanban (meta.layout=app) в router + web.php.
- .gitleaks.toml: tests/Frontend/*.spec.ts в allowlist (assertion на mock-телефоны).
- cspell-words.txt: инлайн, vueuse.
DnD НЕ реализован на MVP - отдельный коммит после выбора библиотеки
(vue-draggable-next или @vueuse/integrations/useSortable).
Vitest +14 (всего 70/70 за 7.37s):
- KanbanCard 3 (data + initials + emit open)
- KanbanColumn 5 (header + total + empty + accent CSS-var case-insensitive +
проброс openDeal)
- KanbanView 6 (заголовок + 14 columns + правильные status'ы + stats + кнопка +
DnD-предупреждение)
Регресс: lint+type+format OK; vitest 70/70; vite build (KanbanView lazy-chunk);
story:build 14/20 за 31.17s; Pest 48/48 за 5.06s.
CLAUDE.md v1.23->v1.24, реестр Открытых_вопросов v1.32->v1.33.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a51a94830c |
phase2(deals): DealsView - центральный экран CRM (список сделок)
- DealsView (/deals): page-head со stats (всего/в работе/ждут оплату) + Экспорт/Новая сделка кнопки. filter-bar с 5-tab chiprow (Все/Активные/ Ждут оплату/Закрытые/Невалидные) + поиск по name/phone/project. v-data-table с 6 колонками: Лид (avatar+name+phone), Статус (tonal-chip с colorHex из lead_statuses), Проект, Менеджер, Стоимость (Intl.NumberFormat ru-RU JBM tnum), Время (formatRelative мин/ч/д назад). show-select для будущих bulk-actions. Empty-state. - composables/mockDeals.ts: 12 mock-сделок + DEALS_TABS (5 срезов с slug[]). - Маршрут /deals (meta.layout=app) в router + web.php. - .gitleaks.toml: mockDeals.ts в allowlist (фиктивные имена/телефоны). Vue regex quirk: ESLint no-irregular-whitespace в regex с literal nbsp - \s в JS уже matches U+00A0/U+202F из Intl.NumberFormat, использовать \s+. Vitest +8 (всего 56/56 за 5.66s): заголовок + page-stats + 5 tabs + дефолт active-фильтр (5/12 строк) + кнопки + 6 колонок + format «2 400 ₽» + format «7 мин назад». Регресс: lint+type+format OK; vitest 56/56; vite build (DealsView lazy-chunk 87.54KB - v-data-table крупный, но грузится только на /deals); story:build 11/15 за 31.93s; Pest 48/48 за 4.96s. CLAUDE.md v1.22->v1.23, реестр Открытых_вопросов v1.31->v1.32. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
290e7dbc34 |
phase2(charts): ActivityChart + FunnelChart - Dashboard закрыт по дизайну
- ActivityChart: native SVG line chart (без chart-library, чтобы не +400KB зависимость для статичных дашборд-графиков). Y-grid 5 линий, area-gradient, 7 точек (предпоследняя выделена primary teal как «сегодня»), 3 tabs (Принято/Оплачено/Отказ). - FunnelChart: segmented bar по 14 статусам + funnel-list (sort desc). - composables/leadStatuses.ts: snapshot 14 статусов из db/schema.sql:2130 (НЕ из BRANDBOOK §3.6 - расхождение #1 handoff vs ТЗ из реестра v1.13). 14 правильных slug'ов: new/viewed/worked/base/missed/negotiations/ waiting_payment/partnership/paid/closed/test_drive/hot/replacement/final_missed. - DashboardView интегрирует оба чарта в charts-row (md=7+5). - cspell-words.txt: ldot, композаблом, инлайнингом, инлайнены. Vue compiler quirk: withDefaults factory не разрешает референсить module-level const'ы (checkInvalidScopeReference). Обходим инлайнингом литерала. Vitest +13 тестов (всего 48/48 за 5.5s): - ActivityChart 6 (3 tabs + 7 circles + 'сегодня' + custom points + legend) - FunnelChart 7 (14 segments + 14 list-items + assertion на отсутствие 'Думает'/'Спам' из handoff + сортировка + colorHex + total) Stories +2 с 3 variants каждый (Histoire 10/14 за 30.43s). Регресс: lint+type-check+format OK; vitest 48/48; vite build (DashboardView chunk 14.9->21.17 KB с чартами); story:build 10/14 за 30.43s; Pest 48/48 за 5.10s. CLAUDE.md v1.21->v1.22, реестр Открытых_вопросов v1.30->v1.31. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1e233a70c8 |
phase2(dashboard): AppLayout + DashboardView - default-layout приложения
- AppLayout: v-navigation-drawer (теало-нуар sidebar 240px) + brand-block + nav-tree из 8 пунктов в 3 группах (Работа/Финансы/Команда), v-app-bar с crumb «Рабочая область → currentPage» + search ⌘K + bell + user-chip. Mobile (md<): drawer toggleable. - DashboardView: page-head «Доброе утро, Иван» + page-meta + range-toggle 4 опции (Сегодня/7д/30д/Период). KPI-row из 4 cards: 3 outlined (получено лидов/конверсия/активные проекты) + 1 hero balance с runway-bar 4/7 заполненных сегментов teal #32C8A9. - AppShell упрощён до layout-mapper (route.meta.layout 'app'/'auth'). - Маршрут /dashboard (meta.layout='app') в router + web.php. - histoire.setup расширен 8 app-stub-маршрутами для AppLayout. - Vitest +11 тестов: AppLayout 6 (brand+3 группы+8 пунктов+счётчики+crumb), DashboardView 5, AppShell.spec.ts переписан под layout-mapper. - cspell-words.txt: JBM. Регресс: lint+type-check+format OK; vitest 35/35 за 4.92s; vite build DashboardView lazy-chunk 14.9KB; story:build 8/8 за 28.97s; Pest 48/48 за 4.88s. CLAUDE.md v1.20->v1.21, реестр Открытых_вопросов v1.29->v1.30. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
19096552b4 |
phase2(auth): закрыты 4 оставшихся auth-экрана из v8_login.html
- RegisterView: email + password (strength-meter 0..4) + 2 click-wrap'а (оферта + ПДн). 3-й «маркетинг» из handoff НЕ реализован (расхождение #2 реестра v1.13 - handoff противоречит ТЗ §1.5/§4.1). - TwoFactorView: 6 input-cell с auto-focus вперёд при вводе цифры, Backspace назад при empty, paste 6 цифр заполняет все. - ForgotPasswordView: email + alert «5 попыток / 15 минут» по ТЗ §1.7. - RecoveryCodesView: 8 кодов в 2-column grid + Скачать .txt (Blob/URL.createObjectURL) + Копировать (navigator.clipboard) + warning о невозможности повторного просмотра. Router: 4 новых маршрута (/register, /2fa, /forgot, /recovery), все meta.layout='auth', lazy-imports. Vitest +14 тестов (всего 24/24 за 3.29s): - RegisterView 4 (вкл. assertion на отсутствие маркетингового click-wrap) - TwoFactorView 3, ForgotPasswordView 3, RecoveryCodesView 4 Stories +4 (Histoire 6/6 за 29.17s). Регресс: lint+type-check+format OK; vitest 24/24; vite build 5 lazy-chunks для views + Vuetify в отдельные chunks (app chunk 198KB→78KB); Pest 48/48 за 4.85s. CLAUDE.md v1.19→v1.20, реестр Открытых_вопросов v1.28→v1.29. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e909a95a8d |
phase2(login): vue-router 4 + AuthLayout + LoginView - первый реальный экран
- vue-router@^4.6.4 (--legacy-peer-deps из-за Histoire vs Vite 8 peerDep). - resources/js/router/index.ts: createWebHistory + lazy-imports + meta.layout. Маршрут / -> /login, /login -> LoginView (meta.layout=auth). - resources/js/layouts/AuthLayout.vue: двухпанельный (brand-pane тёмный с radial-gradient акцентами + form-pane warm ivory). На mobile brand-pane скрыт. - resources/js/views/auth/LoginView.vue: форма Vuetify по v8_login.html секция #form-login - email/password (autocomplete + eye-icon), primary submit, Yandex 360 SSO, RouterLink на /register и /forgot. - AppShell.vue: layout-mapper по route.meta.layout (default/auth). - routes/web.php: явные Route::view для 6 SPA-путей (НЕ catch-all - он перехватывал /_test/* в Pest beforeEach и валил 5 SetTenantContextTest). - Vitest: +LoginView.spec.ts (5), +router.spec.ts (2), AppShell.spec.ts переписан (3). Vitest 10/10 за 3.01s. - LoginView.story.vue для Histoire. Setup расширен memory-router'ом. - cspell-words.txt: рендерят, коммиты, Лидерру. - Регресс: lint:vue OK, type-check OK, format:check OK, vitest 10/10, vite build 212 модулей за 383ms (LoginView lazy-chunk 43.5KB JS / 51.7KB CSS), story:build 2/2 за 29.94s, Pest 48/48 за 4.86s. CLAUDE.md v1.18->v1.19, реестр Открытых_вопросов v1.27->v1.28. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c3e45343ac |
phase2(histoire): #24 Histoire 1.0-beta.1 — каталог компонентов, фаза 2 закрыта 6/6
- histoire@1.0.0-beta.1 + @histoire/plugin-vue@1.0.0-beta.1 (--legacy-peer-deps: Histoire требует vite ^7, у нас vite 8 ради @vitejs/plugin-vue 6 — runtime smoke OK). - app/histoire.config.ts: HstVue plugin + setupFile + Forest primary palette (Teal #0F6E56). - app/resources/js/histoire.setup.ts: defineSetupVue3 регистрирует vuetify для каждой story. - app/resources/js/components/AppShell.story.vue — первая story (smoke). - npm-scripts: story / story:build / story:preview. - .gitignore: /app/.histoire/ (статическая сборка). - cspell-words.txt: рендерятся, репо, тулчейна. - Регресс: lint:vue, type-check, format:check OK; vitest 3/3 за 2.98s; vite build 158 модулей за 334ms; story:build 1/1 за 30.25s; Pest 48/48 за 5.12s. Активно 18/28 инструментов: 9 фазы 0 + 8 фазы 1 + 6 фазы 2 (фаза 2 по тулчейну закрыта). CLAUDE.md v1.17->v1.18, Tooling v1.6->v1.7, реестр Открытых_вопросов v1.26->v1.27. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
952373fce5 |
phase2(trigger): Vue 3 + Vuetify 3 + ESLint+Prettier+Vue + vue-tsc + Vitest
Триггер фазы 2 выполнен — первый коммит в app/resources/js/. Активирует 5 из 6 инструментов фазы 2 (CLAUDE.md §3.3) — без Histoire (отдельно). Tailwind удалён (правило CLAUDE.md §5 п.2). Стек: - vue@3.5, vuetify@3.12, @vitejs/plugin-vue@6 (для Vite 8 совместимости — @vitejs/plugin-vue@5 поддерживает только Vite 5/6), vite-plugin-vuetify@2 (auto-import), sass-embedded - eslint@10 (flat-config), eslint-plugin-vue@10, @vue/eslint-config-typescript@14, eslint-config-prettier, prettier@3.8 - typescript@6, vue-tsc@3.2 - vitest@4.1, @vue/test-utils, jsdom, @vitest/coverage-v8 Конфиги: - app/vite.config.js — vue + vuetify auto-import - app/vitest.config.ts — jsdom + setup file для ResizeObserver/ IntersectionObserver/matchMedia/CSS.supports stubs (без них Vuetify падает в JSDOM) - app/eslint.config.js — flat-config - app/.prettierrc.json, app/tsconfig.json Frontend-структура: - resources/js/app.ts — точка входа - resources/js/plugins/vuetify.ts — палитра Forest (Teal/ivory/теало-нуар из BRANDBOOK_v2 §3) - resources/js/components/AppShell.vue — первый компонент: v-app + v-app-bar + v-card - resources/css/app.css — Inter (UI, opsz axis), JetBrains Mono (.numeric, tnum) - resources/views/welcome.blade.php — упрощён под Vue mount Smoke-тесты Vitest 3/3 за 2.8 сек: - монтируется без ошибок - содержит «Лидерра.» - рендерит chip с фазой 2 Build: 158 модулей за 386 ms (184 KB JS / 295 KB CSS gzipped). npm-scripts: lint:vue, format, format:check, type-check, test:vue. lefthook job #8 (ESLint на staged .ts/.vue) добавлен в pre-commit. Полный type-check (vue-tsc) и Vitest — отдельно вручную (медленно для pre-commit: ~3 сек на каждый). CLAUDE.md v1.16 → v1.17 (фаза 1 → фаза 2 в §6). Реестр Открытые_вопросы v1.25 → v1.26. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ba97f952cc |
phase1(webhook): failed() callback + FailedWebhookJob модель — упавшие jobs после 3 ретраев
После исчерпания всех 3 ретраев Laravel вызывает failed(\Throwable $e) —
упавший job сохраняется в failed_webhook_jobs для ручного разбора и
повторного запуска через админку.
Реализация:
- app/app/Models/FailedWebhookJob.php — Eloquent для failed_webhook_jobs
- ProcessWebhookJob::failed() через DB::table->insert (не Eloquent::create)
чтобы обойти RLS: failed-callback запускается вне транзакции воркера,
SET LOCAL app.current_tenant_id не выставлен, политика бы отвергла INSERT.
Запись должна попасть в БД даже в катастрофическом сценарии.
- payload через json_encode(JSON_UNESCAPED_UNICODE) — UTF-8 кириллица
сохраняется
Sentry::captureException оставлен как TODO для production (на dev-стеке
нет DSN).
3 новых Pest-теста:
- failed() пишет упавший job с webhookLogId (через DB::table('webhook_log')
для FK satisfaction)
- failed() работает БЕЗ webhookLogId (NULL ok — soft FK)
- failed() записывает payload с UTF-8 кириллицей корректно
Pest 48/48 зелёные за 4.7 сек. Pint + Larastan чисто.
Webhook-flow покрыт полностью на dev-стеке (за исключением Sentry и
SendNewLeadNotificationJob — Биз-20 Telegram, ждёт настоящего бота).
CLAUDE.md v1.15 → v1.16. Реестр Открытые_вопросы v1.24 → v1.25.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1d4738dfa2 |
phase1(infra): partitions:create-months — Artisan-команда (замена pg_partman)
Закрыт пункт «pg_partman replacement» из project_phase1_strategy.md
(расширение pg_partman недоступно на native Windows-стеке без сборки
из исходников).
Реализация:
- app/app/Console/Commands/PartitionsCreateMonths.php
- signature: partitions:create-months {--ahead=2}
- создаёт партиции для deals + supplier_lead_costs (обе по received_at)
- идемпотентна (проверка через pg_class WHERE relkind='r' перед CREATE)
- запускать ежесуточно через Windows Task Scheduler / cron
Smoke-test на dev: --ahead=8 создал 6 партиций (Nov 2026 - Jan 2027) +
12 skipped. После migrate:fresh партиции возвращаются к initial 6.
4 новых Pest-теста в PartitionsCreateMonthsTest:
- создание партиций на 8 месяцев вперёд для обеих таблиц
- идемпотентность (повторный --ahead=5 → 0 created, 12 skipped)
- --ahead=0 создаёт только текущий месяц
- INSERT в deals с received_at в новой партиции корректно роутится
Тесты используют beforeEach/afterEach для cleanup'а через
DROP TABLE ... CASCADE (FK webhook_dedup_keys на партицию propagates).
Pest 45/45 зелёные за 4.9 сек. Pint + Larastan чисто (phpstan-baseline
регенерирован для динамических свойств $this в Pest closure'ах).
CLAUDE.md v1.14 → v1.15. Реестр Открытые_вопросы v1.23 → v1.24.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
2d9e84ef1d |
phase1(antifraud): DuplicateDetector сервис (Биз-19) — антифрод-дедуп по phone в окне 24ч
Закрыт Биз-19 (§10.8.1) на код-уровне. При создании НОВОЙ сделки сервис
DuplicateDetector ищет master по (tenant_id, phone) в окне 24 ч. Если
найден — новой сделке проставляется duplicate_of_id = master.id, баланс
НЕ списывается, SupplierLeadCost НЕ создаётся. ActivityLog пишется с
context.duplicate_of = master.id.
Реализация:
- app/app/Services/DuplicateDetector.php — отдельный сервис:
findMaster(tenantId, phone, ?Carbon $now): ?Deal. Ищет deals с
duplicate_of_id IS NULL и received_at >= now - 24h. Возвращает
первую по received_at ASC или null. WINDOW_HOURS = 24 — константа.
- App\Jobs\ProcessWebhookJob::handle() — после upsertDeal() для новой
сделки вызывает findMaster(). Если master !== создаваемая сделка —
markAsDuplicate(): UPDATE duplicate_of_id + ActivityLog с context.
- DI через app(DuplicateDetector::class) внутри handle() (не в
сигнатуре — для совместимости с прямыми вызовами из Pest без
Bus::dispatchSync).
4 новых Pest-теста:
- master в окне 24ч → дубль, баланс НЕ списывается
- master старше 24ч → НЕ дубль, баланс списан дважды
- дубли изолированы по tenant_id
- ActivityLog для дубля содержит context.duplicate_of
Pest 41/41 зелёные за 4.1 сек. Pint + Larastan чисто.
CLAUDE.md v1.13 → v1.14. Реестр Открытые_вопросы v1.22 → v1.23.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1ba25e6b4e |
phase1(webhook): закрыты TODO в ProcessWebhookJob — BalanceTransaction/ActivityLog/RejectedDealsLog/SupplierLeadCost
Закрыты 4 TODO в Webhook PoC. Job теперь полностью реализует §5.5
narrative ТЗ за исключением DuplicateDetector (Биз-19) и
SendNewLeadNotificationJob (Биз-20) — отдельные ветви.
5 новых Eloquent-моделей:
- app/app/Models/BalanceTransaction.php — списание lead_charge -1,
type-константы (TYPE_LEAD_CHARGE и т.д.)
- app/app/Models/ActivityLog.php — event=deal.created с
context.source=webhook, event-константы
- app/app/Models/RejectedDealsLog.php — zero_balance ветка вместо
Log::info (payload сохраняется для возможного восстановления)
- app/app/Models/SupplierLeadCost.php — composite PK (id, received_at),
snapshot cost_rub из suppliers, supplier_id resolves через
project_suppliers m2m (первый активный по sort_order)
- app/app/Models/Supplier.php — минимальная для FK target
Job-структура реструктурирована: handle() оркестрирует, делегирует в
logRejection() / chargeNewLead() / resolveSupplierId() / upsertDeal().
Все INSERT'ы в одной DB::transaction — атомарность Ю-2 (deal +
balance_transaction + supplier_lead_cost появляются вместе).
Graceful skip SupplierLeadCost если у проекта нет активного supplier
через project_suppliers + Log::warning. TODO для production: SystemSetting
fallback.
6 новых Pest-тестов в ProcessWebhookJobTest:
- BalanceTransaction lead_charge -1 для новой сделки
- Дубль vid НЕ создаёт BalanceTransaction
- ActivityLog event=deal.created с context.source=webhook
- RejectedDealsLog reason=zero_balance при balance_leads=0
- SupplierLeadCost snapshot cost_rub (helper seedSupplierForProject)
- SupplierLeadCost graceful skip без активного supplier
Pest 37/37 зелёные за 3.9 сек. Pint + Larastan чисто (ide-helper:models
регенерирован для 5 новых моделей).
CLAUDE.md v1.12 → v1.13. Реестр Открытые_вопросы v1.21 → v1.22.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4803fa0200 |
phase1(webhook): Deal/WebhookDedupKey + ProcessWebhookJob (advisory lock) — CTO-17 addendum
Webhook PoC раскрыл архитектурный пробел в schema v8.6: §5.5-спецификация
делает INSERT в webhook_dedup_keys ДО INSERT в deals (атомарный захват
ключа), но FK был immediate. Решение в две стадии:
1. schema.sql v8.6 → v8.7 — DEFERRABLE INITIALLY DEFERRED на FK
(deal_id, deal_received_at) → deals. ON DELETE CASCADE остаётся
immediate. В bare-транзакции production worker'а решает проблему.
2. Pivot Job на pg_advisory_xact_lock — Pest-тесты с DatabaseTransactions
trait всё равно падали: PG проверяет deferred FK на RELEASE SAVEPOINT,
не на outer COMMIT. Воспроизведено standalone PHP-скриптом, это
PG-семантика subtransactions. Advisory lock работает identically
в любой вложенности транзакций. DEFERRABLE FK сохранён в schema
как defense-in-depth для batch-импортов без savepoint.
Backend стек:
- app/app/Models/Deal.php — composite PK через override
setKeysForSaveQuery (PG требует id+received_at для partition pruning)
- app/app/Models/WebhookDedupKey.php — мини-модель для тестов и debug
- app/database/factories/DealFactory.php — fake данные с received_at
в текущей партиции
- app/app/Jobs/ProcessWebhookJob.php — advisory-lock-based upsert
по §5.5 v8.7. PoC scope: dedup + balance check + project findOrCreate.
TODO для следующих ветвей: BalanceTransaction, SupplierLeadCost,
ActivityLog, RejectedDealsLog, DuplicateDetector (Биз-19).
- app/tests/Feature/DealModelTest.php — 6 тестов composite PK + связи
- app/tests/Feature/ProcessWebhookJobTest.php — 6 тестов: новая сделка,
дубль vid, balance=0, изоляция тенантов, findOrCreate проекта,
ON DELETE CASCADE.
Pest 31/31 за 2.7 сек. Pint + Larastan чисто (phpstan-baseline регенерирован,
scanFiles _ide_helper_models.php добавлен в phpstan.neon).
Документы:
- db/CHANGELOG_schema.md §W (две стадии решения)
- narrative §2.4/§5.5/§6.5/§11 синхронизированы под advisory lock
- Реестр Открытые_вопросы v1.20 → v1.21
- CLAUDE.md v1.11 → v1.12
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e7c075919a |
phase1(hygiene): narrative ↔ schema v8.6 webhook dedup — закрытие техдолга v1.19
Точечная in-place синхронизация SQL/PHP-примеров webhook handler'а в narrative
ТЗ под двустадийную dedup-логику schema v8.6 (CTO-17). 8 разделов, без bump'а
версии файла (как для L13-гигиены
|
||
|
|
5560ebbdfd |
phase1(eloquent): Tenant/User/Project models + SetTenantContext middleware
Первый Eloquent-слой над schema v8.6 + middleware для RLS-фильтрации HTTP-запросов. Pest 19/19 passed (1672 ms): 4 RLS smoke + 8 model smoke + 5 middleware + 2 default. app/app/Models/ (NEW Tenant.php, Project.php; MODIFIED User.php): - Tenant — saas-уровневая модель (БЕЗ RLS, тенант-родитель). Soft Deletes. hasMany Users, Projects. - User — переписан под нашу схему: password_hash вместо password, first_name/last_name вместо name, override getAuthPassword/Name для Laravel auth-интеграции. Soft Deletes. belongsTo Tenant. - Project — tenant-aware с RLS. belongsTo Tenant. app/database/factories/ (NEW TenantFactory, ProjectFactory; MODIFIED UserFactory): - TenantFactory: уникальный subdomain через Str::random + дефолты (timezone Europe/Moscow, locale ru, is_trial true, api_key_limit 5). - UserFactory: tenant_id через Tenant::factory() chain, email unique через faker, password_hash через Hash::make. - ProjectFactory: tenant_id через factory chain, дефолты под schema.sql (region_mask 255, delivery_days_mask 127, assignment_strategy manual). app/app/Http/Middleware/SetTenantContext.php (NEW, alias `tenant`): - Резолюция tenant_id (приоритет): auth()->user() → subdomain (3+ parts HTTP_HOST) → X-Tenant-Id header (только dev/testing). - Без контекста → 403 Forbidden с явным сообщением. - SET LOCAL app.current_tenant_id внутри транзакции (DB::beginTransaction + SET LOCAL + next() + commit/rollback). PgBouncer-safe (Прил. И Г.1 кейс 2). - Зарегистрирован в bootstrap/app.php через $middleware->alias(). app/tests/Feature/ (NEW TenantModelsTest, SetTenantContextTest): - TenantModelsTest (8 тестов): factories + связи (Tenant→users/projects, User→tenant, Project→tenant) + проверка User::getAuthPassword override. - SetTenantContextTest (5 тестов): 403 без контекста, X-Tenant-Id header, игнор не-числового header, subdomain резолюция через absolute URL, middleware устанавливает app.current_tenant_id для last query. Сопутствующие правки: - app/.gitignore: + _ide_helper_models.php (сгенерированный, как и _ide_helper.php — не в репо) - app/phpstan-baseline.neon: regenerated — Pest dynamic methods ($this->get(), withHeaders()) и factory-return-type mismatch (Larastan v3 не понимает array<string, mixed> vs array<model property of T>) + Request::user/header (Larastan тип hint мисс) + console.php $this в Closure — все в baseline до миграции на typed properties / pest extension - CLAUDE.md §6: Pest 6/6 → 19/19, добавлены модели + middleware - memory project_state.md, MEMORY.md: обновлены под новый этап Не сделано в этой сессии (отложено): - Deal model — composite primary key (id, received_at) + partitioned parent. Эта модель сложнее по архитектуре Eloquent. - ide-helper:models -W -M -N запускался — добавил @mixin IdeHelperX и сгенерил _ide_helper_models.php; но т.к. этот файл gitignored, @mixin строки удалены из моделей (PHPStan не нашёл бы класс на CI). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
c6f9c62da0 |
phase1(rls-smoke): CTO-13 — Pest 4 RLS smoke-test (4/4 passed, 662 ms)
Реализованы кейсы 1 + 4 из Прил. И Г.1 «CTO-13: RLS smoke-test через PgBouncer» как первая проверка RLS-фундамента schema v8.6 ДО первого PR с tenant-моделью. app/tests/Feature/RlsSmokeTest.php (NEW): - кейс 1 (× 2 теста): SET LOCAL app.current_tenant_id изолирует SELECT в deals — оба тенанта видят только свои 2 deals из 4 общих. - кейс 1 расширенный: RLS работает на projects (не только deals) — тот же tenant-контекст применяется ко всем 36 политикам. - кейс 4: WITH CHECK блокирует INSERT в projects с чужим tenant_id — ожидается QueryException (RLS WITH CHECK violation). Стек теста: - testing-роль `testing_rls_user` NOLOGIN (создаётся идемпотентно через DO $$ ... IF NOT EXISTS $$). На dev superuser обходит RLS — поэтому через SET LOCAL ROLE переключаемся на NOLOGIN-роль без BYPASSRLS. - DatabaseTransactions trait вместо RefreshDatabase — каждый тест в транзакции, ROLLBACK сбрасывает SET LOCAL ROLE и тестовые данные. - Отдельная БД liderra_testing (создана `CREATE DATABASE` через psql, мигрирована `DB_DATABASE=liderra_testing artisan migrate:fresh` 743 ms). - phpunit.xml: DB_CONNECTION sqlite → pgsql, DB_DATABASE liderra_testing. Pest 6/6 passed (RlsSmokeTest 4/4 + ExampleTest 2/2) за 723 ms total. Кейсы НЕ покрытые (отложены): - Кейс 2-3 (PgBouncer transaction-pooling reuse, job retry): production- только, на native Windows-стеке нет PgBouncer - Кейс 5 (REVOKE на 6 saas-таблицах для crm_app_user): требует ролей из db/02_grants.sql, на dev не созданы (только postgres-superuser) Сопутствующие правки: - .gitleaks.toml: + allowlist path для app/tests/*.php (фиктивные телефоны вида +79000010001 в фикстурах — не реальные ПДн) - app/phpstan-baseline.neon: regenerated — Pest dynamic $this properties ($this->tenant1Id и т.п.) не парсятся PHPStan без pest-extension, занесены в baseline (12 entries) до миграции на typed-properties - CLAUDE.md §6: Pest 2/2 → 6/6, добавлено упоминание CTO-13 smoke-test - memory project_state.md, MEMORY.md: smoke-test реализован Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f502b0058d |
phase1(backend): multi-tenant фундамент развёрнут — schema v8.5→v8.6 + migrate:fresh
Backend multi-tenant фундамент развёрнут на dev-БД liderra: 68 таблиц (52 обычных + 16 партиций) + 36 RLS-policies + 5 функций + 13 триггеров. `php artisan migrate:fresh` за 870 ms через одну raw-SQL миграцию `load_initial_schema.php` (DB::unprepared с db/schema.sql). Первый реальный запуск schema.sql на pristine PG 16 поймал 2 несовместимости v8.5, исправлены архитектурно (CTO-17): 1. CREATE UNIQUE INDEX на партиционированной deals (schema:1263) PG требует partition key (received_at) в UNIQUE; включить нельзя — ломает идемпотентность webhook'ов. Решение: новая таблица webhook_dedup_keys (не партиционированная, PK (tenant_id, source_crm_id) → deal_id, composite FK на deals(id, received_at) ON DELETE CASCADE, RLS tenant_isolation USING+WITH CHECK). UNIQUE INDEX в deals заменён на обычный. Webhook handler — двустадийная UPSERT. 2. GENERATED ALWAYS AS на pd_subject_requests.deadline_at (schema:1999) `+ INTERVAL '30 days'` не immutable. Решение: обычная TIMESTAMPTZ NOT NULL + триггер trg_pd_subject_requests_deadline + функция set_pd_subject_request_deadline(). Изменения: - db/schema.sql: v8.5 → v8.6 (заголовок, 1 новая таблица, 1 RLS-policy, 1 функция, 1 триггер, замена UNIQUE на обычный INDEX, замена GENERATED на TIMESTAMPTZ NOT NULL) - db/CHANGELOG_schema.md: новая запись §X v8.5→v8.6 - db/00_create_roles.sql (NEW): deployment-скрипт 4 ролей PG для production (crm_app_user, crm_admin_user BYPASSRLS, crm_migrator BYPASSRLS+CREATEDB, crm_audit_writer). На dev — postgres superuser (schema §13 разрешает) - db/02_grants.sql (NEW): GRANT/REVOKE из закомментированных секций §13 schema. REVOKE на 6 saas-таблицах для crm_app_user (defense-in-depth поверх RLS, OPEN-И-14). REVOKE DELETE на 4 финансовых таблицах для crm_admin_user (только soft markers) - app/database/migrations: удалены 3 default Laravel (users/cache/jobs дублировались с нашей schema), создан 0001_01_01_000000_load_initial_schema.php - .squawk.toml: + excluded_paths для db/00_create_roles.sql (psql client-side variables :'name' не парсятся libpg_query) - docs/Открытые_вопросы_v8_3.md: v1.18 → v1.19, CTO-17 закрыт фиксом, 70 ✅ / 5 🟦 / 4 ⏸. Техдолг: ТЗ §15-16 webhook handler нужно обновить под двустадийную dedup-логику - CLAUDE.md: v1.9 → v1.10 (§0 ссылки на schema v8.6 + реестр v1.19; §2 метрики БД 54→55/91→92/35→36/12→13/4→5; §6 фундамент развёрнут) - cspell-words.txt: +9 новых терминов Smoke-test через Boost MCP database-query: - 68 таблиц (включая webhook_dedup_keys + 16 партиций) - 36 RLS-policies - 35 RLS-enabled (relkind='r'; +2 partitioned 'p' = 37 total) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e81eeef1fe |
phase1(db-tooling): squawk v2.51 + pgFormatter v5.9 — закрытие тулчейна фазы 1
Активны Прил. Н #15 squawk и #16 pgFormatter; фаза 1 по тулчейну закрыта 13/17 (без #17 pg_partman — Windows native стек). squawk: - bin/squawk.exe (npm i -g squawk-cli + копия из ~/AppData/... /squawk-cli/js/binaries/squawk → bin/squawk.exe; npm-wrapper не находит spawn-target без .exe на Windows) - .squawk.toml: 9 правил исключено (5 bootstrap-неприменимых + 4 дизайнных решения проекта). Smoke-test на db/schema.sql — 0 issues. - lefthook.yml: pre-commit job на staged *.sql - npm run lint:sql pgFormatter: - bin/pgFormatter/ (pg_format + lib/ из v5.9 release tarball, запуск через Cygwin Perl 5.42.2 из Git for Windows — без отдельной установки ActivePerl/Strawberry) - Без авто-fix хука: diff vs db/schema.sql 3255 строк (UPPERCASE→lowercase для типов, плотный одностроковый стиль, перетасовка inline-комментариев) — стиль schema.sql ручной, авто-fix недопустим - Только ручной режим: npm run format:sql:check (dry-run + diff), npm run format:sql (пишет в db/schema.sql.formatted для review) Документы: - Tooling Прил. Н v1.5 → v1.6 (§0 «Что нового», §3.3 таблица, §10.1 п.8 ⏸→✅, §11.4 уточнение по Cygwin Perl) - CLAUDE.md v1.8 → v1.9 (§0 ссылка, §6 текущая фаза 17/28, колонтитул) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
59f8ca04bb |
phase1(hooks-fix): сужение pre-push lychee + 2 битых ссылки + allowlist demo HTML
При первом запуске pre-push hook'ов после установки lefthook (`4eee06f`)
обнаружились 3 проблемы — все легитимны, не в самом lefthook'е:
1. **gitleaks-full-history** ловил 42 хита `ru-phone-unmasked` в HTML-концептах
handoff Платона (liderra_v8_handoff/concepts/v8_*.html и web/v8/v8_*.html).
Это ДЕМО-данные для визуализации, не реальные ПДн. Добавлены в allowlist
.gitleaks.toml: `liderra_v8_handoff/concepts/.*\.html` + `web/v8/.*\.html`
+ `app/composer.lock`.
2. **lychee-links** ловил 21 ошибку «Cannot resolve root-relative link» на
ссылки `/login`, `/register`, `/legal/*` в HTML-концептах. Эти маршруты
появятся только в фазе 2+ (Vue+Vuetify реализация). Сужен glob
pre-push lychee — выкинут `web/**/*.html`. Дополнительно добавлены
`liderra_v8_handoff/concepts` и `web/v8` в .lychee.toml exclude_path
как защита для других вариантов запуска.
3. **2 РЕАЛЬНЫХ битых ссылки** в narrative — спасибо lychee, нашёл:
- `docs/CRM_bp-gr_Инструкция_v8_5.md:6128` ссылался на `brandbook.md`,
но этот файл удалён 08.05.2026 (заменён на `liderra_v8_handoff/docs/
BRANDBOOK_v2.md` v8 Forest). Исправлен относительный путь.
- `README.md:88` ссылался на `docs/README_АРХИВ_v8_4.md`, но переименован
в `_v8_5.md` коммитом `4ffc19a` от 07.05.2026. Поправлено + bump v8.4→v8.5
в подписи.
Финальный smoke-test после правок:
- gitleaks detect (full history): «no leaks found»
- lychee на narrative .md: 122 OK / 0 Errors / 5 Excluded
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4eee06fb22 |
phase1(hooks): lefthook auto-install + Pint + Larastan pre-commit hooks
Закрывает Tooling §10.1 шаги 10/11 («хуки в .claude/settings.json для Pint/Larastan» — реализация через lefthook, как и было обещано в CLAUDE.md §3.5: «Pre-commit (gitleaks, link-check) — не здесь, отдельно через git hooks (lefthook)»). Добавлено в lefthook.yml: - pre-commit job #5 «pint»: glob app/**/*.php, root app/, `php vendor/bin/pint {staged_files}` + stage_fixed: true (авто-fix форматирования PSR-12 на staged PHP) - pre-commit job #6 «larastan»: glob app/**/*.php, root app/, `php vendor/bin/phpstan analyse --no-progress --memory-limit=512M` (полный анализ при любой правке *.php — phpstan нужен граф классов, точечный анализ только staged невозможен) Хуки установлены через `npx lefthook install` — .git/hooks/pre-commit и .git/hooks/pre-push сгенерированы. Smoke-test: - pint --test (на default scaffold) → passed - phpstan analyse --no-progress → 0 ошибок выше baseline (3 в baseline) Это опционально-блокирующий слой: при коммите .php — Pint авто-форматирует, Larastan ловит ошибки выше phpstan-baseline.neon. Обход — `LEFTHOOK=0 git commit` (использовать только для emergency). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9ee31b71cb |
phase1(docs): Tooling v1.5 + CLAUDE.md v1.8 — фиксация фактической установки Boost
Doc hygiene после установки Boost ( |
||
|
|
0eb2f72da2 |
phase1(security): roave/security-advisories — CVE-блокировка на install (Прил. Н #13)
Метапакет roave/security-advisories:dev-latest добавлен в require-dev. Это conflict-only пакет — он не содержит кода, а просто помечает известные CVE-версии других пакетов как несовместимые. При попытке установки/обновления таких версий composer падает с conflict-resolution до того, как уязвимый код попадёт в vendor/. Текущий снапшот зависимостей чист: «No security vulnerability advisories found» при установке (95 пакетов). Закрывает Прил. Н #13. Не требует конфигурации, бинарников, env. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e04f53b103 |
phase1(boost): laravel/boost v2.4.6 + Vuetify guideline (Прил. Н #10)
Установка Laravel Boost вручную (без `php artisan boost:install`):
wizard падает в обоих режимах из-за двух багов окружения:
- интерактив: PHP fatal на кириллице-пути при рендере laravel/prompts
multiselect (нет UTF-8 codepage в Windows PowerShell)
- --no-interaction: ConfiguresPrompts::multiselectFallback возвращает
null → array_map() crash в L13 framework
Manual setup вместо wizard'а:
- app/composer.json: laravel/boost ^2.4 + транзитивы (laravel/mcp v0.7,
laravel/roster v0.5.1, symfony/yaml v7.4)
- app/boost.json: agents=[claude_code], guidelines=true, mcp=true
- .mcp.json (корень): добавлен блок laravel-boost — command=php,
args=[app/artisan, boost:mcp]. Заменяет PostgreSQL MCP из фазы 0
(был убран в _comment_postgres ещё ранее).
- app/.ai/guidelines/vuetify.md: кастомный Vuetify 3 guideline по
Tooling §10.2 + Forest-палитра + 14 slug'ов воронки берутся из
schema.sql:2076 (НЕ из BRANDBOOK_v2 §3.6).
Smoke-test (boost:mcp via JSON-RPC):
- protocolVersion 2024-11-05, serverInfo "Laravel Boost"
- 9 tools: application-info, browser-logs, database-(connections|query|
schema), get-absolute-url, last-error, read-log-entries, search-docs
Уточнение к CLAUDE.md §7: «отключать guidelines Inertia/Livewire/Tailwind/
Filament/Sail/PHPUnit» — избыточно. Boost через laravel/roster детектит
установленные пакеты из composer.lock; то, что не установлено, не серверит.
Pest 4 будет detected, PHPUnit guideline не активируется (PHPUnit убран
из direct deps в коммите
|
||
|
|
3a9ed7101e |
phase1(hygiene): narrative ↔ Laravel 13 — закрытие техдолга v1.17
Точечная синхронизация трёх narrative-документов под Laravel 13 (один токен на файл, без bump'а версии каждого): - docs/CRM_bp-gr_Инструкция_v8_5.md:6219 - docs/Vybor_oblaka_v8_3.md:3 - docs/Админка_SaaS_v8_2.md:103 Не трогалось намеренно (исторические записи): - docs/Объединённый_конспект.md:149 — Часть I, фиксация решений v8.0 на 03.05 - docs/Открытые_вопросы_v8_3.md:354 → 361 — фиксация результата аудита Реестр Открытые_вопросы v1.17 → v1.18 (новый блок «закрыт техдолг»). Оперативная карта CLAUDE.md v1.6 → v1.7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
63c7364e6e |
reopen(stack): Laravel 11 → Laravel 13 — фиксация переоткрытия
Произошло так: 08.05.2026 при `composer create-project laravel/laravel app` я не зафиксировал `^11` — Composer подтянул свежайшую `laravel/framework: ^13.7` (Laravel 13.7, релиз ~Feb 2026), вместо ожидаемого Laravel 11 LTS. Smoke-test'ы подтвердили работоспособность Laravel 13: - php artisan migrate: 3/3 default-миграций OK - pestphp/pest 4.7.0: 2/2 за 281 ms - laravel/pint 1.29: pint --test passed - larastan/larastan 3.9.6 + phpstan 2.1.54: analyse passed (с baseline) - barryvdh/laravel-ide-helper 3.7.0: ide-helper:generate OK Live-проверка совместимости 5 ключевых плагинов: - laravel/boost v2.4.6: composer dry-run резолвит lock без conflict - остальные 4 — установлены и запущены без issue Заказчик 08.05 (поздний вечер) принял Laravel 13 как latest stable. Откат дороговат — rm -rf app/ + повторение всей сессии (~10-15 мин). По правилу «явная фиксация переоткрытий» обновлены 3 источника: - CLAUDE.md v1.5→v1.6: §2 backend (Laravel 11 → Laravel 13 + объяснение обстоятельств), §0 источники (Tooling v1.4, Реестр v1.17), футер - docs/Tooling_v8_3.md v1.3→v1.4: блок «Что нового в v1.4» (live- проверка совместимости 5 плагинов + установка Pint/Larastan/IDE Helper) - docs/Открытые_вопросы_v8_3.md v1.16→v1.17: блок «Что изменилось в v1.17» с обстоятельствами, импактом и техдолгом Техдолг (для следующих сессий): синхронизация narrative ТЗ + 2 других архивных документов под Laravel 13: - docs/CRM_bp-gr_Инструкция_v8_5.md (строка 6219, главное ТЗ) - docs/Vybor_oblaka_v8_3.md (строка 3, аналитическая записка по облаку) - docs/Админка_SaaS_v8_2.md (строка 103, описание стека админки) Не блокер для разработки, но нарушает связность архива. Включить в hygiene-проход при следующем апдейте narrative до v8.6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
03456833eb |
phase1(tooling): Pint + Larastan + IDE Helper (Прил. Н #11/#12/#14)
Установлены 3 dev-инструмента из Прил. Н фазы 1: - laravel/pint v1.29 (форматтер PHP, Прил. Н #11) — Laravel preset по умолчанию. Команды: composer pint, composer pint:test - larastan/larastan v3.9.6 + phpstan/phpstan v2.1.54 (статанализ PHP, Прил. Н #12) — level 5, paths app/bootstrap/config/database/ routes/tests. Команда: composer stan - barryvdh/laravel-ide-helper v3.7.0 (IDE-stubs, Прил. Н #14) — команда: composer ide-helper. Артефакт _ide_helper.php в gitignore Конфиги: - app/phpstan.neon — base config с includes на larastan extension и baseline. Включены checkOctaneCompatibility и checkModelProperties - app/phpstan-baseline.neon — зафиксированы 3 ошибки в default Laravel scaffold (UserFactory return type covariance + 2× ExampleTest always-true). Двигаемся вверх с этого baseline'а. Composer scripts (composer.json): - pint, pint:test (форматирование / dry-run проверка) - stan (PHPStan analyse с memory-limit 512M) - ide-helper (generate + meta) Smoke-test'ы все 3 прошли: Pint passed, PHPStan passed (с baseline), ide-helper:generate написал _ide_helper.php. Фикс окружения: на этой машине composer require падал с "Permission denied на vendor/composer/tmp-XXX.zip" из-за race condition параллельных curl'ов. Решение: COMPOSER_MAX_PARALLEL_HTTP=1 (сохранено в memory feedback_environment). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
30f0335f5f |
phase1(test): Pest 4 swap + reopen(CTO-12) — Pest 3 → Pest 4
Произошло так: при `composer require pestphp/pest pestphp/pest-plugin-laravel --dev --with-all-dependencies` я не зафиксировал `^3` — composer подтянул свежайшую Pest v4.7.0 + pest-plugin-laravel v4.1.0. Smoke-test (`./vendor/bin/pest`) на default-тестах Laravel 11 прошёл 2/2 за 281 ms — backward-compat с Pest 3 syntax подтверждён. Заказчик 08.05 (поздний вечер) принял Pest 4 после live-проверки на стеке. Бонус Pest 4: browser testing (без Dusk), stress testing, mutation testing v2. Откат дёшев — `composer require pestphp/pest:^3`. Что сделано: - composer remove phpunit/phpunit (был direct dev-dep) — phpunit остался как транзитивная зависимость Pest - composer require pestphp/pest:^4.7 pestphp/pest-plugin-laravel:^4.1 --dev - Pest.php создан через `vendor/bin/pest --init` (`tests/Pest.php`) - `vendor/bin/pest` smoke-test: 2 passed (Unit/Feature ExampleTest), 281 ms - `vendor/bin/pest --init` упал на интерактивном промпте «Wanna show Pest some love?» в non-interactive PowerShell — Pest.php к этому моменту уже создан, нефатально Reopen(CTO-12): по правилу «явная фиксация переоткрытий» обновлены 3 источника: - docs/Открытые_вопросы_v8_3.md v1.15→v1.16: блок «Что изменилось в v1.16», таблица §0 (CTO-12 переоткрыт+закрыт), запись §3 (Pest 4 + обоснование), финальный список §X (Pest 4) - docs/Tooling_v8_3.md v1.2→v1.3: блок «Что нового в v1.3», §3.1 п.4 (Pest 4 в boost:install), §3.4 строка 18 (Pest 4 + бонусы), §6 п.2 (конфликт Pest↔PHPUnit), §10.1 п.9 (процедура установки + warning про --init на Windows non-interactive) - CLAUDE.md v1.4→v1.5: §0 источники (Tooling v1.3, Реестр v1.16), §3.2 строка 18 (Pest 4), §7 п.5 (Pest 4 в boost:install), футер с историей версий Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4d38f75826 |
phase1(scaffold): Laravel 11 + predis + .env под PG 16 + Memurai
Триггер фазы 1 запущен 08.05.2026 (вечер): composer create-project laravel/laravel app Стек подтверждён native (без Docker/WSL2/Sail): - PostgreSQL 16.13 (Chocolatey, Windows-сервис, port 5432) - Memurai Developer 4.1.8 (Redis 7-совм., port 6379) — TCP +PONG OK - PHP 8.3.31 + 11/11 Laravel-required ext (pdo_pgsql, mbstring, openssl, tokenizer, xml, ctype, json, bcmath, fileinfo, curl, pgsql) - Composer 2.9.7 Что в коммите (59 файлов, 11059 строк скаффолда Laravel 11 + правки): - composer require predis/predis (v3.4.2) — PHP-only Redis-клиент, т.к. php_redis ext не установлен (см. project_phase1_strategy.md) - app/.env (gitignored) — APP_NAME=Liderra, APP_LOCALE=ru, APP_TIMEZONE=Europe/Moscow, DB_CONNECTION=pgsql → liderra@localhost, REDIS_CLIENT=predis - app/.env.example — те же правки без секретов (для команды) Smoke-test PG ↔ Laravel ↔ pdo_pgsql прошёл: 3/3 default-миграций → 9 таблиц в liderra (cache, cache_locks, failed_jobs, job_batches, jobs, migrations, password_reset_tokens, sessions, users). Артефакт стартера app/database/database.sqlite (0 B) удалён — sqlite не используется. Что НЕ в этом коммите (следующие шаги фазы 1): - Pest 3 swap (CTO-12) — composer remove phpunit + require pest - Laravel Boost MCP + 9 guidelines disable по CLAUDE.md §5/§7 - Pint, Larastan, Roave/SecurityAdvisories, IDE Helper, squawk, pgFormatter (Прил. Н #11–18) - resources/boost/guidelines/vuetify.blade.php Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
477e866726 |
phase1(stack): native Windows вместо Sail — Tooling v1.2, CLAUDE.md v1.4
Обнаружено 08.05.2026 (вечер): машина — VPS на OpenStack (Manufacturer = OpenStack Foundation, HypervisorPresent = True). Provider не пробрасывает nested virtualization в guest VM (VirtualizationFirmwareEnabled = True на CPU, но KVM на хосте блокирует VT-x для гостя). Docker Desktop показывает «Virtualization support not detected, Engine stopped». Следствие: ранее обсуждённые варианты A (полный WSL2) и B (Sail на Windows FS) — физически невозможны на этой машине. Переход на вариант D — native Chocolatey-стек. Изменения: - Tooling v1.1 → v1.2: §3.1 п.2/п.3 — Sail удалён из allow-list Boost. Шапка обновлена под native-стек. Блок «Что нового в v1.2» с обоснованием. - CLAUDE.md v1.3 → v1.4: §0 (версия Tooling v1.2), §6 (стек dev = native PG 16 + Memurai + PHP 8.3), §7 п.3 (Sail отключён в boost:install). Футер. Установлено 08.05 вечер: - ❌ Docker Desktop — удалён (choco uninstall docker-desktop) - ⏳ PostgreSQL 16.13 — устанавливается (choco install postgresql16) - ⏳ Memurai Developer 4.1.8 — устанавливается (Redis 7-совместимый, free до 1GB RAM) pg_partman/pg_audit/pg_anonymizer на native Windows — ручная замена. На MVP: партиции через php artisan cron-команду (Биз-партиции уже в schema v8.5), audit-trigger'ы уже встроены. Подробное обоснование и триггеры пересмотра — в personal memory project_phase1_strategy.md (не коммитим). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b0be139845 |
chore(gitignore): подготовка к фазе 1 — Laravel в app/ + игнор лендинг/
Подготовка перед composer create-project laravel/laravel app
(стратегия B — Sail на Windows FS, monorepo).
Расширены Laravel-пути:
- /app/vendor/, /app/node_modules/, /app/public/build/
- /app/storage/{app/public,framework,logs}/, /app/bootstrap/cache/*
- /app/.env*, /app/auth.json, /app/.phpunit.cache/, /app/.pest.cache/
- /app/.idea/, /app/.vscode/, /app/Homestead.{json,yaml}
- !/app/storage/logs/.gitkeep, !/app/bootstrap/cache/.gitkeep
(Laravel требует пустые папки)
Также игнорируем лендинг/ — локальный дизайнерский трек
(TZ_landing_v1_0.md + README + ZIP-handoff ~9.5 МБ для дизайнера),
не должен случайно попасть в основной репо.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
7f28721723 |
fix(typo): laravel/liderra → laravel/laravel в триггере фазы 1
CLAUDE.md §6 и Объединённый_конспект Часть X указывали `composer create-project laravel/liderra app`. Такого Composer- шаблона на Packagist нет. `liderra` — название нашего продукта, а не пакета. Правильный official-стартер: `laravel/laravel`. Замечено при smoke-test окружения 08.05 (готовка к фазе 1, вариант B — Sail на Windows FS). Без этого фикса первая команда фазы 1 упала бы. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e1641f2e16 |
docs(hygiene 08.05): Часть X конспекта — ребрендинг + CTO-12 + Диз-1
Доделка консистентности архива по итогам сессии 08.05.2026 (коммиты |
||
|
|
63c7c83d82 |
close(Диз-1): HTML-прототипы покрыты handoff'ом Платона — реестр v1.15, CLAUDE.md v1.3
Решение заказчика 08.05.2026: формально закрыть Диз-1 (P1) — handoff v8 Forest от Платона покрыл HTML-прототипы на 100%+ (13 экранов в web/v8/ против 8 запланированных + 3 экрана ошибок). Прил. Л не выпускается (избыточно после handoff'а). Точечные расхождения handoff vs ТЗ (зафиксированы в шапке v1.13) реализовывать по ТЗ/schema: - 14 «обобщённых» статусов в BRANDBOOK_v2 §3.6 ≠ 14 slug'ов в schema - 3-й click-wrap в v8_login.html ≠ ТЗ §1.5/§4.1 - SSO «локальный 2FA fallback» в v8_admin.html ≠ ТЗ OPEN-И-13 axe: 81 violation на 10/13 HTML (color-contrast на декоративных separator'ах) — исправлять при реализации в Vue+Vuetify. Изменения: - Открытые_вопросы_v8_3.md v1.14 → v1.15: Диз-1 в §5 → ✅; сводка §0 (дизайнер 2→3 закрыто, итого 78/68→69 ✅, 5→4 ⏸, 4→3 P1) - CLAUDE.md v1.2 → v1.3: §0 (версия реестра v1.15), §6 (P0-блокер упоминание Диз-1 закрыт), футер Открыто на 08.05.2026: **4 ⏸ продуктовых** — Б-1 (P0) + 3 P1 (Диз-3, DO-2, DO-4), все ждут Б-1. Один настоящий блокер — Б-1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
62a85f2a08 |
close(CTO-12): Pest 3 — реестр v1.14, Tooling v1.1, CLAUDE.md v1.2
Выбран Pest 3 (не PHPUnit). Обоснование: - datasets лаконично покрывают 14 статусов воронки и 34 RLS-политики - architecture testing закрепляет правила Claude на код-уровне (например, integration тесты не моки́руют БД — линт вместо памяти) - родной для Laravel/Boost (Nuno Maduro в core team) — один guideline вместо разрыва на boost:install - Pest на PHPUnit под капотом — откат дёшев Изменения: - Открытые_вопросы_v8_3.md v1.13 → v1.14: запись о закрытии в §3, сводка §0 (CTO 15→16 закрыто, итого 77→78 / 67→68 ✅) - Tooling_v8_3.md v1.0 → v1.1: §3.1 п.4, §3.4, §6 п.2, §10.1 п.9 - CLAUDE.md v1.1 → v1.2: §0 (версии источников), §3.2 строка 18, §7 п.5, футер Импакт: при composer create-project (триггер фазы 1) — вместо шага «решить Pest или PHPUnit» теперь composer require pestphp/pest --dev --with-all-dependencies + vendor/bin/pest --init. Guideline PHPUnit на boost:install отключить. P0-блокер фазы 1 остаётся один — Б-1 (юр. лицо). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3dca049f9c |
chore(cspell): +opsz/+tnum/+rgba — термины из CLAUDE.md v1.1 §2/§3
Автодобавлены pre-push lefthook'ом после коммита
|