Skip to content

fix(panel): navigate and key context injections by group.id (#53) - #56

Merged
axisrow merged 4 commits into
mainfrom
fix/53-group-id-navigation
Oct 7, 2026
Merged

axisrow merged 4 commits into
mainfrom
fix/53-group-id-navigation

Conversation

@axisrow

@axisrow axisrow commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Проблема

После #52 turnIndex AI-группы = порядковый номер её user-сообщения, поэтому две
AI-группы одного хода (автокомпакт/system рвут ход на группы) получают одинаковый
turnIndex; aiCount-фолбэк тоже даёт дубли. На число были завязаны:

  1. Навигация — ChatHistory искал цель через find(... turnIndex ===): клик по
    инъекции из поздней группы хода вёл в первую (до-компактную).
  2. Id инъекций — синтетический ai-${turnIndex} давал дубли id (React keys,
    lookup'ы), а parseTurnIndex по regex ^ai-(\d+)$ с uuid-id возвращал -1 —
    claude-md/mentioned-file пункты теряли кликабельность.

Closes #53.

Решение

Навигация и id инъекций — по стабильному group.id (= chunk id = ai-<uuid>
первого ответа, детерминирован при re-parse и в инкрементальных обновлениях).
turnIndex остаётся только отображаемой меткой «Turn N».

  • Трекеры (d8ce1ec): turnGroupId = aiGroup.id; генераторы id принимают
    group id (tool-output-<id>, loop-<id>, …); ClaudeMdInjection получил
    firstSeenTurnIndex (display-only), чтобы «@turn N» выживал с uuid-id.
  • Панель (9bff19a): колбэки onNavigateToTurn/onNavigateToTool/onNavigateToUserGroup
    принимают groupId: string; секции/items передают aiGroupId (или
    firstSeenInGroup для claude-md/mentioned-file); parseTurnIndex удалён,
    formatFirstSeen принимает число.
  • Пул тестов (7c29518): vitest workers зафиксированы на n-1 (просьба владельца —
    машина висла на параллельном прогоне).

Доказательство (TDD)

Красные тесты до фикса:

  • contextTracker.test.ts «two AI groups of one turn keep distinct injection ids…»:
    expected 'ai-0' to be 'ai-xxx' — трекер клал синтетический id.
  • burnHeaderNav.test.ts: received 0 вместо 'ai-11111111-uuid' — секции слали число.
  • ClaudeMdItem: entry button not found: @Turn 4 — с uuid-id пункт был некликабелен.

После фикса: 934/934 теста, pnpm check (typecheck + lint + test + build) зелёный.

Прогон с новым пулом стал быстрее: 6.9s → 3.5s (env setup 20s → 12s).

Известные компромиссы

  • ai-empty-<Date.now()> фолбэк-id пустых AI-групп нестабилен между re-parse — клик
    по такой инъекции даст no-op (не падение); случаев в живых данных не видно.
  • Смена React key сбрасывает локальный useState развёрнутых строк панели — косметика.

🤖 Generated with Claude Code

axisrow and others added 4 commits October 7, 2026 12:34
Issue #53. After #52 two AI groups of one turn share turnIndex (compact or
system boundaries tear a turn; aiCount fallback collides too), so the
synthetic "ai-${turnIndex}" id collided: duplicate injection ids and
wrong-group navigation.

Trackers now carry the group's real id (= stable chunk id, unique across
re-parses):

- contextTracker: turnGroupId = aiGroup.id; injection id generators take the
  group id instead of turnIndex (tool-output-<id>, loop-<id>, wait-loop-<id>,
  thinking-text-<id>, task-coord-<id>, user-msg-<id>); reread id follows.
- claudeMdTracker: same turnGroupId change; firstSeenTurnIndex (display-only
  "@turn N") added next to firstSeenInGroup.
- ClaudeMdInjection gains firstSeenTurnIndex so labels survive real uuid ids.

Red test (contextTracker.test.ts): two AI groups sharing turnIndex 0 must
keep disjoint injection ids, their own aiGroupId, and claude-md display
metadata — failed with 'ai-0' expected 'ai-xxx'.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Issue #53, panel half. SessionContextPanel callbacks (onNavigateToTurn /
onNavigateToTool / onNavigateToUserGroup) take the AI group's id instead of
turnIndex; ChatHistory resolves the target by group.id, so a click from the
LATER group of a split turn lands in that group, not the first one sharing
the index.

- Sections/items pass injection.aiGroupId (or firstSeenInGroup for
  claude-md/mentioned-file); clickability gate is a non-empty id.
- FlatInjectionList rows carry groupId; RankedInjectionList swaps
  getInjectionTurnIndex for getInjectionGroupId.
- parseTurnIndex dropped; formatFirstSeen formats a turnIndex number —
  with uuid ids the "ai-N" parse was already unfixable.
- DirectoryTree nodes carry firstSeenTurnIndex for the "@turn N" label.

Burn-header nav tests now assert the callback receives the group id string;
ClaudeMdItem test covers a click with a uuid id (previously not clickable:
parseTurnIndex returned -1).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Pin poolOptions.threads (minThreads 1, maxThreads cpus-1) so worker count
does not depend on vitest defaults and the suite cannot saturate the
machine.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Follow-up to the #53 migration from /code-review on PR #56:

- vitest.critical.config.ts: mirror the n-1 worker cap — test:coverage:critical
  would otherwise spawn uncapped workers, the exact load that hung the machine.
- ClaudeMdItem: guard firstSeenTurnIndex with ?? -1 like DirectoryTreeNode —
  an injection without the field rendered "@turn NaN" instead of degrading.
- createGlobalInjections: turnIndex becomes a required parameter (was
  `= 0`); both callers already pass aiGroup.turnIndex, the default silently
  mislabeled a future caller as "@turn 1".
- Stale doc examples in contextInjection.ts: ids are now
  "<prefix>-<group id>" / real group ids, not "ai-0".
- burnHeaderNav fixtures: injection ids follow the real scheme
  ("wait-loop-<group id>"), so an id-scheme regression can't keep them green.
- formatFirstSeen moved into utils/formatting.ts; pathParsing.ts deleted —
  it parsed no paths after parseTurnIndex's removal.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@axisrow
axisrow merged commit c0cd2ef into main Oct 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Turn navigation and injection IDs keyed by turnIndex — collisions when one turn has multiple AI groups

1 participant