8 задач: baseline → таблица-замок supplier_lead_deliveries → раздача
по клиентам (LeadRouter DISTINCT ON) → удаление DuplicateDetector из
обоих джобов → замок insertOrIgnore → тесты (model-agnostic) → регрессия.
Вариант B. Заякорено на always-rub LedgerService (Спек A в origin/main).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Правило: дедуп — ответственность поставщика; Лидерра телефон не фильтрует,
берём за всё, что прислано. Защита от своих дублей — на уровне БД
(замок supplier_lead_deliveries, ключ по поставке, не по телефону).
Лимит шеринга = 3 разных клиента, одному клиенту одна копия.
Вариант B (железобетонный) утверждён на брейнсторме 23.05.2026.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
status-md-generator рендерит блок «Активные многоэтапные проекты»
из repo-local docs/observer/active-projects.md (если файл есть).
renderStatus backward-compatible: без activeProjects блок пустой.
active-projects.md — single source состояния многоэтапного router
overhaul (этап 1 ✅ закрыт, этапы 2-4 pending). Будущая сессия видит
статус в STATUS.md dashboard + memory tracker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Структура узла (9 полей + 3 trigger типа + 3 boundary типа), status
маппинг, процесс добавления узла, auto-render, lefthook gate, cross-refs
на spec/plan/Pravila/ADR-011/router-procedure/routing-off-phase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-region <!-- auto:tooling-registry-summary --> в docs/Tooling_v8_3.md
обновлён рендером из docs/registry/nodes.yaml (3 → 83 строки).
routing-off-phase auto-region остался без изменений: routing-table
выводит только classifications, а в реестре их два узла (#18 Pest + #19
Superpowers, 5 строк). Keyword-based routing — отдельная задача
(этап 3, классификатор).
Diff: +80 строк строго внутри маркеров auto-region. --check mode прошёл.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tooling §3.5 line 332 содержит опечатку «лит» вместо «линт» —
буквальный перенос в Task 8a сделал keyword мёртвым (роутер
не сработает на «линт миграций»). Реестр приоритезирует
функциональность над faithful copy.
Tooling §3.5 будет починен отдельной задачей при следующем
auto-rerender (Task 10).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
renderAll() режим без --check переписывает файлы; с --check
возвращает exit 1 на drift (для lefthook).
Сейчас рендерит 2 региона: Tooling summary + routing-table.
Маркеры в Markdown добавим в Task 6 (без них скрипт корректно падает
с понятной ошибкой Markers not found).
Fix: entry-point guard использует fileURLToPath+resolve вместо
string-замены — совместимость с кириллическими путями (Windows quirk).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Покрытие: индексация по classification/keyword, exclude
historic/dormant из индексов, cache lifecycle, schema violation,
chain membership lookup.
Все 11 GREEN на пилотном реестре из 3 узлов.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Экспортирует loadRegistry/findByClassification/findByKeyword/
findActiveNodes/findChainsByNode + clearCache для тестов.
Кэширует в module-scope (per-process); валидирует через ajv при
загрузке (schema + ajv-formats). Keyword индексация case-insensitive
(.toLowerCase()) для последовательности с findByKeyword.
Тестов нет — Task 4.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
I-1: weight range 0-1 added to classification + file_pattern trigger
variants (раньше было только на keyword) — иначе weight=50 silent
ranking-bug в Task 3 indexByTrigger.
I-2: additionalProperties:false на 3 trigger-variant объекты — ясная
ajv-ошибка при mixed-key (keyword+classification одновременно).
I-3: additionalProperties:false на definitions.node и definitions.chain
— typo ("categori" / "keywrod") теперь reject'ится, не silently
accepted.
Smoke-проверка: 3 теста — weight=50 reject, typo categori reject, valid
accept. ajv compile OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema поддерживает: id/name/slug/category/status, триггеры трёх видов
(keyword/classification/file_pattern), границы (adr/pair), членство в
цепочках L1-L16, dormancy/deferred-статус.
README — заглушка, наполнится в Task 13.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final-review followup. .claude/settings.json uses regex-style combined
matchers like "Edit|Write"; transcript writes per-tool PreToolUse:Edit.
Split on | when building map so per-tool counts resolve. Also sync
spec doc loadHookMap -> buildHookMap (impl name).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aggregation-template.md gets two new sections (Hook script breakdown,
Recommended-node candidates) + paragraph in Missed Activations.
factor-analysis spec gets a v3 amendment cross-ref to the 2026-05-23 spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors missed-activations dormancy logic (id === false strict).
First live recommended node from classification-map, else null.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review followup. TOOL_SCRIPT_RE didn't include \ in delimiter
char class — Windows-native commands like `node tools\foo.mjs` fell
through to inline:<sha> fallback. Added \ to char class + inner
[\/\] alternation, normalize match to forward-slash.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure module. buildHookMap(project, user) reverse-lookup settings.json,
resolveScriptCounts duplicates counts per script. No exec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec terminology aligned with codebase: recommended_skill →
recommended_node (classification-map хранит Tooling IDs `#NN`, не имена
skill'ов). Test runner — vitest (npm run test:tools), не node --test.
Missed-activations filter тоже поднимается до >=2.
5 atomic TDD commits: hook-resolver, recommended-node, parser+smoke,
analyzer factor-axis, brain-retro template.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Forward-only расширение episode schema: hook_fired.scripts (reverse-lookup
.claude/settings.json → имена хук-скриптов рядом с matcher-counts) +
primary_rationale.recommended_skill для direct-эпизодов (из
classification-map). Analyzer фильтр >=2 для backward-compat с v2.
Связано: ADR-011, factor-analysis spec 2026-05-19, Pravila §16,
feedback_feature_via_writing_plans.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: gitleaks (rule `ru-phone-unmasked`) caught `79135191264` in 3 lines
of docs/observer/episodes-2026-05.jsonl during brain-retro #3 push
(963379c3). Stop-hook PII-filter was not masking bare-format Russian
phone numbers (without the `+` prefix).
Root cause:
const RU_PHONE = /\+7\d{10}/g; // requires literal '+7'
Free-text observer episodes captured phone `79135191264` in field-value
context (`call client 79135191264` / `phone 79135191264 in payload`),
slipping past the existing filter.
Fix:
const RU_PHONE = /(?:\+7|\b7)\d{10}/g;
The `\b7` branch catches bare format with a word-boundary on the left,
avoiding false-positives inside long digit sequences (timestamps, IDs,
hashes). False-positive guard verified via test:
'id 1796133619135191264999 not a phone' → unchanged.
TDD cycle:
- RED: 3 new tests + 1 sanitizeWithCount test (4 fails on bare phone)
- GREEN: regex extended, 24/24 file tests pass, 373/373 full tools
suite GREEN (0 regressions across 18 files).
Cleanup: applied sanitize() to docs/observer/episodes-2026-05.jsonl;
11 lines touched (3 phone-leak lines + 8 with other PII patterns).
gitleaks now finds 0 leaks in the file.
Pravila §5.2 (no PII in commits) + 152-FZ (phone is regulated PD).
Closes DO-PII-1 (see memory observer-pii-leak-2026-05-23).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brain-retro #3 за весь май 2026 — 116 v2-эпизодов / 61 task_ref.
Здоровье: 0 observer_error, 1.7% correction-rate, 19 skill-инвокаций
(vs 6 в ретро #2 — рост в 3×).
Применены 4 кандидата по явному «делай» от заказчика:
A1. observer-classification-map.json: question → [] (был ["#60"])
Разговорные RU-вопросы давали 17/40 false-positive промахов против context7.
A2. observer-classification-map.json: memory-sync → [] (был ["#33"])
#33 claude-md-management — канал ТОЛЬКО для CLAUDE.md (Pravila §5 п.10),
не для memory/*.md. Давало 8/40 false-positive.
B1. Tooling §4.8 #34 Sentry MCP — boundaries +DEFERRED
Sentry instance не задеплоен (pending Б-1). Двойной сигнал
extractor'а → .node-dormancy.json[#34] = true.
D1. memory/feedback_feature_via_writing_plans.md (user-memory вне git).
Effect: missed-activations 40 → 15 после очистки шума. Из 15 реально
значимы 2 эпизода (audit-journaling closure 116 tools без writing-plans;
SyncSupplierProjectJobTest planning без skill). Остальные 13 — шум
классификатора на правках своих документов.
+cspell-words.txt: 20 слов (9 секций Tooling + 11 из retro-note).
NB: docs/observer/episodes-2026-05.jsonl снят со staging — gitleaks
обнаружил 3× RU-phone leak (`ru-phone-unmasked` rule). Это сигнал что
observer PII-фильтр пропустил телефон в free-text record — отдельный
follow-up (PII фильтр Stop-хука).
Retro-отчёт: docs/observer/notes/2026-05-23-brain-retro.md.
STATUS.md перегенерирован.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Раньше чтобы убрать один регион из выбора, приходилось сбрасывать все
и выбирать заново. Добавлен closable-chips на v-autocomplete регионов в
трёх местах: карточка создания проекта (NewProjectDialog), панель
редактирования (ProjectDetailsDrawer) и массовое изменение регионов
(RegionsBulkDialog). Теперь у каждого чипа есть крестик.
Покрыто Vitest: closableChips=true на каждом селекторе.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
20 cron/job classes analyzed against RLS-protected tables. 4 GAP findings (P1):
RemindersDispatchDue, ReportsCleanupExpired, GenerateReportJob,
ProcessWebhookJob::failed() — all touch RLS tables on default conn in cron/queue
context (no tenant GUC). Fail/silent on prod (crm_app_user), hidden on dev
(postgres superuser). Phase B fixes follow.
Worker раз в час штатно выходит по --max-time=3600 с кодом 0 (success);
Restart=on-failure такой выход НЕ перезапускает -> очередь умирала после первой
пересменки (инцидент 22.05.2026 17:03 -> простой 12ч, обнаружен 23.05 при QA).
Защита от краш-шторма сохранена (StartLimitBurst=5/300s + OnFailure).
Применено на боевом liderra.ru (основной unit, drop-in restart.conf удалён).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
lefthook 2.1.x не завершает pre-commit при git commit на пути
"C:\моя\проекты\портал crm\Документация" (кириллица+пробел): проверки
проходят, но движок виснет на git stash/index.lock и плодит node-зомби.
Решение (выбор заказчика «свой простой скрипт»):
- tools/git-hooks/pre-commit.sh — нативная замена, зеркалит джобы lefthook.yml
(gitleaks/markdownlint/cspell/stylelint/pint/larastan/squawk/eslint), но
вызывает инструменты напрямую (node <entry>, не npx) и НЕ модифицирует index
(нет git add/--fix) → нет конфликта за .git/index.lock. Явный exit.
- .git/hooks/pre-commit (локальный, не в git) → диспетчер на этот скрипт.
- lefthook.yml: npx→node в md/cspell/stylelint джобах + убран stage_fixed
(markdownlint/pint) — кросс-платформенно безопасно, для CI/Linux где lefthook
работает штатно (lefthook.yml остаётся источником истины конфигурации).
- lefthook 2.1.6→2.1.8.
post-commit (status-md) и pre-push lefthook работают штатно — не трогаю.
Bypass: LEFTHOOK=0 git commit ...