Commit Graph

1047 Commits

Author SHA1 Message Date
Дмитрий 7fa0747961 fix(observer): C5 coverage driven by hook registration, drop commit ratio (COV-1)
Bug: checkCoverage flagged anomaly when "recent commits > 0 AND episodes == 0".
Two design flaws, proven in this project:
- Wrong unit: commits = work-unit (one turn → many commits via subagent
  workflow); episodes = turn-unit. A 1023-vs-19 ratio is not anomalous, it's
  expected.
- Wrong window: the 14-day commit window predated the Stop-hook's existence
  (registered 2026-05-19). For 13 of 14 days the hook didn't exist — 889
  commits were structurally impossible to mirror as episodes.

Result: the C5 indicator was either always-red (flagging the hook's birth
as anomaly) or always-green (any episode count vs huge commit count = ok).
Either way uninformative.

Fix:
- checkCoverage(episodeCount, hookRegistered) — drops the commit param.
  Warn iff hook is registered AND 0 episodes this month → the hook is
  silently failing. If the hook isn't registered, 0 episodes is correct.
- runCoverageChecker derives hookRegistered from settings.json
  (isObserverStopRegistered helper) and passes it to checkCoverage.
  No more git execFileSync — pure fs.

Tests rewritten under the new contract: 7/7 (was 6, +1 drift-hazard guard
ensuring detail strings never mention "commit"). 15/15 coverage tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:07:58 +03:00
Дмитрий c2db9a9cfd feat(supplier): backfill project_supplier_links from legacy FK slots 2026-05-20 11:06:13 +03:00
Дмитрий 7fc9b5935e fix(observer): validate prompt_signal + events in appendEpisode (C-7)
V2_FIELDS list omitted prompt_signal and events — both are always produced
by parser and buildEpisodeFromContext, so the happy path is unaffected, but
a future ctx-fallback path that dropped them would silently write a
malformed episode. Add both to V2_FIELDS; appendEpisode now throws on either
being missing.

Tests: 2 new — appendEpisode throws when prompt_signal missing /
when events missing. 38/38 stop-hook tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:05:56 +03:00
Дмитрий 1d2d1ec6c9 fix(observer): exclude hot/normative files from causal chains (A-3)
Bug: findCausalChains flagged a chain whenever two episodes shared any
file. CLAUDE.md / MEMORY.md / STATUS.md / episodes-YYYY-MM.jsonl /
memory/*.md are touched by almost every turn (memory store, status
regeneration, normative-doc updates) — sharing them is not evidence of
causality, just baseline noise. Result: spurious chains on hot files
crowded out the genuine signal.

Fix: HOT_FILE_PATTERNS regex list + `isHotFile(path)` predicate. In
findCausalChains, filter hot files out of BOTH the errored-episode file
set AND the candidate-shared list. If only hot files were shared → no
chain. If a non-hot file is also shared → the chain stands and the
sharedFiles list contains only the non-hot ones.

Tests: 4 new cases — CLAUDE.md / memory/*.md / episodes/STATUS/MEMORY
sharing yields no chain; a turn sharing both CLAUDE.md AND /src/app.ts
yields a chain with sharedFiles=['/src/app.ts'] only. 33/33 analyzer
tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:04:59 +03:00
Дмитрий d8ab9df4b8 feat(supplier): Project<->SupplierProject belongsToMany via pivot 2026-05-20 11:04:24 +03:00
Дмитрий e0788a4675 fix(observer): infer blocked from unrecovered_error tail, not raw error/retry count (A-1)
Bug: inferOutcome flagged `blocked` whenever errorCount > retryCount across
the turn's events. But the parser emits an `error` event for ANY tool_result
with is_error=true — including expected failures: TDD failing-test-first,
grep returning nothing, git commands with intentional non-zero exit. On
TDD-heavy turns (project's standard discipline) this systematically marked
turns as blocked even when they ended on a successful tool_use.

Fix:
- Parser (extractProcessEvents): walk turn from end, find the LAST
  tool_result; if its is_error=true, emit a single `unrecovered_error`
  event. Distinguishes "turn ended on failure" from "errors recovered
  later". The original per-is_error `error` events remain (useful as raw
  factor signals).
- Analyzer (inferOutcome): replace `errorCount > retryCount → blocked`
  with `events.some(kind === 'unrecovered_error') → blocked`. Same
  ordering preserved (interrupt > blocked > rework/success/unknown).

Tests:
- Parser: emits unrecovered_error when last tool_result is_error;
  does NOT emit when turn ended on a successful tool_result;
  does NOT emit for turns with no tool_results.
- Analyzer: blocked iff unrecovered_error event present (not raw count);
  events=[error, error, retry] → success (no unrecovered_error).

142/142 vitest green (was 128).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:03:15 +03:00
Дмитрий ff50b30ab5 fix(observer): uuid-dedup in parseLines (C-1 root fix for quirk #101)
Bug: Claude Code's transcript JSONL file accumulates duplicated context-
rebuild snapshots — the same entry re-printed with the SAME `uuid`. Without
dedup, session_turn / task_size / events double-count, and session_turn
becomes non-monotonic across episodes parsed at different file-growth
states. Live evidence: episodes-2026-05.jsonl lines 14/15/16 of the same
session showed session_turn 139 → 140 → 91 (backwards in time). Probe
on transcript 553717ec: 22400 entries, only 6074 unique uuid (68% dup
rate); real user prompts 264 total vs 92 unique-uuid.

Fix: parseLines now tracks a `seenUuid` Set and skips entries whose uuid
has already been encountered (keep-first). Entries without `uuid`
(synthetic test fixtures) pass through unchanged. All downstream functions
(findTurnStart, extractEnvironment, extractTaskSize, etc.) operate on the
deduped entries array, so the fix is single-point and total.

Tests: new `parseTranscript — uuid-dedup` describe block covers
(1) duplicated-uuid prompts collapse → session_turn counts once,
(2) distinct-uuid entries preserved (no over-dedup),
(3) no-uuid entries pass through (synthetic-fixture safety),
(4) duplicated-uuid assistant turns → tool_calls / files_touched counted once.
110/110 parser tests green (was 106).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 11:00:50 +03:00
Дмитрий d70e776c3f feat(supplier): seed supplier_export_mode toggle (v8.26) 2026-05-20 10:59:27 +03:00
Дмитрий 8496b52bd1 fix(observer): narrow parallel_session detector to tool_result evidence (C-2)
extractEnvironment was scanning JSON.stringify(turn) for collision markers
(чужой staged / foreign git index / index.lock / another git process). Prose
mentions in user/assistant text flipped parallel_session=true. Live FP proven
on episodes-2026-05.jsonl line 20: my own analysis turn was non-parallel but
recorded parallel_session: true because the finding text mentioned the markers.

Fix: collectToolResultText(turn) — gather text only from tool_result blocks
(both string content and structured `[{type:text,text}]` arrays). Scan THAT
for collision markers; prose is no longer a signal.

Tests: rewrote `parallel_session narrowed` block — false on user/assistant
prose / no-tool-result turns; true on tool_result strings + structured form.
106/106 parser tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:58:37 +03:00
Дмитрий 9f8482277d feat(supplier): deals.subject_code from supplier tag (v8.26) 2026-05-20 10:57:04 +03:00
Дмитрий dbdb7232a0 feat(supplier): project_supplier_links M:N pivot (v8.26) 2026-05-20 10:54:50 +03:00
Дмитрий 189a42e665 test(supplier): isolate subject_code test (DatabaseTransactions+SharesSupplierPdo) 2026-05-20 10:48:32 +03:00
Дмитрий 7ec9dd6061 feat(supplier): supplier_projects.subject_code + per-subject unique index (v8.26) 2026-05-20 10:45:02 +03:00
Дмитрий fad03c736c chore(tools): worktree pre-commit gate runner (quirks #86/#97)
In a git worktree the shared .git/hooks/pre-commit cannot find lefthook on
PATH and silently skips every gate (pint/larastan/pest/gitleaks). This
script hardcodes the lefthook.exe + lefthook.yml paths from the main
checkout and runs `pre-commit` explicitly. Run before `git commit` inside
any worktree. Exit 0 = all gates passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:32:31 +03:00
Дмитрий d6cb977a81 fix(tests): ProjectFactory unique name — Str::random suffix (quirk #77)
fake()->unique() builds a fresh UniqueGenerator per definition() call, so
uniqueness is not guaranteed within a batch — names collided on the
(tenant_id, name) UNIQUE under pest --parallel. Append Str::random(8)
(62^8 ≈ 2e14 space) to eliminate the collision.

Verified: ProjectBulkActions 15/15 ×2 parallel runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:28:32 +03:00
Дмитрий e5883c4c18 docs(plan): Plan 4 (админка + ЛК) — переделка миграции проектов
5 TDD-задач: тумблер режима экспорта (endpoint + UI), экран «Проекты у поставщика»
(кто заказывал/дата последней поставки + bulk-delete бэк/фронт), ЛК require-region
UI-гейт + «Вся РФ» предупреждение/подтверждение, полная регрессия. Финальный из
4 планов эпика. +cspell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:09:26 +03:00
Дмитрий 8fb5a680e7 docs(plan): Plan 3 (экспорт + заказ) — переделка миграции проектов
8 TDD-задач: R-SAVE live smoke (гейт), SupplierExportMode тумблер, формула заказа
max(наиб,ceil(Σ/3)) + убран split, saveProjectMultiFlag R5/R6/R7 (захват 3 id),
SyncSupplierProjectsJob группировка источник×субъект + pivot, онлайн mode-aware
sync + grouping-хелперы, крон 18:00, регрессия. Третий из 4 планов. +cspell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:09:24 +03:00
Дмитрий 41a3ce5e80 docs(plan): Plan 2 (входящее распределение) — переделка миграции проектов
5 TDD-задач: RegionTagResolver (тег субъекта -> код, зеркало regions.ts),
LeadRouter на pivot без phone-фильтра, LeadDistributor cap=3 (seedable RNG),
RouteSupplierLeadJob (cap + deal.subject_code из тега), регрессия.
Второй из 4 планов эпика. +cspell. Реализация не начата.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:09:22 +03:00
Дмитрий 884f761183 docs(plan): Plan 1 (фундамент данных) — переделка миграции проектов
7 TDD-задач: supplier_projects.subject_code + per-subject unique (NULLS NOT
DISTINCT), pivot project_supplier_links (замена 3 FK-слотов), deals.subject_code,
seed supplier_export_mode, belongsToMany связи, backfill pivot, регрессия.
Первый из 4 планов эпика (см. spec §3). +cspell сид/бэкофилл. Реализация не начата.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:09:20 +03:00
Дмитрий ebbeab3a5e docs(spec): design — переделка миграции проектов + распределения лидов
Закрыты 5 под-вопросов brainstorming + P1 (Вся РФ = 1 пул + предупреждение
с подтверждением) + P2 (один связный spec). Ядро: 3-FK слоты -> M:N pivot,
per-субъект supplier_projects (subject_code), формула заказа max(наиб, ceil(Σ/3)),
cap=3 рандом из недобравших, ручной экран очистки в админке, режимы экспорта
online/batch (глобальный тумблер). R2 уже 18:00. R-SAVE = вариант а (дочитать
listProjects). Реализация не начата.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:08:31 +03:00
Дмитрий fad041f414 feat(map): finance-tooling — populate C6+C7 (+3 nodes, +7 edges)
+finance_plugin (C7+C6) / billing_audit (C6) / ru_tax (C7) + reuse secondary-
классификация (Boost/Pest/Larastan/Sentry/Redis/PM/data-scientist/operations/
process-*/context7) + NODE_DETAILS + NODE_META + версии-метки (pravila v1.34 /
claude_md v2.21 / psr_v1 v3.18 / tooling v2.18). JS-smoke: 137 nodes / 155 edges,
0 drift. ADR-012.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:54:25 +03:00
Дмитрий 2c673dd980 docs(router): finance-tooling routing rows + L13 chain (routing-off-phase v1.2)
+3 строки routing (#61 finance plugin / #62 billing-audit / #63 ru-tax-accounting),
связка L13 (финансовая цепочка C6->C7), scope §4.11->§4.38. router-procedure v1.1
changelog. ADR-012.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:51:28 +03:00
Дмитрий 7565e8e05b docs(normative): finance-tooling #61-#63 cross-ref version bump
Tooling Прил.Н v2.18 (§4.36/37/38 + §0 60->63 + 15-я подкатегория) +
PSR_v1 v3.18 (R10.1 Блок 1 +finance + note) + Pravila v1.34 (§13.2 +абзац) +
CLAUDE.md v2.21 (§3.3 +#61-63 + §0 cross-refs + §6 + §9).
Атомарный version-bump-набор (cross-ref-checker C2 STRICT: 0 drift). ADR-012.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:49:52 +03:00
Дмитрий e8a01f3d23 docs(adr): ADR-012 finance-tooling boundary C6/C7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:40:33 +03:00
Дмитрий 28ddd98893 feat(finance): ru-tax-accounting skill — РСБУ/НК РФ context C7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:37:52 +03:00
Дмитрий 2a73ee0cfe feat(finance): billing-audit skill — money invariants C6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:31:30 +03:00
Дмитрий fee8efc809 docs(finance): C6+C7 finance-tooling implementation plan
11 задач в 3 фазах: Ф1 billing-audit скил (C6), Ф2 finance plugin enable +
ru-tax-accounting скил (C7), Ф3 нормативка (Tooling/PSR/Pravila/CLAUDE) +
роутер (routing-off-phase L13 + router-procedure) + наблюдатель (9-атрибутные
блоки + C1/C2) + карта (+3 узла) + ADR-012 + push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:19:50 +03:00
Дмитрий 2ef20d7d48 docs(finance): C6+C7 finance-tooling epic design spec
Объединённый эпик «Финансы»: наполнение разделов карты C6 (биллинг/тарификация)
+ C7 (бухгалтерия/налоги). 3 новых узла (#61 finance plugin, #62 billing-audit,
#63 ru-tax-accounting) + reuse-классификация + расширенная нормативка
(роутер routing-off-phase.md + наблюдатель 9-атрибутные блоки) + ADR-012.
+9 терминов в cspell-words.txt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:11:07 +03:00
Дмитрий 84a56d449e docs(discovery): brief переделки миграции проектов + распределения лидов
Зафиксированы решения discovery-интервью 2026-05-20: два режима экспорта
проектов (онлайн + пакетный 18:00 МСК), один save с тремя флагами B1+B2+B3,
tag=регион, и новый алгоритм распределения лидов (cap=3 рандом из недобравших,
заказ = max(наиб_лимит, ceil(Σ/3)); группировка отменена). Реализация не начата.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 08:58:57 +03:00
Дмитрий 91030445fe feat(map): iter9 — brain governance subsystem (+9 nodes, +12 edges, +1 GREEN)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 05:12:24 +03:00
Дмитрий 02dc9a88c5 docs(map): iter9 implementation plan
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 04:59:29 +03:00
Дмитрий 0678d702e6 docs(map): spec self-review fix — edges 13->12
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 04:50:18 +03:00
Дмитрий 031f745c13 docs(map): iter9 brain-governance design spec
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 04:49:32 +03:00
Дмитрий c44299eac9 docs(etalon): refresh snapshot after supplier-migration-followup epic (HEAD 667f8fa→d24482c, demo re-seed, failover live-smoke) 2026-05-20 04:10:09 +03:00
Дмитрий d24482c123 docs(observer): sync spec §6 with as-built factor-analyzer
§6 drifted from the implemented brain-retro analyzer after Phase 1.2/1.3:
- factor matrix now lists 9 axes (session_turn + parallel_session were
  captured in the episode schema §3 but missing from the §6 matrix);
- outcome inference documents 'blocked' (error events > retry events) and
  notes 'failure' as deferred to the phase-2 agent-judge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:17:17 +03:00
Дмитрий a2e63d2076 fix(observer): brain-retro analyzer — blocked outcome + v1 filter + factors
P0.1b: inferOutcome emits 'blocked' when a turn had more error than retry
events (an unrecovered tool failure) — previously the enum value was dead.

P0.1c: 'failure' documented as deferred to the phase-2 agent-judge. It is a
judgment (work wrong AND never corrected), not deterministically recoverable
from a transcript; a wrong-then-corrected turn surfaces as 'rework'.

P1.1: analyze() drops v1 episodes (no schema_version 2) — they lack
environment/prompt_signal/decision_provenance and polluted the factor
matrix. Reports v1SkippedCount.

P2.1: session_turn (bucketed early/mid/late) and parallel_session added to
FACTOR_FNS — closes the schema↔matrix mismatch (both were captured in the
episode but absent from the factor axes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:40:44 +03:00
Дмитрий 30d7afc54e fix(observer): transcript-parser accuracy — session_turn + correction signal
P0.2: count session_turn from the last compaction. The transcript file
accumulates duplicated context-rebuild snapshots (quirk #101), so counting
real prompts from i=0 inflated it and made it non-monotonic. Now counts
"real prompts since the last compaction" — monotonic by construction.

P0.1a: widen the correction prompt_signal regex (не работает / сломал /
опять / откати / revert / still not / wrong / ...). The old regex was too
narrow, so rework outcomes were invisible to the factor analysis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:40:29 +03:00
Дмитрий 667f8fa114 docs(discovery): 3-tier failover live-smoke 2026-05-19 — all tiers green, 156/156 Supplier suite 2026-05-19 17:31:15 +03:00
Дмитрий 2012026637 fix(supplier): manage-project.js — drop wrong status-switch click + recon live-smoke
Task 4 live-smoke выявил: единственный .el-switch формы — include/exclude
регионов (regions_reverse), НЕ статус active/paused. Старый код кликал его
по dto.active → ошибочно ставил regions_reverse. Статус — дефолт портала
(active), UI-switch для него нет → switch-блок удалён.

recon-doc 2026-05-19-rt-project-form-locators.md: +секция Live-smoke
(domain-формат валидируется, multi-source save = N проектов, switch = regions,
type/tab re-render); row 6 исправлен.
2026-05-19 17:31:15 +03:00
Дмитрий 702bf22ac1 fix(supplier): manage-project.js robustness — conditional type/tab clicks + diag dump
Найдено при Task 4 live-smoke form-канала:
- type-select и вкладка «Список» кликались безусловно → re-click уже-активного
  значения ремоунтит Element UI tab-pane (textarea детачится). Теперь кликаем
  только при реальной смене значения + waitForTimeout после смены типа.
- defensive: проверка непустого textarea после fill content.
- diag: на status!=OK дамп фактически отправленного rt-project-save body в stderr.
2026-05-19 17:31:14 +03:00
Дмитрий c1af7ddcc2 fix(supplier): manage-project.js — text-only platform locator + exact endsWith URL match (reviewer Critical+Important) 2026-05-19 17:31:13 +03:00
Дмитрий 4c1f32419f feat(supplier): rewrite manage-project.js for Element UI + intercept rt-project-save response for external_id
- fillForm rewritten to label-for locators (.el-form-item:has([for="..."])) from recon 2026-05-19
- createOp: external_id from page.waitForResponse('rt-project-save') body, not DOM
- updateOp: same save endpoint intercept; row found by data-id or text
- listOp: Vuex state strategy 1, DOM scrape strategy 2, empty array fallback
- Known gaps (JSDoc + stderr warnings): workdays not in add-project form (portal default);
  regions require id->name mapping (skipped in Tier-2 MVP, logged to stderr)
- Test: HTTP fixture server serves rt-form-element-ui.html + handles /admin/visit/rt-project-save
- Fixture: .v-dialog--active wrapper + 10 .el-form-item (label[for=...]) + type-select popup in body

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:31:13 +03:00
Дмитрий b9fff59f9c docs(discovery): rt-project form locators recon (Element UI + Vuetify dialog, 10 fields) 2026-05-19 17:31:12 +03:00
Дмитрий 0c3ff35485 test(supplier): cover FailoverProjectChannel tier-3 escalation + transient bypass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:31:11 +03:00
Дмитрий 4d331457ea test(supplier): lock HTTP-200-without-Content-Type contract (no login-detect false-positive) 2026-05-19 17:31:11 +03:00
Дмитрий 39288a7152 feat(supplier): detect HTTP-200 HTML login page → force refresh+retry (defense-in-depth) 2026-05-19 17:30:54 +03:00
Дмитрий 49fc6ea580 docs(supplier): plan for migration follow-up — HTTP-200 login detect + form rewrite + 3-tier smoke 2026-05-19 17:29:52 +03:00
Дмитрий ac7017f405 fix(brain): redirect / to /docs/observer/dashboard.html (browser-smoke fix)
Browser smoke (Playwright) revealed that rewriting path internally without
changing the response URL left the browser's base URL as /, breaking
relative <script src="dashboard.js"> and ../automation-graph-data.js
references. 302 redirect makes the browser settle on /docs/observer/,
which resolves the relative paths correctly. All 4 views verified clean
(0 console errors). Screenshots: brain-dashboard-{map,replay,feed,aggregate}-view.png.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:23:52 +03:00
Дмитрий f1ec324eed feat(brain): Forest polish + observer README entry for the dashboard 2026-05-19 16:23:52 +03:00
Дмитрий 8f3c24af76 feat(brain): conflict three-layer panel (design / friction / correlation) +3 tests 2026-05-19 16:23:51 +03:00