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
31 changes: 31 additions & 0 deletions AGENT_DIARY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@

## [2026-10-10] EXP-29 — H5 закрыт, а число EXP-28 (18%) опровергнуто собственным же экспериментом

- Разметка symbol/NL детерминированная и **заморожена до прогона** (`e29_label.py`): 15 symbolic / 18 NL.
Иначе H5 остался бы навсегда «не проверен» — постфактум разметку добавлять нельзя (§17).
- **Харнесс поймал собственный баг:** первый прогон из worktree дал `hit1=0/33` при rc=0 и 167ms —
тихий ноль на пустом индексе (индекс per-project-path). Guard: все-пусто → `exit 2`;
`E29_PROJECT_ROOT` вынесен в манифест. Первый прогон не засчитан.
- A dense ON: NL 3/18, symbolic 8/15, 0 ошибок. B dense OFF: NL **5/18**, symbolic 7/15, 0 ошибок.
NL: 2 лучше / 0 хуже; symbolic: 0 лучше / 1 хуже (TR-P2). Ускорение стабильно 7.2%/7.9%.
- **CORRECTED:** EXP-28 публиковал 18.0% — раздуто одним шумным прогоном (разброс 21%).
Действующее число 7.5%; правка внесена новой записью, не тихо (§11).
- Решение по Фазе 3 не изменилось: dense не отключаем, LanceDB не удаляем (primary store чанков),
`MSCODEBASE_DENSE_OFF` остаётся kill-switch. Числа EXP-29 — разрез той же панели, не новый эксперимент.

## [2026-10-10] EXP-28 dense-off A/B — LanceDB оказался primary store, удаление отклонено

- **Phase Zero угадал наполовину:** «LanceDB = единственное хранилище графа» — опровергнуто
(graph.db живёт отдельно); «LanceDB = единственное хранилище чанков» — подтверждено:
`bm25.py:93` строит BM25 из `table.to_pandas()`.
- **Знаменатель спас план:** «52 обращения к table-API в 13 файлах `src/`, 8 идиом», из них
dense — 5 мест. Формулировка «удалить LanceDB» была бы миграцией хранилища, а не чисткой.
- EXP-28: флаг `MSCODEBASE_DENSE_OFF` (default OFF) гасит dense-тир. A/B на 33 замороженных
кейсах, 2 прогона на плечо: A h1 11/33 vs B h1 12/33, ошибок 0/0; per-case 2 лучше /
1 хуже / 30 так же. Ускорение mean-of-means 18%, но разброс 5.8%…29.5% → H3 PARTIAL.
H1/H2/H6 опровергнуты (ожидаемо), H4 CONFIRMED, **H5 ⏳ нет разметки symbol-vs-NL** (не
«прошло», а не проверено — разметку нельзя добавить постфактум, §17).
- Харнесс проверен: счётчик `_vector_search_async` 1 → 0 при живом эмбеддере. Иначе A/B был бы пустым.
- Решение: dense **не отключаем** по умолчанию (выигрыша нет, скорость не установлена); флаг
остаётся как kill-switch. Основание для удаления — только отдельная задача «чанки в Parquet/SQLite».
- Guard: EXP-28 не удалил ни одной строки в прод-пути — только условие в `engine.py:936`.

## [2026-10-08] Phase 2 (on-demand rerank в engine) — реализовано, PR следом

- Новый pure `src/core/search/ondemand_rerank.py` (cosine, stable ties, пропуск
Expand Down
93 changes: 93 additions & 0 deletions EXPERIMENTS_LOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,97 @@
# EXPERIMENTS_LOG.md — Audit Verification (2026-07-22)
## [2026-10-10] — EXP-29 разрез symbol/NL: закрывает H5, корректирует число EXP-28 (PARTIAL)

**Зачем:** EXP-28 оставил H5 как «⏳ не проверено» — в панели не было разметки symbol-vs-NL.
Разметка не могла быть добавлена постфактум (§17), поэтому потребовался новый эксперимент.

**Вход (заморожен ДО прогона):** `experiments/e29_symbol_ab/frozen/queries_labeled.jsonl`,
sha256 `7639ba1bda00212f8bceb04ae63ddaacddb897d21ae58f2aceff2430f88a53bf`, 33 кейса панели EXP-28 + колонка `subset`.
Правило разметки детерминированное и заморожено в `e29_label.py`:
`symbolic := snake_case | CamelCase | pathy(:: / слэш / .метод()) | одиночный токен без пробелов` → symbolic 15 / NL 18.
5 гипотез, 3 из них внесены как ожидающе-провальные (H1, H2, H3).

**Команда (код из worktree, индекс проекта — явный параметр):**
```
$env:E29_PROJECT_ROOT="<project-root-with-index>" # индекс per-project-path, в worktree его нет
python experiments/e29_symbol_ab/e29_run.py # arm A: dense ON
$env:MSCODEBASE_DENSE_OFF="true"; python experiments/e29_symbol_ab/e29_run.py # arm B: dense OFF
```
**Сырой вывод (2 прогона на плечо; полные логи в results/e29_A.json / e29_B.json):**
```
arm=A dense ON : NL 3/18 (16.7%) symbolic 8/15 (53.3%) errors 0 empty_top1 0 mean_ms 2559.2 / 2555.2
arm=B dense OFF: NL 5/18 (27.8%) symbolic 7/15 (46.7%) errors 0 empty_top1 0 mean_ms 2375.2 / 2354.1
NL : 2 лучше / 0 хуже / 16 так же
symbolic: 0 лучше / 1 хуже / 14 так же (TR-P2: engine.py -> docs/zh/SEARCH_PIPELINE.md)
ускорение: 7.2% и 7.9% (стабильно, разброс 0.2%)
```

**Инцидент харнесса (закрыт):** первый прогон из worktree дал `hit1=0/33` при rc=0 и mean_ms 167 —
тихий ноль на пустом индексе (§19.6). Причина: индекс per-project-path, в worktree его нет.
Исправлено guard'ом: если все запросы вернули пусто → `return 2` с явным сообщением, плюс явный
`E29_PROJECT_ROOT` в манифесте. Непрозрачность устранена, но первый прогон не засчитан.

**Вердикты:**
- H1 (symbolic не хуже, допуск 1 кейс) — **CONFIRMED**: минус ровно 1 кейс из 15.
- H2 (NL хуже ≥3 п.п.) — **REFUTED, как ожидалось**: NL **лучше** на 2 кейса.
- H3 (ускорение ≥15%) — **REFUTED, как ожидалось**: стабильные 7.2%/7.9%.
- H4 (0 ошибок) — **CONFIRMED** (0/33 × 2 прогона × 2 плеча).
- H5 (≥1 улучшенный NL-кейс) — **CONFIRMED, как ожидалось провалиться**: E2E-05 и E2E-07.

**Механизм:** оба улучшения — NL-запросы, где dense уводил top-1 в документ
(`docs/adr/0003-verify-on-read.md` и `src/core/multi_project_searcher.py` вытесняли код).
Регрессия одна и лексическая (`docs/zh/SEARCH_PIPELINE.md` вместо `engine.py`).

**Согласованность с EXP-28:** суммы подмножеств воспроизводят EXP-28 один в один
(A 8+3=11, B 7+5=12) — это разбиение панели, а не независимый эксперимент.

**CORRECTED к EXP-28 (без тихой правки):** EXP-28 публиковал ускорение **18.0%**. EXP-29 двумя
прогонами даёт **7.2% и 7.9%**; прежнее число было раздуто одним шумным прогоном (разброс 21%).
Прежний вердикт H3=PARTIAL остаётся верным, но **число 18% больше не публикуется**: действующее — 7.5%
(среднее двух прогонов EXP-29).

**Вывод по Фазе 3:** наблюдаемого вреда dense нет ни на одном подмножестве, но и доказанного выигрыша
в качестве нет (1–2 кейса из 33 = шум по построению). Решение не изменилось: dense не отключаем,
LanceDB не удаляем (он primary store чанков), флаг остаётся как kill-switch.

## [2026-10-10] — EXP-28 dense-off A/B: гасим dense-тир флагом (PARTIAL)

**Вопрос:** можно ли отключить dense (LanceDB vector search) без потери качества — то есть довести Фазу 3 до точки принятия решения об удалении?
**Гипотезы:** frozen/HYPOTHESES.md (6 шт., из них 3 внесены как ожидающе-провальные: H1, H2, H6).
**Вход (заморожен ДО прогона):** `experiments/e28_denseoff/frozen/queries.jsonl`, 33 кейса,
sha256 `f31e594ef729b2d7f577d37049734b26aa4a6dca9cd44e067e2db72514227cc8` (33 уникальных gold-файла, все на диске).
Панель = объединение трёх уже существовавших frozen-наборов (4A f5 — 16, token_reduction — 13 из 16, e2e_quality_search::CASES — 10), дедуп по gold-файлу; N1/N2/N3 без целевого файла исключены.
**Команда (одна сессия, один индекс, различается только env):**
```
python experiments/e28_denseoff/e28_run.py # arm A: dense ON
$env:MSCODEBASE_DENSE_OFF="true"; python experiments/e28_denseoff/e28_run.py # arm B: dense OFF
```
**Сырой вывод (2 прогона на плечо, полные логи в results/e28_A.json / e28_B.json):**
```
arm=A(dense_on) hit1=11/33 (33.3%) hit5=15/33 (45.5%) errors=0 mean_ms=3359 / 3534
arm=B(dense_off) hit1=12/33 (36.4%) hit5=16/33 (48.5%) errors=0 mean_ms=3163 / 2492
mean-of-means: A=3446ms B=2828ms -> 18.0% быстрее; разброс плеча B между прогонами: 21%
per-case hit1: 2 лучше / 1 хуже / 30 так же (net +1 = шум)
```
**Проверка харнесса (dense реально гасится, а не флаг «для галочки»):** счётчик `_vector_search_async` на одном запросе:
`arm A dense_calls=1` -> `arm B dense_calls=0` (эмбеддер живой, число результатов то же).
**Вердикты:**
- H1 (hit1 должен упасть ≥3 п.п.) — **НЕ ОПРОВЕРГНУТ**: dense-off не хуже, +1 кейс.
- H2 (hit5 должен упасть ≥6 п.п.) — **НЕ ОПРОВЕРГНУТ**: +1 кейс.
- H3 (ускорение ≥15%) — **PARTIAL**: mean-of-means 18%, но разброс 5.8%…29.5% перекрывает порог — на 2 прогонах не устанавливается.
- H4 (0 исключений) — **CONFIRMED**: 0/33 ошибок в обоих плечах, оба прогона.
- H5 (символьные запросы не пострадают) — **⏳ НЕ ПРОВЕРЕНО**: в панели нет разметки symbol-vs-NL, подмножество не выделено. Единственная регрессия — TR-P2 (`src/core/search/engine.py`), лексический, а не символьный кейс.
- H6 (dense-off не лучше ни на одном кейсе) — **ОПРОВЕРГНУТ, как и ожидалось**: 2 кейса улучшились (E2E-05, E2E-07) — там, где dense уводил топ-1 в документ.

**Вывод (важнее цифр):** из 52 обращений к table-API в 13 файлах `src/` на векторный поиск приходится 5 мест.
Остальные 47 — хранение чанков: `bm25.py:93` строит BM25-индекс из `table.to_pandas()`, `db_writer`/`indexer`/`indexer_table`
пишут и удаляют чанки, `file_move_manager` и `index_guard` делают `version()`/`where()`-сканы для meta-patching и ремонта.
**LanceDB здесь — не «векторный индекс», а primary store чанков.** PropertyGraph живёт отдельно (`graph.db`), это
единственная Phase-Zero гипотеза, которая НЕ подтвердилась.

**Решение по Фазе 3:** удаление dense — **НЕ рекомендовано** (выигрыша в качестве нет — разница в шуме; скорость не установлена).
`MSCODEBASE_DENSE_OFF` остаётся как операционный kill-switch (default OFF, прод-путь байт-в-байт прежний).
Основание для удаления можно рассматривать только после отдельной задачи «чанки в Parquet/SQLite».


## [2026-09-27] — stale_after + discriminator for memory notes (CONFIRMED)

Expand Down
30 changes: 30 additions & 0 deletions experiments/e28_denseoff/PENDING_LEDGER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# PENDING_LEDGER — предложения в реестры (чужая сессия владеет KNOWN_ISSUES.md)

Правок в `KNOWN_ISSUES.md` не делаю: файл сейчас меняет другая сессия (незакоммиченные правки),
по §21.3 реестр принадлежит одной сессии.

## Предложение 1 — в KNOWN_ISSUES (новый пункт, не баг, а инвентаризация)

**Статус:** предложено 2026-10-10, не подтверждено как дефект.
**Тема:** LanceDB в проекте — primary store чанков, а не «векторный индекс».
**Факт (проверено):** 52 обращения к table-API в 13 файлах `src/`; 8 идиом (`add`, `delete`,
`version`, `count_rows`, `search`, `to_pandas`, `to_arrow`, `merge_insert/query`).
На векторный поиск — 5 мест; остальные: запись/удаление чанков (`db_writer`, `indexer`,
`indexer_table`), чтение данных (`bm25.py:93` строит BM25 из `table.to_pandas()`, `index_status`,
`health`, 3× `mcp/tools/*`), метаданные (`file_move_manager` `version()`/`where()`, `index_guard` repair),
обвязка (`settings.dense`-соседние `lancedb_version`, `artifact_paths`, `file_guard`).
**Следствие:** любая формулировка «удалить LanceDB» неверна без слов «перенести чанки в другое хранилище».
**Температура:** 🟢 (факт, не проблема). **Ссылка:** `EXPERIMENTS_LOG.md` EXP-28.

## Предложение 2 — в WISDOM / ADR

Правило «измеряй знаменатель, а не числитель» на живом примере: первая формулировка была
«5 мест завязаны на dense → их можно отключить», реальный свип дал 52 места в 13 файлах,
из которых dense — меньшинство. Свип занял один вызов; неверная формулировка успела бы уйти
в план миграции и в PR.

## Предложение 3 — в KNOWN_ISSUES (маленькое)

`EXPERIMENTS_LOG.md` не имеет таблицы вердиктов по гипотезам: шесть гипотез эксперимента
разбросаны по абзацам. `experiments/e28_denseoff/VERDICTS.md` — образец таблицы; стоит
попросить формализацию при следующем большом эксперименте.
23 changes: 23 additions & 0 deletions experiments/e28_denseoff/VERDICTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# EXP-28 — вердикты (дописывается ПОСЛЕ прогона; HYPOTHESES.md не редактируется)

Дата: 2026-10-10. N=33 кейса, 2 прогона на плечо, одна сессия, один индекс.

| # | Гипотеза | Фальсификатор | Наблюдение | Вердикт |
|---|---|---|---|---|
| H1 | hit1 без dense падает ≥3 п.п. | hit1 падает ≥3 п.п. | 11/33 → 12/33 (33.3% → 36.4%) | ❌ REFUTED (dense-off не хуже) |
| H2 | hit5 без dense падает ≥6 п.п. | hit5 падает ≥6 п.п. | 15/33 → 16/33 (45.5% → 48.5%) | ❌ REFUTED |
| H3 | dense-off ускоряет ≥15% | ускорение <5% | mean-of-means 18.0%, но 5.8% и 29.5% по прогонам | ⏳ PARTIAL — порог перекрыт разбросом |
| H4 | dense-off даёт 0 исключений | ≥1 ERROR | 0/33 в обоих плечах, оба прогона | ✅ CONFIRMED (2 прогона) |
| H5 | символьные запросы не пострадают | hit1 на символьных падает | подмножество не размечено в панели | ⏳ UNTESTED — нет разметки, кейс TR-P2 лексический |
| H6 | dense-off не лучше ни на одном кейсе | есть кейс, где dense-off лучше | E2E-05, E2E-07 лучше; TR-P2 хуже | ❌ REFUTED (ожидаемый провал) |

## Что стоит за H5 (почему не «закрыт»)
Панель объединена из трёх источников без поля «тип запроса». Чтобы честно проверить H5, нужна
разметка symbol-vs-NL в frozen-входе. Добавлять её после прогона нельзя (§17 freeze-before-look) —
значит H5 проверяется только новой панелью. Это следующий шаг, а не «мелочь».

## Ограничения вердикта
- Один индекс, один день, один хост: перенос на другое железо не проверялся.
- Разница качества (net +1) — шум: 2 лучше / 1 хуже / 30 так же.
- Разница скорости не установлена: разброс между прогонами одного плеча 21%.
- Проверка «флаг реально гасит dense» выполнена (dense_calls 1 → 0) — иначе весь A/B был бы пустым.
Loading
Loading