Skip to content

fix(#59): classify mixed relay-XML content as user text - #67

Merged
axisrow merged 1 commit into
mainfrom
ao/claude-devtools-25/fix-59-mixed-relay-content
Oct 8, 2026
Merged

axisrow merged 1 commit into
mainfrom
ao/claude-devtools-25/fix-59-mixed-relay-content

Conversation

@axisrow

@axisrow axisrow commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Суть

Релей определяется только тогда, когда весь контент состоит из полных блоков <teammate-message teammate_id="...">…</teammate-message>. Промпт, начинающийся с обёртки, но содержащий пользовательский текст (вопрос после релея, цитата), остаётся user-сообщением — текст сохраняется в отображении.

Fixes #59

Что изменено

  • scripts/turn-accounting.mjs — канон: TEAMMATE_BLOCK_RE (тот же regex, что у парсера карточек) + stripTeammateBlocks; isTeammateText = «весь текст — только полные релей-блоки»; isTeammateRelayLine применяет whole-content правило и по массиву текст-блоков (раньше один релей-блок делал всю строку релеем).
  • src/shared/utils/teammateMessageParser.ts — импортирует regex из канона вместо собственной копии: классификация и рендер карточек не могут разъехаться.
  • src/renderer/utils/displayItemBuilder.ts — сабагентский путь кладёт в карточки только чистые релеи; смешанное содержимое идёт в subagent_input с полным текстом (раньше текст вне обёртки выбрасывался).
  • scripts/turn-accounting.d.mts, eslint.config.js — декларации и allowlist импорта нового экспорта.

Поведение

  • Чистый релей — как раньше: relay, не hook-turn, transcript-turn (turnCount is 0 for agent-team sessions whose only inputs are teammate relays #55 не тронут).
  • Смешанное (обёртка + вопрос) — user-сообщение, transcript-turn остаётся одним ходом.
  • Некорректные обёртки (незакрытая; старый id='x', не подтверждён в реальных транскриптах) — user-текст, не невидимый релей.

Тесты

  • Регрессия в turnAccounting.relay.test.ts: репро из issue (строка и массив), чистый релей, некорректные обёртки.
  • Обновлены пинны старого поведения (id='x', mixed-массив, незакрытая обёртка в хук-тесте); фикстура парити-теста переведена на реальную форму релея — золотые числа не изменились.
  • pnpm typecheck — чисто; pnpm test — 950/950; eslint на изменённых файлах — чисто (заодно сняты 3 существовавших lint-ошибки в turnBudgetHookScript.test.ts).

🤖 Generated with Claude Code

A user prompt starting with a <teammate-message> wrapper plus real text
(a trailing question, quoted XML) was swallowed as a teammate relay and
the user's words were dropped from display.

Relay detection now requires the whole content to be complete relay
blocks: TEAMMATE_BLOCK_RE moves to the canonical core and is shared with
teammateMessageParser, so classification and card rendering cannot
drift. isTeammateRelayLine applies the whole-content rule across text
blocks; the subagent display path only routes pure relays to teammate
cards, mixed content keeps its text as subagent input.

Pure relays, transcript-turn semantics and the hook canon (#55) are
unchanged; malformed wrappers (unclosed, old-style id='x' — unconfirmed
in real transcripts) stay visible as user text instead of vanishing.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@axisrow axisrow left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пост-ревью PR #67 — fix(#59): mixed relay-XML → user text. Вердикт: approved

PR уже смержен, поэтому это пост-ревью: правок не требуется, заметки ниже — кандидаты в фоллоу-ап, если когда-нибудь понадобятся.

Проверено по диффу против origin/main:

  • Ядро (turn-accounting.mjs): relay теперь = только полные <teammate-message teammate_id="...">...</teammate-message> блоки (плюс whitespace). isTeammateRelayLine склеивает text-блоки через \n и применяет правило к контенту целиком; смешанный контент → isUserChunkLine = true → UserChunk, слова пользователя сохраняются. Ядро zero-imports — бандл в renderer безопасен.
  • Все потребители консистентны: MessageClassifier (категория user → UserChunk), hook isRealUserLine (импортирует ядро напрямую), isTranscriptTurnLine (turnCount в jsonl.ts), groupTransformer (display summary), displayItemBuilder (гейт карточек). Второго расходящегося механизма на вопрос «relay или нет» не появилось.
  • Regex один: TEAMMATE_BLOCK_RE перенесён в ядро, teammateMessageParser переиспользует. Оба потребителя клонируют regex (new RegExp(source, flags)), утечки lastIndex у global-regex нет.
  • Первый call site карточек (displayItemBuilder.ts:274) остался без гейта — проверил достижимость: для mixed недостижим. classifyMessages отправляет mixed в категорию user, и он не попадает в AIChunk.responses; в responses попадают только чистые relay-и (категория ai) и meta-строки, которые сайт скипает. Гейт там был бы no-op. Второй site (subagent-путь) загейчен корректно и рендерит mixed как subagent_input.
  • eslint allowImportNames: все 14 имён deep-импорта из turnAccounting.ts покрыты; stripTeammateBlocks напрямую никто не импортирует — несоответствия нет. d.mts-сигнатуры (inputSideTokens(u?), readConfig(raw?)) согласованы с новыми вызовами тестов без аргументов.
  • Тесты и fixture: смена fixture на канонический teammate_id корректна; новый тест пинает, что unclosed и old-style (id= без teammate_id) обёртки — user text. prettier на test/ не распространяется, так что длинная строка в relay-тесте — только косметика.

Наблюдения вне находок: (1) malformed обёртки в старых логах теперь открывают hook-turn — осознанная семантика #59, покрыта тестом; (2) гейт в displayItemBuilder не покрыт тестом — displayItemBuilder.test.ts вообще не тестирует teammate-карточки, можно добросить в фоллоу-апе; (3) SubagentResolver.ts:165 держит свой regex для summary-атрибута — это извлечение атрибута из файлов сабагентов, не relay-предикат, дрейфа нет.

@axisrow
axisrow merged commit 70e3053 into main Oct 8, 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.

User prompts starting with relay XML can be misclassified as teammate messages

1 participant