# Аудит мета-информации в событиях (t-261015083454, принцип m-516174369) Дата: 2026-10-15. Стор: ~2.8M событий. Принцип Alex: **события должны сохраняться с максимумом мета-информации** — событие без контекста теряет ценность при разбирательстве инцидентов и аналитике burn/AUM. ## 1. Системные поля (у всех событий, таблица `events`) | Поле | Есть | Оценка | |---|---|---| | `id`, `timestamp` (UTC ISO), `version` | ✅ | ок | | `aggregate` / `aggregate_id` | ✅ | ок, но двойная кодировка меты (см. §3) | | `sig` / `key_id` / `prev_sig` (ed25519-цепочка) | ✅ | криптоаудит t-260930083716 | | `trace_id`, `level`, `источник`, `context` | ❌ в payload | точечно, неравномерно | ## 2. Аудит payload по типам (выборка последних 150 на тип) | Тип (кол-во) | trace_id | level | ts | источник | прочая мета | |---|---|---|---|---|---| | `CycleTicked` (1.35M) | — | ✅ agg_id | — | — | seq | | `ReflexObserved` (1.33M) | — | ✅ agg_id | ✅ | — | load1 | | `ToolCalled` (34.7k) | **❌** | **❌** | **❌** | — | tool, input | | `ToolResult` (34.7k) | **❌** | **❌** | **❌** | — | tool=**«?»-заглушка**, result | | `BudgetSpent` (28.6k) | ✅ | ✅ | — | ✅ | model, tokens | | `DigestWritten` (21.3k) | — (summary_ref) | ✅ | — | stub-флаг | period, text | | `InvocationStarted` (1.4k) | ✅ | ❌ | ✅ | — | model | | `TaskSubmitted` (230) | — | — | — | ✅ source | id, size, est_minutes | | `TaskClaimed` (434) | — | ✅ | — | — | session_id | | `TaskCompleted` (317) | — (result_ref) | **❌** | — | — | answer | | `TaskFailed` (12) | — | ×2/12 | — | ×1/12 | error | | `EscalationRaised` (96) | — | ✅ from/to | — | ✅ source | severity, task_id, context | | `MessageReceived` (1238) | — | — | ✅ date | ✅ username | 19 полей, эталон | | `MessageTranscribed` (65) | — | — | — | ✅ engine | transcript | | `RagQueried` (412) / `PortalMcpCalled` (64) | ✅ | ✅ | ✅ ms | — | query/result_preview | | `CycleError` (51) | — | ✅ | — | — | error, trace | | `AumMeasured` (16) | — | — | ✅ | — | aum, burn_24h/7d, policy | ## 3. Найденные пробелы (по убыванию веса) 1. **`ToolCalled`/`ToolResult` — 68k событий без `trace_id`/`level`/`ts`** (⅔ всех нетаймовых событий стора; связь с тиком только через декодирование `aggregate_id = "-:"`). У `ToolResult` имя инструмента — заглушка `"?"` (после параллельных вызовов вообще непонятно, чей результат). 2. **`TaskCompleted`/`TaskFailed` без `level`** — не видно, какой уровень лестницы закрыл/уронил задачу; `trace_id` только как `result_ref`. 3. `DigestWritten.summary_ref` ≠ trace_id напрямую — это trace_id тика, но имя не говорит об этом (путаница при разборе инцидентов). 4. `BudgetSpent` без `ts` (после миграции v2 на инкрементальные записи — только `timestamp` колонки; хронология внутри тика не видна). 5. Двойная кодировка: `level` дублируется в `aggregate_id` и payload — данные об оси «уровень» живут в двух местах с разной полнотой. ## 4. Внедрено в этом тике (коммит f3ab66e) - **`harness/runtime.py` `_wire_trace_hooks`**: `trace_id`, `ts`, `level` теперь в каждом trace-событии (`InvocationStarted`/`ToolCalled`/ `ToolResult`); `ToolResult` дополнительно `duration_ms` (из `AfterToolCallEvent.duration`), `ok: bool`, `exception` (до 500с), имя инструмента из `tool_use` (заглушка `"?"` убрана). - **`harness/_llm_worker.py`**: проброс `level` в `_wire_trace_hooks`. - **`harness/scheduler.py`**: `TaskCompleted`/`TaskFailed` получают `level` + `trace_id` тика-воркера. - Проверено: смоук-скрипт (мета во всех 4 типах), 5 смежных тестов PASS (task_batch 11, timeout_kill 2, ttft_retry, tool_budget, task_claim_race). Прирост мета-покрытия: trace-события (доля LLM-активности без мета- контекста) — с **0% → 100%** новых событий; TaskCompleted/TaskFailed — с 0% → 100% `level`. ## 5. План на следующие итерации (не внедрено — вне бюджета тика) | # | Что | Зачем | Где | |---|---|---|---| | 1 | `ts` в `BudgetSpent` (воркер уже пишет после каждого вызова) | хронология spend внутри тика, детект всплесков burn | `_llm_worker._emit_budget_incremental` | | 2 | `trace_id` в `DigestWritten` (рядом с `summary_ref`) и переименование семантики `summary_ref` в доках | единая ось «тик → трасса → spend» | `scheduler.py` | | 3 | `source` (`cli`/`poller`/`scheduler`/`daemon`) в `TaskClaimed`, `MessageSeen`, `ReplySent` | отличать ручные команды от автоматических | `scheduler.py`, `commands.py` | | 4 | `host`/`pid`/`commit` в `InvocationStarted` (или в `CycleTicked` раз в тик) | мульти-инстансность: кто именно писал (инцидент rogue-key t-261008084600) | `runtime.py` | | 5 | `duration_ms` в `CycleTicked`/`DigestWritten` | находить медленные тики без парсинга логов | `scheduler.py` | | 6 | Унификация: helper `es.meta(level=…)` для единообразной меты во всех `append_event` | убрать ручную возню, единый контракт | `es.py` | Ретро-заполнение старых событий **не делаем**: стор append-only + подписи ed25519 — перезапись сломает тампер-цепочку. Граница: события до f3ab66e — «до мета-стандарта», после — полные. ## 6. AUM-фильтр Аудит меты — инвестиция в долгосрочную базу: разбор инцидентов (часы диагностики → минуты по SQL-запросу), честный burn-контроль 9router (193.68 у.е./7д — прямой минус AUM), доверие ко стору у внешних проверяющих (подписи + мета =аудито-готовность). Слабого короткого дохода нет — чистый плюс в базу.