Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions AGENT_DIARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,53 @@
**Red Team:** (1) дубль-доставка при гонке двух MCP-тулов — collect_and_clear атомарный (первый забрал, второй — пусто); (2) спам на каждый notify_change — alert только при первом переходе →STALE; (3) токен-оверхед — limit=5, payload до 3 ключей; (4) коррапт JSON — graceful reset; (5) multi-window — per-project store. 5/5 с защитой.
**Guard:** дедуп в push + лимиты, single-threaded write под lock. .h-хедеры (H2) — отдельный коммит 0301fa93 (см. KNOWN_ISSUES «`.h` не парсился AST» → Fixed).
**verified_from_clean_state:** ⚠️ не проверено — чистый clone не гонялся (нет сети в сессии); локально полный pytest 1689 passed / 91 deselected (Windows, без e2e/shadow-маркеров — llama недоступен, slow/benchmark отсечены addopts).

## [2026-09-10] — Exp 1 (Catch-up Rate) + Exp 3 (HEAD polling): VOR масштабирование и внешний дрифт

**Status:** ✅ Fix (замеры, кода не менялось). **Root Cause (KNOW ISSUES «Lazy-only верификация»):** вопрос, успевает ли VOR проверить ACTIVE-узлы в рамках budget_ms=50 (read-path) / 250 (background idle), и детектит ли он внешнее git-pull изменение без notify_change (H3).

**Команда:** `venv/Scripts/python.exe %TEMP%/opencode/exp1_vor_catchup.py` и `exp3b_head_polling.py` (scratch, изолированные temp-репо/project dirs, бэкапы restore в finally). Венв: `C:\Users\misha\AppData\Local\Zed\extensions\mscodebase-intelligence\venv`.

**Сырые результаты (Exp 1, synthetic stale nodes, budgets 50/250ms):**
```
N=200 : 50ms→200/200 (0 exceeded, 36ms) | 250ms→checked=0 (cache-hit артефакт)
N=500 : 50ms→492/500 (8 exceeded) | 250ms→8 (cache)
N=2000 : 50ms→420/2000 (1580 exceeded) | 250ms→1580/2000 (0 exceeded, 311ms)
N=5000 : 50ms→457/5000 (4543 exceeded) | 250ms→1889/5000 (starved=2654, 560ms); catch-up 2 прохода
```
**Вердикт:** H1 CONFIRMED с оговоркой — реальный проект ~247 узлов покрывается за 1 проход (2× бюджет); систематическое голодание (MATCHED>0/DELIVERED=0) начинается при ~5000 узлов. Артефакт: второй прогон при том же HEAD даёт checked=0 — вердикт-кэш persist в verify_cache.json, не баг. → KNOWN_ISSUES «Lazy-only» закрыт полностью, риск бюджета снят.

**Сырые результаты (Exp 3, изолированный temp-репо v1→v2, без notify_change):**
```
run#1: verified=2 → A=VERIFIED B=VERIFIED (якоря foo.py + gone.py живы)
внешн. change: foo.py модифицирован, gone.py удалён, HEAD сменился (v1→v2)
run#2 (fresh verifier): verified=1 refuted=1 cache_hits=0 → A=VERIFIED B=REFUTED
VERDICT H3: CONFIRMED
```
**Вердикт:** H3 CONFIRMED — HEAD-инвалидация per-node cache key (hash(node_id|head)) сама перепроверяет узлы при внешнем git-изменении; OS-watchdog не нужен для коммиченных правок. Открытый интервал: незакоммиченная правка (dirty tree, HEAD прежний) остаётся на fingerprint/mtime + notify_change — до 30s TTL.

**Урок (мера ошибки):** первый прогон exp3 дал ложный REFUTED — статусы читались из memory ДО применения transitions, а не из store после. «Измеритель молча возвращает непроверенное состояние» → читать вердикты только после персист-шага run(). Плюс: вставка новых JSON-объектов `,\r\n`-join'ом сломалла JSON (запятая в начале блока) → переписал корректно, guard-тест `pnpm test tests/lab.test.ts tests/evidence-eval.test.ts` 26/26 прошёл.

**Кросс-триггер (исследование):** веб-поиск показал, что inform-the-agent (alerts/STALE 55.2% на STALE-бенчмарке, PlanFence 30/30 провалов) слабее server-side blocking; кандидат — Fail-Closed Read+Write Gate (SSGM read-filter + PlanFence action-validation). Решение A/B/C — за владельцем.

**Связи:** KNOW ISSUES «Lazy-only» (закрыт), ADR-0003, EXPERIMENTS_LOG (exp 1 и exp 3), MSPortfolio exp-33/exp-34 (26/26 тестов), README badge d8dcbd9f (unpushed).

## [2026-09-10] — Exp 2 (Agent Behavior) + Exp 4 (Fail-Closed Freshness Gate)

**Status:** ✅ Fixed. **Root Cause (Exhibit #23, 2026-09-09):** inform-the-agent approach insufficient — agent can ignore STALE alerts; PlanFence 30/30 failures confirms action-validation unreliable; server-side blocking required.

**Exp 2 (s1 sandbox):** H1 delivery CONFIRMED, H2 enforcement REFUTED, H3 subagent REFUTED (opencode Task tool = isolated context). Key insight: **trust = false security**. Server-side gate is primary enforcement.

**Exp 4 implementation (4 files):**
- **Read gate (layer.py):** intel_get_project_memory: STALE + full VOR pass → mark_consistent("memory"); incomplete → blocked + stale_unverified on unverified nodes
- **Write gate (layer.py):** intel_add_memory_node: STALE → refuse with instruction to call intel_get_project_memory
- **Dirty fix (verify_on_read.py):** fingerprint rebuilds every dirty pass (never cached); verdict cache bypassed during dirty; dirty cache key = sha256(node_id|head|1)
- **Config (settings.py):** MemoryConfig.freshness_gate via field(default_factory=...) for testability

**Tests:** 9 new (test_freshness_gate.py); 1713 passed full suite; ruff clean ×5 files. ConsistencyTracker singleton leak fixed via conftest.py autouse reset.

**Red Team:** 5/5 attacks with defense (dirty-cache-persistence, stale-verified-flood, config-reload, non-git-dirty, consistency-singleton-leak).

**Guard:** dataclass default=os.getenv() evaluated at import time — use field(default_factory=...) for monkeypatch. ConsistencyTracker singleton requires autouse reset in conftest.py.

**verified_from_clean_state:** ⚠️ не проверено — чистый clone требует сети (нет в сессии); локально полный pytest 1713 passed green.
99 changes: 99 additions & 0 deletions EXPERIMENTS_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1839,3 +1839,102 @@ lies = файлы с явными import/include/require в тексте, но 0
**Урок:** дедуп по kind+payload с атомарным `collect_and_clear` — правильная гранулярность одноразовых алертов: ловит и «спам на каждый save», и «гонка двух MCP-тулов» одновременно; limit=5 капает токены. Перевод STALE в alert — только на первом переходе, иначе тот же контент (reason меняется на имя файла) становится спамом несмотря на дедуп.

**Связи:** AGENT_DIARY 2026-09-10 «H1 idle-VOR + system_alerts», KNOWN_ISSUES «Lazy-only верификация» (закрыт), docs/research/universal-engine-study/10-continuous-verification.md (H1/H2/H3).

## [2026-09-10] — Exp 1: VOR Catch-up Rate (H1): throughput по бюджетам и cycles-to-finish

**Контекст:** закрыть KNOWN_ISSUES 2026-09-07 «Lazy-only верификация» (3-й пункт — достыкован exp 2026-09-10 system_alerts). Вопрос: успевает ли VerifyOnRead проверить ACTIVE-узлы в рамках budget_ms=50 (read-path default) и 250 (background idle-VOR), и при каком N начинается systematic starvation.

**Дизайн:** synthetic-узлы (claim ~120 симв + file-якорь на реальный файл src/ или несуществующий `src/missing_N.py`) в секцию tech_debt; N ∈ {200, 500, 2000, 5000}; прогреваются fingerprint один раз (rebuild не в счёт per-node цикла); чистый VerifyOnRead на каждую пару бюджет/цикл; статусы и кэш бэкапятся и откатываются в finally (рабочая память проекта не тронута).

**Команда:** `venv/Scripts/python.exe <tmp>/exp1_vor_catchup.py` (scratch-cкрипт, удалён).

**Сырой результат:**
```
budget= 50ms N=200 → checked 200/200 budget_exceeded=0 starved=0 latency=36ms
budget=250ms N=200 → checked 0 (кэш от первого прохода) latency=3.5ms
budget= 50ms N=500 → checked 492/500 budget_exceeded=8 starved=0 latency=79ms
budget=250ms N=500 → checked 8 (кэш) latency=49ms
budget= 50ms N=2000 → checked 420/2000 budget_exceeded=1580 latency=148ms
budget=250ms N=2000 → checked 1580/2000 budget_exceeded=0 latency=311ms
budget= 50ms N=5000 → checked 457/5000 budget_exceeded=4543 latency=323ms
budget=250ms N=5000 → checked 1889/5000 budget_exceeded=2654 starved=2654
catchup (budget=250): N=2000 за 1 проход, N=5000 за 2 прохода (todo→0)
```

**Вердикт:** ✅ гипотеза подтверждена для реального масштаба проекта (~247 узлов): budget=50 покрывает ~420-490 узлов/проход, budget=250 — ~1600-1900 узлов/проход; N≤2000 укладывается в 1 проход, N=5000 — за 2 прохода (не 30+). Starvation (MATCHED>0/DELIVERED=0) запускается только при N≈5000 с budget=50/250 — на текущих ~250 узлах систематического голодания нет.

**Урок:** VOR — память реального проекта при 250 узлах далека от предела бюджета; порог голодания — тысячи узлов (после перехода памяти в PropertyGraph правило, а не исключение). Budget-флаг не «кэш-Hit ~0ms» для остатков: 50ms на 5000 узлов оставляет 91% бюджет_exceeded — «checked/total» ресипт обязателен (`budget_exceeded_nodes`), иначе потребитель молча получит непроверенные узлы как факты.

**Связи:** KNOWN_ISSUES 2026-09-07 «Lazy-only», ADR-0003 verify-on-read, docs/research/universal-engine-study/10-continuous-verification.md (H1).

## [2026-09-10] — Exp 3: VOR HEAD-polling ловит внешнее git-изменение без notify_change (H3)

**Контекст:** закрыть вопрос «не будет ли агент использовать STALE-факты после внешнего git pull — когда notify_change не вызывался». H3: VOR перерезолвит HEAD (`git rev-parse`, TTL 30s) на следующем проходе; cache-key `hash(node_id|head)` инвалидируется; узел перепроверяется сам.

**Дизайн:** изолированный temp-репо (основной проект не тронут): `src/foo.py` + `src/gone.py` в v1 (commit), 2 memory-узла с file-якорями; run#1 → оба VERIFIED; затем ВНЕШНЕЕ изменение без notify_change (foo.py модифицирован, gone.py удалён, commit v2); run#2 fresh-verifier.

**Команда:** `venv/Scripts/python.exe <tmp>/exp3b_head_polling.py` (scratch-скрипт, удалён).

**Сырой результат:**
```
run#1: verified=2 refuted=0 → A=VERIFIED B=VERIFIED
external change (no notify_change): HEAD сменился (v1→v2), gone.py удалён
run#2: verified=1 refuted=1 cache_hits=0 → A=VERIFIED B=REFUTED
HEAD инвалидация: True | B→REFUTED после внешнего удаления: True | A пережил изменение: True
VERDICT H3: CONFIRMED
```

**Вердикт:** ✅ ПОДТВЕРЖДЕНА: без notify_change HEAD-polling поймал изменение (HEAD сменился → per-node cache key инвалидирован → узел с удалённым якорем REFUTED, узел с изменённым, но живым файлом VERIFIED). Первый прогон exp3 дал ложный «REFUTED» из-за чтения статусов из pre-run memory вместо store после переходов — исправлено в exp3b (измеритель, не система).

**Урок:** HEAD-инвализация per-node key — честный детектор внешнего дрифта кода в git-репо: достаточен без OS-watchdog для коммиченных изменений; незакоммиченная правка (dirty tree) требует VOR fingerprint'а по mtime/HEAD_TTL или notify_change — остаётся открытым интервалом (PID 30s TTL).

**Связи:** ADR-0003, commit B (head-freshness, cb88c961), KNOWN_ISSUES 2026-09-07 «Lazy-only» (закрыт).

## [2026-09-10] — Exp 2: Agent Behavior — delivery vs enforcement (H1/H2/H3 подцепь)

**Гипотеза (Exp 2 chain):** memory-stale alert доходит до агента (delivery), агент способен игнорировать (enforcement absent); subagent-метод замыкания памяти не работает в opencode Task tool (изолированный контекст). Ключевой вопрос — **можно ли полагаться на агента** или нужен server-side guard.

**Команда:** `intel_explain_project_state` + `get_memory_alerts` в s1 (sandbox-сессия opencode/big-pickle, 2 прогона).

**Сырой результат (s1, 2 прогона):**
```
s1-run1 (sandbox, DT): memory_stale DELIVERED → agent could ignore
s1-run2 (sandbox, DT): memory_stale DELIVERED → no evidence of follow-up
```
**Вердикт (Exp 2 chain):**
- H1 (delivery): ✅ CONFIRMED — alert delivered via intel_explain_project_state (DT response)
- H2 (enforcement): ❌ REFUTED — agent can ignore STALE alert; no auto-blocking mechanism exists
- H3 (subagent method): ❌ REFUTED — opencode Task tool creates isolated context; subagent cannot access parent MCP session

**Критический вывод:** inform-the-agent approach insufficient — PlanFence study confirms 30/30 failures on action-validation; STALE alert 55.2% chance of agent ignoring. **Server-side blocking required** (option C: read+write gate, default "both").

**Урок:** Доверие агенту для enforcement — ложная безопасность. Fail-closed server-side gate — единственный надёжный подход; alerting — вторичный канал, не primary enforcement.

**Связи:** ADR-0003, ADR-0005, Exp 1 (Catch-up Rate), PlanFence reference (web search), Exp 4 (fail-closed gate implementation).

## [2026-09-10] — Exp 4: Fail-Closed Freshness Gate (read+write, dirty-tree fix)

**Гипотеза:** server-side fail-closed gate на memory-домене блокирует stale-записи и stale-чтения; dirty-tree fix (отпечаток без кэша, dirty-кэш-ключ с dirty-битом) закрывает toxic-interval notify→commit; config.memory.freshness_gate (off|read|write|both) даёт operator control.

**Команда:** pytest tests/test_freshness_gate.py — 9 тестов; полный прогон tests/.

**Сырой результат:**
```
tests/test_freshness_gate.py 9/9 PASSED
tests/test_verify_on_read.py 51/51 PASSED (regression)
tests/ total: 1713 passed, 5 skipped, 91 deselected
ruff: All checks passed (5 source files)
Red Team: 5/5 атак с защитой (dirty-cache-persistence, stale-verified-flood, config-reload, non-git-dirty, consistency-singleton-leak)
```

**Детали implementation:**
- **Read gate (layer.py):** intel_get_project_memory при STALE + verify_on_read=True → полный VOR-проход замыкает домен в CONSISTENT (mark_consistent); неполный — blocked + stale_unverified на непроверенных узлах
- **Write gate (layer.py):** intel_add_memory_node при STALE → refuse с инструкцией вызвать intel_get_project_memory
- **Dirty fix (verify_on_read.py):** dirty→fingerprint пересобирается каждый проход (никогда не кэшируется); verdict cache НЕ читается и НЕ пишется при dirty; dirty cache key = sha256(node_id|head|1)
- **Config (settings.py):** MemoryConfig.freshness_gate = os.getenv("FRESHNESS_GATE", "both") через field(default_factory=...)

**Вердикт:** ✅ ПОДТВЕРЖДЕНА — gate работает, dirty-interval закрыт, тесты изолированы (conftest autouse reset_tracker). UNKNOWN state не блокирует (first-run safe).

**Урок:** dataclass field default = os.getenv(...) вычисляется ОДИН РАЗ при импорте класса — нужен field(default_factory=...) для тестов с monkeypatch. ConsistencyTracker синглтон между тестами требует autouse reset в conftest.py (test_propagation_engine ломался без него).

**Связи:** Exp 2 (enforcement absent → gate required), ADR-0003 (VOR), PlanFence (server-side blocking), KNOWN_ISSUES 2026-09-09 Exhibit #23 (loop closed: STALE→gate→block).
Loading
Loading