TODO: plan Phase 15 for mid-turn compaction reinject gap (B-003)
Document root cause and fix strategy from lost-reinject.jsonl analysis. Add B-003 to BACKLOG; ignore *.ndjson and *.jsonl session dumps. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,3 +2,5 @@ node_modules/
|
||||
dist/
|
||||
*.tsbuildinfo
|
||||
.DS_Store
|
||||
*.ndjson
|
||||
*.jsonl
|
||||
|
||||
+10
-1
@@ -41,7 +41,16 @@
|
||||
|
||||
## Открыто
|
||||
|
||||
_Новые пункты — ниже (следующий id: **B-003**)._
|
||||
_Новые пункты — ниже (следующий id: **B-004**)._
|
||||
|
||||
### B-003 · open · e2e · 2026-06-18
|
||||
|
||||
- **Сценарий:** Длинная сессия `gitlab-mr-review` + pi-auto-compact; auto compaction в ходе Phase 6 review (issue #480334)
|
||||
- **Проблема:** Второй подряд auto compaction не re-inject'ит tracked skill; `/skill-reinject` status показывает `last compaction: none`
|
||||
- **Место:** `session_before_compact` / `session_compact` / defer + `before_agent_start`; артефакт `lost-reinject.jsonl`
|
||||
- **Факт:** Compaction #1 (07:25:03): `lastCompactionSource: auto`, `skill-reinject:inject` есть, follow-up «Auto-compact ran before this turn». Compaction #2 (+11s, mid-turn после `ls`): `lastCompactionSource: null`, inject нет, агент продолжил без user-prompt. Skill `gitlab-mr-review` вне kept window (оригинальный block до `firstKeptEntryId`; reinject — `custom_message`, не user)
|
||||
- **Обход:** `/skill-reinject now` (не проверялось в этом прогоне)
|
||||
- **Предложение:** Phase 15 в `TODO.md` — fallback детекции source на `session_compact` + mid-turn доставка defer вне `before_agent_start` + kept-window учитывает `skill-reinject:inject`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -106,8 +106,9 @@
|
||||
| 12 | Edge cases и полировка | 7–11 | §11 |
|
||||
| 13 | Приёмка и документация | 0–12 | §12–13 |
|
||||
| 14 | Re-inject для `--skill` / не-discovery skills (B-002) | 7, 9, 10, 13 | §5.2, §6.2, §11; BACKLOG B-002 |
|
||||
| 15 | Mid-turn / пропущенный compaction (B-003) | 7, 8, 12, 14 | §5.2, §6.4–6.5, §8, §13, §16; BACKLOG B-003 |
|
||||
|
||||
**Порядок:** фазы 0→13; внутри фазы — сверху вниз. Параллельно после фазы 0 можно вести 1 и 2; фазы 3 и 4 — независимы друг от друга.
|
||||
**Порядок:** фазы 0→13; внутри фазы — сверху вниз. Параллельно после фазы 0 можно вести 1 и 2; фазы 3 и 4 — независимы друг от друга. Фаза 15 — после 14.
|
||||
|
||||
---
|
||||
|
||||
@@ -272,6 +273,46 @@
|
||||
|
||||
---
|
||||
|
||||
### Фаза 15 — Mid-turn compaction и пропуск reinject (B-003)
|
||||
|
||||
Закрывает [BACKLOG B-003](./BACKLOG.md#b-003--open--e2e--2026-06-18): второй auto compaction подряд не re-inject'ит skill; status `last compaction: none`. Артефакт: `lost-reinject.jsonl` (два compaction за ~11s; первый OK, второй — нет).
|
||||
|
||||
**Корень (два связанных сбоя):**
|
||||
|
||||
1. **Gate источника compaction** — `lastCompactionSource` выставляется из `pendingCompactionSource` только если до `session_compact` успел отработать `session_before_compact`. На втором compaction в логе `lastCompactionSource: null` → `shouldReinject = false` → очередь сброшена, reinject не планировался.
|
||||
2. **Дыра defer-доставки** — consume только на `before_agent_start` (новый user prompt). Mid-turn compaction внутри активного agent loop (после toolResult, без «Auto-compact ran…») не даёт `before_agent_start` до следующего сообщения пользователя → skill теряется до ручного `/skill-reinject now`.
|
||||
|
||||
**Дополнительно (двойной compact):** `skillsPresentInKeptWindow` не видит `custom_message` `skill-reinject:inject` → после успешного reinject skill формально «вне kept», и при корректном gate второй compact снова планирует reinject (лишний inject, не причина пропуска в B-003, но усиливает хрупкость).
|
||||
|
||||
**Целевое поведение:** каждый auto `session_compact` либо re-inject'ит отсутствующие в kept skills, либо явно skip с причиной в debug; status не показывает ложное `none` после auto compact; нет гонки с pi-auto-compact на turn-boundary path.
|
||||
|
||||
**Стратегия доставки (две ветки, одна точка решения на `session_compact`):**
|
||||
|
||||
| Условие на `session_compact` | Доставка defer |
|
||||
|------------------------------|----------------|
|
||||
| `ctx.isIdle()` | Как сейчас: `pendingReinject` → consume на следующем `before_agent_start` (pi-auto-compact follow-up / user prompt) |
|
||||
| `!ctx.isIdle()` (mid-turn) | Немедленно: `pi.sendMessage({ customType: "skill-reinject:inject", … }, { deliverAs: "steer" })` — в очередь до следующего LLM-вызова в том же turn; **не** `sendUserMessage` в `session_compact` (§16.2) |
|
||||
|
||||
**Стратегия source (fallback §8):** manual — только явный `/compact` в `input`; всё остальное на `session_compact` с `pendingCompactionSource === null` → считать `auto` (вызов `ensureCompactionSourceMarked` и в `session_before_compact`, и в `session_compact` как safety net).
|
||||
|
||||
- [ ] **SPEC phase 15** — §8: fallback infer `auto` на `session_compact`; §6.5.1: mid-turn defer через `sendMessage`/`steer`; §6.4: kept учитывает `skill-reinject:inject`; §16.6: double compact + mid-turn; §13: критерий «второй compact подряд»; зачем: контракт до кода
|
||||
- [ ] **compaction.ts ensureSource** — `ensureCompactionSourceMarked(runtime)`: если не `manual` → `auto`; вызывать из `markAutoCompactionBeforeCompact` и экспортировать для `session_compact`; зачем: закрыть `lastCompactionSource: null` (B-003 факт #1)
|
||||
- [ ] **index.ts compact source fallback** — в `handleSessionCompact` до `consumeCompactionOnSessionCompact`: `ensureCompactionSourceMarked(compactionRuntime)`; зачем: safety net когда `session_before_compact` не пришёл
|
||||
- [ ] **test/compaction-source-fallback.test.ts** — (a) `session_compact` без prior `before_compact`: source `auto`, `shouldReinject` true; (b) prior `manual` из input: остаётся manual, reinject off по default; зачем: регрессия gate
|
||||
- [ ] **kept.ts reinject custom** — `skillsPresentInKeptWindow` (или сосед) учитывает entries `type: custom_message`, `customType: skill-reinject:inject` с `<skill name="…"` в `content`; зачем: §6.4, не планировать лишний reinject после успешного inject
|
||||
- [ ] **test/kept-window.test.ts reinject-custom** — inject custom в kept slice → skill считается present; вне slice → absent; зачем: double-compact dedup
|
||||
- [ ] **reinject.ts mid-turn steer** — `deliverDeferredReinjectSteer(pi, planned, …)` через `pi.sendMessage` + `DEFERRED_REINJECT_CUSTOM_TYPE`, `deliverAs: "steer"`; clear `pendingReinject`; флаг `compactionRuntime.deferredDeliveredForCompactionId` (или аналог) чтобы `before_agent_start` не дублировал; зачем: B-003 факт #2, §16.2-safe
|
||||
- [ ] **index.ts mid-turn wire** — в defer-ветке `handleSessionCompact`: после enqueue, если `shouldReinject && planned.length > 0 && !ctx.isIdle()` → steer deliver; иначе оставить pending для `before_agent_start`; persist; зачем: единая развилка idle / mid-turn
|
||||
- [ ] **index.ts before_agent_start dedup** — `tryConsumeDeferredReinject` / wire: skip consume если steer уже доставил для `lastCompactionFirstKeptEntryId` / compaction entry id; зачем: turn-boundary compact + pi-auto-compact не дают двойной inject
|
||||
- [ ] **test/reinject-mid-turn.test.ts** — mock `pi.sendMessage`: mid-turn (`isIdle: false`) → steer вызван, pending очищен; idle → steer не вызывается; после steer `before_agent_start` не inject'ит повторно; зачем: §12.1
|
||||
- [ ] **diag.ts mid-turn** — расширить snapshot: `compactionSource`, `sourceInferred`, `deliveryBranch: "before_agent_start" \| "steer"`, `isIdle`; фазы `session_compact` + опционально `mid_turn_deliver`; зачем: отладка без повторения lost-reinject
|
||||
- [ ] **scripts/b003-repro.mjs** — RPC/скрипт: симулировать или документировать repro по `lost-reinject.jsonl` (два compact, проверка inject count ≥ 1 на каждый compact с skill вне kept); зачем: регрессионный gate B-003
|
||||
- [ ] **manual E2E B-003** — длинная сессия или уменьшенный `keepRecentTokens`; два auto compact подряд (в т.ч. mid-turn); после каждого — skill в контексте или `debug` показывает `skipped-kept`; `/skill-reinject` → `last compaction: auto`; зачем: критерий закрытия B-003
|
||||
- [ ] **README mid-turn** — короткий подпункт: defer + mid-turn steer, coexistence с pi-auto-compact, что делать при `last compaction: none` (включить `debug`); зачем: §9.1
|
||||
- [ ] **BACKLOG close B-003** — `open` → `done` с датой и ссылкой на коммиты фазы 15; перенести в «Закрыто»; коммит `BACKLOG: …`; зачем: `dev-backlog.mdc`
|
||||
|
||||
---
|
||||
|
||||
## После v1 (не блокирует фазы 0–13)
|
||||
|
||||
Зафиксировано в SPEC §14 — не включать в чеклист v1, только при отдельном запросе:
|
||||
|
||||
Reference in New Issue
Block a user