Commit Graph

1449 Commits

Author SHA1 Message Date
Дмитрий 2ec8707a03 docs(pilot): 25.05 — incident supplier:session РАЗРЕШЁН (бага нет, auto-refresh работает)
TTL-арифметика 2 cron-тиков (implied write 02:01:09 + 03:01:08 = journalctl
DONE) доказала: cron обновляет сессию каждый час. Ранний вывод «zombie lock»
был ошибкой замера. Root cause 3-дневного простоя — worker бежал со stale
--timeout=60 (timeout.conf=300 от 22.05 не подхвачен без рестарта); recycle
00:18 UTC подхватил правильный конфиг → самовосстановление.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 06:04:09 +03:00
Дмитрий 81f52fd1c6 docs(brain): spec v2.2 — закрыты 16 находок 0%-аудита
v2.1 → v2.2. Полный critical audit (экономия 0%) нашёл 16 пробелов;
все закрыты. Новый §22 — changelog с таблицей всех находок.

Критическое (1):
- D1: §17 formal text (§6) включал Continuation в exempt-список direct,
  но Continuation наследует non-conversation classification → §17 enforce
  должен применяться. §6 п.4 переписан: exempt только Acknowledgment +
  Cancellation; Continuation — нет (NON_BLOCKING_TASK_TYPES не содержит её).

Внутренние несогласованности (6):
- A1: schema alternative_better типизирован только null → заметка string|null.
- A2/C3: task_cost reviewer tokens vs usd — взаимоисключающи по пути review.
- A3: §11.3 step 3.2 «создать reviewer-agent.md» → файл уже создан (49aa4ba7).
- A4: prompt-enrichment risk добавлен в §14.
- A5/B3: missed-activations.mjs читает archived map → adapter task §11.2 task 9
  (переключение на nodes.yaml) + §9.5 detail + §14 risk.
- A6: §18.7 «reviewer-agent будет добавлен» → уже добавлен.

Underspecified (5):
- B1/B5: inheritance state→episode chain описан (§4.1 проверка 2).
- B2: reviewer fallback path = brain-retro-opus-reviewer.mjs сохраняется из v2.0.
- B4: discipline-metrics.mjs keep/remove → task §11.1 task 17.

Typo / ellipsis (4):
- C1: «existed» → «создана» в DoD.
- C4: «ОНLY» (кириллица) → «ONLY» в reviewer-agent.md.
- C5: anchor list финализирован (28 существительных + 10 императивов, ellipsis убран).

Сравнение аудитов: v1.0 имел 35 находок, v2.1 — 16, обе волны закрыты.
Остаточные implementation-вопросы (model ID, clustering algo) — в §15,
решаются на старте фаз.

Реализация — после закрытия Биллинга v2 Спек C.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 06:02:33 +03:00
Дмитрий 455bc1439b docs(pilot): 25.05 день — Биллинг v2 Спек C Phase 1 backend готов на ветке (прод НЕ затронут)
Снимок-заметка: feature-фаза, на боевой liderra.ru ничего не выкачено.
Ветка feat/billing-v2-spec-c HEAD d8955f57 (9 ahead of main).
Phase 1 preflight баланса: заморозка cut-off 18:00 MSK + 409 при
перегрузке + 4 письма + initial-sweep. Schema v8.36. Pest 13/13.
Осталось: Task 1.10 frontend + Phase 2-5 (VTB безнал).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 05:54:31 +03:00
Дмитрий 000c196e51 docs(pilot): 25.05 утро — recovery supplier:session (3 дня тишины у Компании 1)
Корень: Redis ключ liderra-database-liderra-cache-supplier:session был пуст
во всех DB. SupplierPortalClient не мог авторизоваться → CSV recovery=0,
sync 7 дней пустой, поставщик не активировал выдачу.

Hot-fix: dispatchSync через cat-pipe tinker stdin (quirk #109 workaround).
TTL 5ч59м. CsvReconcileJob drift=0.425 — false-positive (мусор в project field).

scheduler+worker+cron конфигурация правильная (timeout=300, hourly entry,
liderra-queue active, /etc/cron.d/liderra-scheduler каждую минуту).
journalctl показал DONE каждый час, но Redis пуст — вероятно zombie lock
после 22.05 worker-крахов. Точный root cause не установлен; auto-verify
в 02:00 UTC.

Все 275 lead_charges Компании 1 = prepaid (старая схема); ни одного
charge_source=rub за всю историю прода. Биллинг v2 Phase A ждёт первого
нового лида чтобы впервые применить tier-lookup живьём.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 04:32:07 +03:00
Дмитрий 49aa4ba725 docs(brain): spec v2.1 + reviewer-agent — 16 правок после code review
v2.0 → v2.1 — 3 группы изменений (16 пунктов суммарно):

Группа 1 — решения принятые после v2.0, не внесённые:
- 1.1 Памятка classifier (4 паттерна: brainstorming / discovery-interview /
  writing-plans / systematic-debugging). +flag prompt-enrichment-mode.
- 1.2 Reviewer как полноценный Claude Code subagent (tools=[Read,Grep,Glob,
  Skill], model=opus). Новый файл .claude/agents/reviewer-agent.md.
  +стоимость $240-1200/мес vs $40-80 direct API. Crash fallback на direct
  API. Context bloat cap 10 соседних эпизодов.
- 1.3 Inheritance + 3 группы коротких prompt'ов (continuation/acknowledgment/
  cancellation) + 30-минутный таймаут. +flag inheritance-mode. Новые поля
  в schema v4.1: inherited_from_task_id, inheritance_age_minutes,
  previous_direction_rejected, previous_task_id_rejected.

Группа 2 — edge cases:
- 2.1 Reviewer model явно opus в agent file.
- 2.2 Reviewer subagent crash → fallback direct API call.
- 2.3 Reviewer context bloat: max 10 episodes в agent system prompt.
- 2.4 Manual override приоритет №1 в prefilter (раньше inheritance).
- 2.5 Cancellation clears state + previous_task_id_rejected marker.

Группа 3 — мелкие упущения:
- 3.1 brain-retro SKILL.md description: раз в 1-2 недели (не sprint).
- 3.2 recommended_chain_id nullable для custom chains.
- 3.3 Embedding только для non-prefilter эпизодов.
- 3.4 PII filter wraps sanity-check comments.
- 3.5 requested_node fuzzy matching fallback.
- 3.6 Anchor word list inline initial.
- 3.7 Self-retrospect counter init в фазе 3 step 3.3.
- 3.8 Sanity-check answer file schema_version=1.

Cost rewrite: 720-1380 USD (v2.0) -> 1940-8200 USD (v2.1) на 6 месяцев
из-за reviewer subagent. Granular rollback через reviewer-mode=direct-api
возвращает к v2.0 ценам.

§21 новый — changelog v2.0 → v2.1 со всеми 16 пунктами и где правка.

Реализация — после закрытия Биллинга v2 Спек C.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 04:23:33 +03:00
Дмитрий 10eed4e7e4 docs(brain): spec v2.0 LLM-first router overhaul
Brainstorming сессия 2026-05-24..25:
- LLM-first классификатор (Sonnet 4.6) заменяет regex Layer 1.
- §17 «universal skill-coverage» заменяет §12 (default-deny кроме
  conversation/micro/manual_override/escape-hatch).
- Opus 4.7 ревьюер в /brain-retro заполняет review.* (auto, не пользователь).
- Self-retrospect skill (opt-in) для самоанализа агента.
- 7 гранулярных flag-переключателей + dry-run rollback.
- 3 implementation фазы (~3.5-4.5 недели) + 5-6 месяцев passive
  evidence collection + фаза 4 distillation regex из эмпирики.

v1.0 содержал 8 фактических ошибок (несуществующие skill-discipline
файлы, отсутствующие nodes.yaml поля, L1-L16 в неверных файлах) +
11 пропусков охвата + 10 underspecified + 6 противоречий. v2.0 —
полная перезапись с реальным state inventory (§18) и explicit
accepted trade-offs (§19).

Реализация — после закрытия Биллинга v2 Спек C (per user decision).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 03:27:02 +03:00
Дмитрий af6c328933 docs(billing-v2-c): спек C + план реализации (preflight + VTB)
Спек: preflight баланса на cut-off 18:00 MSK (защита от заказа лидов
клиентам без денег) + VTB-эквайринг через TopupGateway-интерфейс
(безнал полный, СБП/карты dev-заглушки до Б-1).

План: 6 фаз TDD-разбивкой, ~30 задач, subagent-driven-development
с git-verify-протоколом per Pravila §15.1.

Брейнсторм 24.05.2026, реализация стартует 25.05.

Lint: гибрид «Преfflight»→«Префлайт» (опечатка предыдущей сессии),
+6 терминов в cspell (Atol/uniqid/ОФД/брейнсторме/префлайт/Префлайт).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 03:09:07 +03:00
Дмитрий 2e2abe0e53 docs(pilot): legacy webhook removal — выкачено на боевой 24.05 (инцидент + fix + retry + smoke OK)
Phase 6 deploy. 13 commits + fix d377d977 чужей миграции. Инцидент 15:52 UTC →
rollback c7f603aa → fix + повторный deploy → миграция применилась за 57.85ms.
БД: webhook_log/rejected_deals_log/tenants.webhook_token* DROPPED;
webhook_dedup_keys ЖИВА. Портал HTTP 200, шеринг-канал жив.

+3 слова в cspell-words.txt (pre-existing typos в старых snapshot'ах).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 19:54:55 +03:00
Дмитрий d377d97737 fix(migration): 2026_05_22_000002 — use pgsql_supplier connection (owner-rights fix)
Миграция падала на проде:
  SQLSTATE[42501]: Insufficient privilege: must be owner of table webhook_log

Причина: default connection 'pgsql' (crm_app_user) не имеет owner-прав на
webhook_log (owner — crm_migrator). Заменено на 'pgsql_supplier'
(BYPASSRLS-роль crm_supplier_worker) — паттерн Спека B Phase 1 (commit 546ca30a),
который выработан ровно под эту проблему prod-ролей.

Эта миграция блокировала выкатку legacy-webhook-removal (Phase 6 deploy
24.05.2026, отменено rollback'ом). После fix миграция применится
no-op (webhook_log будет дропнут моей миграцией 2026_05_24_140000
сразу после).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 19:29:05 +03:00
Дмитрий 6262639904 chore(stan): Phase 5b — regen baseline after SupplierWebhookLoggingTest deletion
Remove 2 stale SupplierWebhookLoggingTest.php entries from phpstan-baseline.neon.
3 remaining unmatched inline @phpstan-ignore-next-line are pre-existing
(SupplierProjectGrouping/SupplierConnectionTest/Pest.php, present in origin/main).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:51:18 +03:00
Дмитрий af690eaaaa refactor(webhook): Phase 5 — delete SupplierWebhookLoggingTest (tests dropped webhook_log table)
SupplierWebhookLoggingTest.php queried webhook_log table which was dropped
in Phase 4 DROP migration (schema v8.35). This file was missed in Phase 3
cleanup (WebhookReceiveTest.php was deleted but SupplierWebhookLoggingTest
was a separate file testing the same dropped infrastructure).

4 tests deleted — all tested webhook_log INSERT/SELECT which is now gone.
SupplierWebhookTest.php (new controller tests) remains unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:51:18 +03:00
Дмитрий 04aed13bc4 chore(stan): Phase 5 — regen baseline after legacy webhook removal
Remove stale PdErasureService empty.variable ignore (no longer reported).
3 remaining unmatched inline @phpstan-ignore-next-line in SupplierProjectGrouping/
SupplierConnectionTest/Pest.php are pre-existing (present in origin/main).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:51:17 +03:00
Дмитрий 6e1f5355b8 refactor(webhook): Phase 4 — DROP migration + schema v8.35 + test/factory cleanup
Task 4.1 Steps 1–7: legacy direct webhook channel DDL removal.

Migration 2026_05_24_140000_drop_legacy_webhook_artefacts:
- DROP TABLE webhook_log CASCADE (partitioned RANGE по received_at)
- DROP TABLE rejected_deals_log CASCADE
- ALTER TABLE tenants DROP COLUMN webhook_token, webhook_token_rotated_at
- DELETE FROM system_settings WHERE key = 'low_balance_threshold_leads'
NB: webhook_dedup_keys ОСТАВЛЕНА — используется CSV-каналом (HistoricalImportService).

Services fixed (не покрыты Phase 3):
- MonthlyPartitionManager::PARTITIONED_TABLES — убрана строка webhook_log
- PdErasureService::eraseSubject() — убрана секция 4 (SELECT/UPDATE webhook_log)

Factory + tests cleanup (webhook_token column gone):
- TenantFactory: убрано webhook_token из definition()
- 7 test files: убраны вставки webhook_token в DB::table('tenants')->insert(...)
- storage/_demo_split_tenants.php: убрана строка webhook_token

Schema v8.35:
- −2 таблицы (webhook_log partitioned + rejected_deals_log)
- −5 индексов (idx_webhook_log_*, idx_rejected_*, idx_tenants_webhook_token)
- −2 RLS-политики
- db/CHANGELOG_schema.md: запись v8.35

Tests updated:
- SchemaDeltaTest: 66 base tables / 120 indexes / 40 RLS policies
- PartitionsCreateMonthsTest: webhook_log убрана из regex / 48 skipped вместо 54

Smoke: 36/36 passed (RlsSmoke, AdminBilling, AdminPdSubject, PartitionsCreateMonths, SchemaDelta).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:51:17 +03:00
Дмитрий dffefe7fc0 docs(billing): Phase 3 cleanup — refresh orphan comments to live classes
After ProcessWebhookJob/WebhookReceiveController removal — обновлены 8
docblock/inline комментариев, ссылавшихся на удалённый код:

- DealController: ProcessWebhookJob → SupplierWebhookController/RouteSupplierLeadJob
- SupplierWebhookController: убрана legacy backward-compat note
- ImportLeadsJob: паритет с RouteSupplierLeadJob
- RouteSupplierLeadJob: убрана ссылка на ProcessWebhookJob-pattern
- NewLeadNotification mailable: триггер в RouteSupplierLeadJob
- FailedWebhookJob model: ссылка на RouteSupplierLeadJob::failed()
- SupplierLeadCost model: создаётся в LedgerService::chargeForDelivery
- CsvLeadsParser: паритет с RouteSupplierLeadJob парсером

Code-функциональность не затронута, только doc-rot fix.
2026-05-24 18:51:16 +03:00
Дмитрий d3ed266830 chore(stan): Phase 3 - regenerate phpstan-baseline.neon (remove stale WebhookReceiveTest.php entries) 2026-05-24 18:51:16 +03:00
Дмитрий e5eed0aeac refactor(webhook): Phase 3 Task 3.1 fixup - delete WebhookReceiveTest.php (missed in Task 3.1+3.2 commit) 2026-05-24 18:51:15 +03:00
Дмитрий c71d830375 refactor(webhook): Phase 3 Task 3.6 - delete LowBalanceNotification + ZeroBalanceNotification mailables and blade templates 2026-05-24 18:51:15 +03:00
Дмитрий 58d0561bb7 refactor(webhook): Phase 3 Task 3.5 - remove notifyLowBalance/notifyZeroBalance from NotificationService 2026-05-24 18:51:14 +03:00
Дмитрий 220fc6e9c9 refactor(webhook): Phase 3 Task 3.4 - delete RejectedDealsLog model (all callers removed in Phase 2) 2026-05-24 18:51:14 +03:00
Дмитрий b75a677d12 refactor(webhook): Phase 3 Tasks 3.1+3.2 - delete WebhookReceiveController + remove POST /api/webhook/{token} route 2026-05-24 18:51:13 +03:00
Дмитрий 281c4ca5ce refactor(webhook): Phase 3 Task 3.0 - remove webhook_token from Tenant fillable/casts (factory+tests deferred: col still NOT NULL) 2026-05-24 18:51:13 +03:00
Дмитрий ebca32a212 refactor(billing): Phase 2 — remove legacy ProcessWebhookJob + cascade test cleanup
Удалён рудимент pre-sharing эпохи:
- app/app/Jobs/ProcessWebhookJob.php (job целиком, 342 строки)
- app/tests/Feature/ProcessWebhookJobTest.php (тест целиком, 362 строки)

Каскадная чистка 4 тест-файлов:
- BalanceNotificationsTest: -128 строк (оставлены topup_success/invoice_paid)
- InAppNotificationTest: -168 строк (остался notifyInApp direct)
- NewLeadNotificationTest: целиком удалён (-199 строк)
- DealCreatePdLogTest: -36 строк webhook-кейса (остались API+Route)

Локальный smoke (7 тестов без --parallel): 7 passed / 20 assertions.

Phase 2 плана 2026-05-24-legacy-direct-webhook-removal.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:51:12 +03:00
Дмитрий c7f603aa75 feat(brain): register project-agents delegation rule (Pravila §2.4 + CLAUDE.md §3.9 + registry #84/#85)
Level 1 + Level 2 of agent auto-invocation:

Level 1 — нормативный контракт:
- Pravila §2.4 (new) — controller MUST delegate to project agents:
  * normative-sync (#84) after big task closure (4-file sync trigger)
  * prod-deploy-validator (#85) before any liderra.ru deploy
  * pest-parallel-debugger / rls-reviewer — prior project agents formalized in same table
- CLAUDE.md §3.9 (new) — operational map index of all 4 project agents

Level 2 — наблюдатель (missed-activation detector):
- docs/registry/nodes.yaml +#84 normative-sync, +#85 prod-deploy-validator
  с subcategory: "project-agent" + agent_file: attribute
- triggers.classification: "normative_sync_needed" / "prod_deploy_imminent"
  автоматически подхватываются registry-to-classification-map.mjs runtime;
  deprecated observer-classification-map.json не правится.
- tools/registry-load.test.mjs fixtures: 83→85 / 75→77 active

Tooling канон счётчиков НЕ изменился (#1-#83 остаётся; project-агенты вне Tooling).

Spec: docs/superpowers/specs/2026-05-24-controller-offload-agents-design.md.
Headers: Pravila v1.39→v1.40, CLAUDE.md v2.27→v2.28.

Level 3 (hooks) — defer; level 1+2 покрывают первый раунд автоматизации.

Also: +6 cspell words for new vocabulary in normative paragraphs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 17:10:28 +03:00
Дмитрий 9fa5ca1a86 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	cspell-words.txt
2026-05-24 16:14:26 +03:00
Дмитрий 9bc090fbc3 fix(agents): prod-deploy-validator — real prod paths + sudo on П2 + UTF-8 fix
First functional smoke revealed 3 mismatches between agent's templated commands
and the real liderra.ru server layout:

1. App path: /var/www/liderra.ru/app/ → /var/www/liderra/app/ (no .ru segment).
   Affected lines: П1, П2, П5, П8 + smoke command examples.
2. Backups path (П4): /var/backups/db/ → /home/ubuntu/backups/.
3. П2 `file .env` — needs sudo (ubuntu user lacks read on .env);
   green criterion changed from "ASCII text" to "no CRLF substring"
   (UTF-8 is normal when .env has Cyrillic comments).

Without these fixes the agent would issue false NO-GO on every real run
(paths fail / sudo denied) — surfaced by smoke per design ("unexpected
format → escalation, never guess"). Captured in memory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:02:45 +03:00
Дмитрий be8f582a50 docs(continuity): stage 3 follow-up закрыт — 3 fixes + STATUS regen
3 fixes done in worktree feat/router-stage3-three-fixes:
- d7d8c5e helper readStdinAsUtf8 (StringDecoder, 4 tests)
- c7e02ee 3 хука прокинуты через helper (3 placeholder тестов)
- 593f12a observer-state-enricher helper (9 tests, +empty-string guard)
- 92bbd64 parseTranscript enrichment (2 tests, 4 fields в primary_rationale)

Final regression: 538/538 tools GREEN. gitleaks 0/1490 commits.
warn-only mode сохранён. CHECKPOINT B на следующих сутках работы.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 16:01:41 +03:00
Дмитрий 224a048e56 Merge remote-tracking branch 'origin/main' into feat/router-stage3-three-fixes 2026-05-24 16:00:06 +03:00
Дмитрий 92bbd64eed feat(observer): обогащение primary_rationale из router-state (Task 3)
- parseTranscript получает третий параметр options = {}
- options.routerStateBaseDir пробрасывается в readRouterState
- recommended_node: router-state переопределяет classification-map
- новые поля: recommended_chain, chain_progress, chain_completed
- 2 новых теста (enrich + fallback), 538/538 tools GREEN

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 15:53:59 +03:00
Дмитрий 593f12ae6a feat(observer): state enricher helper для эпизодов (stage 3 follow-up 2)
readRouterState(sessionId, {baseDir}) -- pure read state-файла сторожа.
extractRouterFields(state) -- pure извлечение 4 полей для primary_rationale.

Используется парсером эпизодов на следующем шаге (Task 3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:45:43 +03:00
Дмитрий e3ec24462a feat(agents): add prod-deploy-validator project agent (8 SSH checks, Sonnet 4.6)
Pre-flight validator before liderra.ru deploys. Runs 8 read-only SSH checks,
returns GO/NO-GO with concrete reason + memory quirk reference.
Driven by 24.05.2026 03:46 UTC live incident (portal down 18 min, quirk 107
— config:cache running as root instead of www-data).

Spec: docs/superpowers/specs/2026-05-24-controller-offload-agents-design.md §4.
Precedent: .claude/agents/pest-parallel-debugger.md format.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:36:14 +03:00
Дмитрий c7e02eeac9 feat(router): подключить UTF-8 helper к трём хукам (stage 3 follow-up 1)
router-prehook, router-stop-gate, router-tool-gate теперь читают stdin
через readStdinAsUtf8 (StringDecoder). Русский в промпте корректно
доходит до Anthropic API и в state-файл — никаких mojibake типа
'посмотри'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:36:14 +03:00
Дмитрий 352354e30b docs(billing): plan — sync after Phase 1 impact-checks (RED FLAG: webhook_dedup_keys жив)
Phase 1 impact-check выявил что webhook_dedup_keys использует HistoricalImportService
(CSV-канал) для идемпотентности — таблицу и модель НЕ удаляем.

Изменения в плане:
- Task 1.8: заполнена финальная таблица (13 удалить, 2 оставить).
- Task 3.0 NEW: чистка tenants.webhook_token из 7 тест-файлов + фабрики + Tenant model.
- Task 3.3 CANCELLED: WebhookDedupKey.php остаётся.
- Task 4.1: миграция БЕЗ DROP webhook_dedup_keys; verify-команды скорректированы.
- Task 4.2: db/schema.sql baseline сохраняет CREATE TABLE webhook_dedup_keys.
2026-05-24 15:30:38 +03:00
Дмитрий d7d8c5edac feat(router): UTF-8 safe stdin helper for three hooks
StringDecoder correctly assembles multi-byte chars (Cyrillic) across
stdin chunk boundaries. Closes Windows Node quirk where Russian prompts
were turned into mojibake before sending to Anthropic API (Layer 2 escalation).

Stage 3 follow-up fix 1/3 (helper).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:26:18 +03:00
Дмитрий c89630310f feat(agents): add normative-sync project agent (4-file sync, Sonnet 4.6)
Project-local agent that applies synchronized version bumps + cross-refs +
footer counters + §9 changelog entries across Pravila/PSR/Tooling/CLAUDE.md
after a completed task. Does NOT commit. Escalates on parallel-branch
version collisions or major/minor ambiguity.

Spec: docs/superpowers/specs/2026-05-24-controller-offload-agents-design.md §3.
Precedent: .claude/agents/rls-reviewer.md format.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:24:06 +03:00
Дмитрий 136bad4db2 docs(router-stage3): план — 3 follow-up фикса с TDD-шагами
Декомпозиция: Task 1 (UTF-8 helper + 3 хука), Task 2 (state-enricher),
Task 3 (parser enrichment), Task 4 (smoke + continuity + push).

Subagent-driven последовательно: Task 1-3 Sonnet, Task 4 controller Opus.
Worktree от свежего origin/main + junction'ы. Финал — push на main FF.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 15:20:23 +03:00
Дмитрий 36ada767f4 docs(plan): implementation plan for controller-offload agents (3 tasks)
3-task TDD-ish plan to create two new project agents:
- Task 1: .claude/agents/normative-sync.md (full Sonnet 4.6 system prompt)
- Task 2: .claude/agents/prod-deploy-validator.md (8 SSH checks + quirks 104-108)
- Task 3: First dry-run smoke test for both + capture lessons in memory

Spec: docs/superpowers/specs/2026-05-24-controller-offload-agents-design.md (71a5dd6).
Also: +2 cspell-words (маппинге, dogfooded).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:19:01 +03:00
Дмитрий 5f9bd07dd9 docs(router-stage3): spec — три follow-up фикса (UTF-8 + recommended_node + chain_progress)
3 дыры обнаружены при инспекции warn-only состояния сторожа 24.05.2026:
1. UTF-8 mojibake в state-файле сторожа (Windows Node stdin без setEncoding).
2. recommended_node не пишется в эпизоды наблюдателя (helper существует, не подключён).
3. chain_progress / chain_completed / recommended_chain — то же.

Без починки brain-retro новых метрик (domainHitRate / chainCompletionRate)
покажет пустоту, а Layer 2 эскалация на русских промптах работает по
испорченному тексту. Stage 3 enforce включать до фиксов нельзя.

Spec scoped к 3 файлам кода + ≤80 строкам нетто; subagent-driven
последовательно (3 Sonnet + closure Opus). Smoke на живой сессии.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 15:13:18 +03:00
Дмитрий 71a5dd6f6d docs(spec): controller-offload agents design (normative-sync + prod-deploy-validator)
Spec for two specialized AI agents to offload the main controller:
- #1 normative-sync: applies 4-file normative sync (Pravila/PSR/Tooling/CLAUDE.md)
  after a completed task. ~20 invocations/week, saves ~70K controller tokens
  per episode. Model: Sonnet 4.6.
- #2 prod-deploy-validator: 8-check pre-flight before liderra.ru deploy.
  ~5-7 invocations/week. Driven by 24.05 03:46 UTC 18-min portal incident
  (quirk 107 — config:cache not under www-data). Model: Sonnet 4.6.

Based on brainstorming session 24.05 with measured frequencies from
MEMORY.md + CLAUDE.md §6 + push history 16-24.05.

Precedents: pest-parallel-debugger, rls-reviewer project agents.

Also: +7 cspell-words entries for the new spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:11:43 +03:00
Дмитрий 4dbf78b204 docs(billing): plan — legacy ProcessWebhookJob removal implementation
7 фаз: Phase 0 worktree → Phase 1 impact-checks (8 grep'ов) →
Phase 2 удаление core (job + 1 dedicated test) →
Phase 3 удаление обвязки (controller + route + model + conditional
NotificationService методы + Mailable) →
Phase 4 DROP-миграция БД (3 таблицы + 2 колонки tenants) →
Phase 5 регрессия + code review →
Phase 6 merge + deploy + 7д наблюдение.

Все conditional-блоки гейтятся на impact-checks Phase 1
(финальный список — Task 1.8 inline).

Spec: 2026-05-24-legacy-direct-webhook-removal-design.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 15:05:19 +03:00
Дмитрий b103c8819c docs(billing): spec — remove legacy ProcessWebhookJob (direct webhook channel)
Pre-sharing-эпик legacy: ProcessWebhookJob + WebhookReceiveController +
POST /api/webhook/{token} + webhook_log/webhook_dedup_keys/tenants.webhook_token.
На проде 0 вызовов за всю историю. Не часть актуальной архитектуры каналов
(основной = шеринг crm.bp-gr.ru, резервный = CSV reconcile — оба уже на always-rub).

Удаление снимает блокер для Phase B Спека A (DROP COLUMN balance_leads),
закрывает публичный endpoint /api/webhook/{token}, убирает расхождение
биллинг-моделей в коде.

Approach: одним PR, одним релизом. Бэкап перед миграцией, 7д наблюдение.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 14:47:43 +03:00
Дмитрий 554b1f4aa3 docs(continuity): stages 2+3 router overhaul merged + warn-only active
active-projects.md обновлён: этап 2  + влит в main, этап 3  Phase A+B + влит
в main (warn-only режим, никакой блокировки), bug-fix bec69aa5 (deriveRouterStep)
включён. CHECKPOINT B накапливает реальные наблюдения; Task 9 (переключение
в enforce + 2 метрики) — отдельно после ревью baseline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 13:37:42 +03:00
Дмитрий d030dbbec4 Merge router discipline overhaul stages 2+3 into main
Brings in:
- Stage 2 (measurements + baseline, 11 commits): discipline-metrics + brain-retro
  переключён на реестр + STATUS.md "Метрики дисциплины" блок + baseline-snapshot
  docs/observer/baselines/2026-05-24-pre-enforcement.md.
- Stage 3 (enforcement infrastructure, 14 commits): router-classifier (Layer 1
  regex + Layer 2 Sonnet escalation), 3 хука (router-prehook/router-tool-gate/
  router-stop-gate) зарегистрированы в .claude/settings.json в РЕЖИМЕ WARN-ONLY
  (никакой реальной блокировки, только stderr-предупреждения).
- routerStep metric bug fix (bec69aa5, today): step выводится из наблюдаемых
  признаков через deriveRouterStep, а не читается как захардкоженная константа 1.

Gate mode = warn-only. Переключение в enforce — отдельным коммитом после ревью
накопленных данных (Stage 3 Task 9, CHECKPOINT B).

После merge:
- Router tools живут в tools/router-*.mjs (relative-path в settings.json).
- Этап 4 (cleanup устаревших правил наблюдения, classification-map deprecation
  → удаление) — не начат, планируется отдельно.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 13:35:50 +03:00
Дмитрий bec69aa565 fix(brain): derive routerStep from observable signals (was hardcoded constant)
Root cause: primary_rationale.step было жёстко прописано как литерал `1` в обоих
episode-builder'ах (observer-transcript-parser.mjs:813, observer-stop-hook.mjs:153).
Поэтому routerStepReached видел { '1': N } и suspicious=true для ВСЕХ данных —
показатель измерял константу, а не дисциплину роутера.

Фикс: новая чистая функция deriveRouterStep(primary_rationale) — берёт максимум
наблюдаемой стадии router-procedure.md из реальных признаков
(task_classification ≠ 'other' → 2; triggers_matched → 3; chain_ref → 4;
node_chosen ≠ 'direct' → 5). routerStepReached теперь вызывает её при чтении,
игнорируя хранимое pr.step. Это делает метрику честной для ВСЕХ существующих
эпизодов (включая исторические 136 за май) — без миграции данных.

Boost для baseline'а CHECKPOINT B этапа 3: на боевых данных
(131 schema-v2+ эпизод) distribution теперь = { 1: 55, 2: 46, 3: 12, 5: 18 },
suspicious=false. Видно реальную картину: ~42% эпизодов остановились на hard-floor,
только ~14% реально дошли до исполнения навыка.

Follow-up: episode-builder'ы продолжают писать step:1 (теперь это безвредно —
метрика игнорирует). Отдельно можно прибрать запись в builder'ах для
self-describing эпизодов.

Test changes:
- tools/discipline-metrics.test.mjs: +describe('deriveRouterStep') (9 cases),
  routerStepReached describe переписан под сигналы-источник.
- tools/brain-retro-analyzer.test.mjs: 'returns routerStepReached distribution'
  обновлён — эпизоды конструируются с сигналами (triggers vs bare),
  не хранимым step.

Full tools/ vitest run: 520/520 GREEN. 4 pre-existing empty test files
(ruflo-*, subagent-prompt-prefix) — не моя регрессия.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 13:25:05 +03:00
Дмитрий dd0ac43052 chore(observer): commit live router-classification episodes (stage 3 warn-only)
Эпизоды реальных сессий после hotfix — сторож пишет state + классификацию
на каждый промпт (verified: UUID-session state files). Данные для brain-retro
warn-only ревью.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:40:05 +03:00
Дмитрий 57bd85edc6 fix(router): prehook reads 'prompt' field + remove matcher from UserPromptSubmit (stage 3 hotfix)
Two real bugs found via verification (hook didn't fire in live session):
1. UserPromptSubmit block had matcher:"*" — event doesn't support matcher,
   non-standard block dropped (claude-code-guide authoritative). Removed →
   block now {hooks:[...]} like working observer-stop-hook.
2. stdin field was event.user_prompt; Claude Code sends event.prompt.
   Now reads (event.prompt || event.user_prompt) for compat.

Field-fix verified manually with real stdin shape {prompt:...} → #71 pdn-152fz.
Firing fix (matcher) NOT verifiable in-session (hooks load at session start) —
needs restart + next-turn state-file check.

NB stop-gate turn_events field also wrong (Stop sends transcript_path) — separate
follow-up, not on observation critical path (affects chain tracking only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:32:28 +03:00
Дмитрий ebca54f0fa feat(router): register 3 hooks in .claude/settings.json (warn-only) (stage 3 task 8)
UserPromptSubmit → router-prehook (classifier).
PreToolUse Edit|Write|MultiEdit|Bash → router-tool-gate (warn-only).
Stop → router-stop-gate (chain progress).

router-gate-mode.json = warn-only (outside repo, ~/.claude/runtime/).
Переключение в enforce — отдельным шагом после Checkpoint B (24ч наблюдения).

End-to-end smoke verified: «проверь пдн» → #71 pdn-152fz-audit,
warn-only пишет в stderr, не блокирует. Доменная разметка Task 1 работает.

NB активация в основной сессии: хуки в worktree-копии settings.json
версионируются; для реального наблюдения нужна либо merge ветки, либо
ручное применение к основному .claude/settings.json (warn-only безопасен).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:08:44 +03:00
Дмитрий 7c8223bf72 feat(router): Stop hook — chain progress tracking (stage 3 task 7)
После каждого хода обновляет state.chainProgress по реально вызванным
скилам. chainCompleted=true когда последний шаг достигнут.
skillInvokedThisTurn флажок для PreToolUse gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 11:07:20 +03:00
Дмитрий b4fb2cece9 feat(router-stage3): Task 6 — router-tool-gate PreToolUse hook (warn-only)
- tools/router-tool-gate.mjs: PreToolUse hook читает state из
  ~/.claude/runtime/router-state-<session>.json, решает block/proceed
  для Edit/Write/Bash (non-read-only). Escape hatch через HTML-тег
  <!-- routing: direct_justified=true reason="..." -->. Режим
  warn-only (default) / enforce через router-gate-mode.json.
- tools/router-tool-gate.test.mjs: 15 тестов GREEN (4 describe-блока:
  isReadOnlyBash / decodeRoutingTag / shouldBlock / decideDecision).
- CLI guard: fileURLToPath(import.meta.url) — Windows-cyrillic quirk.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 11:05:00 +03:00
Дмитрий 89441d95c3 feat(router): tune Layer 1 — глаголы + keyword>classification приоритет (stage 3 task 5b)
Подкрутка classifier'а БЕЗ правки реестра (доменная разметка Task 1 сохранена):
- TASK_TYPE_KEYWORDS +командные глаголы (проверь/составь/поправь/распиши/...);
  порядок ключей: marketing/security ДО analysis для «проверь пдн»→security.
- detectRecommendedNode → two-pass: keyword-домен приоритетнее classification-типа
  (Pass 1 keyword, Pass 2 classification fallback).
- MICRO_KEYWORDS +увеличь/уменьши/одну строку/bump.

Accuracy regex-only: 68.3% → 80.0% (type 55%→85%, micro 95%→100%, node 55%).
Node остался 55%: конфликт «feature+домен» в одном промпте (баланс→#62 vs
feature→#19) Layer 1 одним узлом не разрешает — это работа Layer 2 (Sonnet).
Ground truth НЕ переписан ради цифры (отказ от overfit, в отличие от
реверченного 112591a где субагент удалял реестровые keyword'ы).

489/489 tools GREEN.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:54:48 +03:00
Дмитрий bbe235b436 Revert "feat(router): tune Layer 1 — глаголы + keyword>classification приоритет (stage 3 task 5b)"
This reverts commit 112591a0da.
2026-05-24 10:53:14 +03:00