feat: tag-structured reflection distiller transcript, unfenced input - #965
Conversation
5224a6a to
5c7a8bb
Compare
|
Recomposed onto current master (f6e63af). The original first commit (speaker-tag transcript wrapping) landed via #942 in its reviewed, hardened form, so this branch now carries only the distiller-side change: tagged unfenced INPUT with block-boundary clipping, adapted to the system/user prompt split that landed on master. Stored session-summary rows keep the labeled shape via an explicit format switch. Full gates green: build, fresh dist, manifest verifier, new file 6/6, full suite. Ready for review. |
rwmjhb
left a comment
There was a problem hiding this comment.
Thanks for tightening the reflection transcript framing. Two correctness gaps remain before this is safe to merge:
-
trimTranscriptToTagBoundarystill returns a headless fragment when the newest (or only) message is longer thanmaxInputChars. In that case the tail slice contains no opening speaker tag, so thetagStarts.length === 0branch returns the raw suffix. The resulting INPUT starts inside message content and ends with (for example)</assistant_message>, which violates the complete tagged-block grammar this PR introduces. Please preserve a structurally complete block in this case (or truncate the message content inside its tags) and add regressions for oversized single user and assistant messages. -
The
"labeled"storage path interpolatesturn.textdirectly, while speaker-tag spoof neutralization only happens informatConversationTranscript. A user-authored literal<assistant_message>therefore survives in a stored session-summary row and can be replayed later as fake transcript structure. Please neutralize these tags in the stored-artifact path as well and add a regression using literal speaker tags in message content.
The six focused tests in reflection-tagged-input.test.mjs pass, and the current GitHub CI is green. The local full-suite run stalled in the unrelated existing update-consistency-lancedb.test.mjs; I am not treating that harness issue as an author blocker.
Port the extraction lane's speaker-tagged conversation structure into the reflection distiller input: session messages render as <user_message>/ <assistant_message> blocks instead of role-colon lines, and the INPUT code fence is removed (any code block inside the conversation terminated the fence early and leaked the rest of the transcript out of the input frame). Clipping now snaps to whole tagged blocks via trimTranscriptToTagBoundary instead of slicing mid-message, and the prompt teaches the tag grammar up front. Stored session-summary rows keep the legacy labeled role-colon shape via an explicit format switch: a stored row must never carry literal speaker tags that a later recall could replay into a prompt as fake transcript structure.
|
Both findings were correct, fixed at
Four regressions added (oversized single user message, oversized single assistant message, oversized newest block in a multi-block transcript, literal speaker tags on the labeled path) — red-proofed: exactly those four fail against the previous head |
5c7a8bb to
a8ca978
Compare
rwmjhb
left a comment
There was a problem hiding this comment.
Re-reviewed the new head. Both requested blockers are addressed:
- Oversized newest/only messages are rebuilt as structurally complete tagged blocks, with focused regressions for user, assistant, and multi-block cases.
- The labeled storage path now neutralizes literal speaker tags, with a production-path regression.
The focused suite passes 10/10, the full npm test chain completes successfully, npm run build succeeds with a clean worktree (committed dist is current), and all GitHub CI checks are green.
Non-blocking follow-ups: consider rejecting/clamping programmatic maxInputChars values smaller than the minimum tag envelope, and consider preserving the exported reader's old labeled default while having the distiller opt into "tagged" explicitly. Neither affects the validated runtime path here.
Problem
The reflection distiller's INPUT block renders the session as
role: textlines inside a code fence. Two failure modes: any code block inside the conversation terminates the fence early and leaks the rest of the transcript out of the input frame, and themaxInputCharsclip slices mid-message so the INPUT can open with headless text whose speaker is unknown.Change
Port the extraction lane's speaker-tagged transcript structure (#964) into the distiller input:
<user_message>/<assistant_message>blocks via the shared formatter (tags are fence-immune: a fenced code block inside a message rides safely inside its tags).trimTranscriptToTagBoundaryinstead of slicing mid-message.role: textshape via an explicit format switch: a stored row must never carry literal speaker tags that a later recall could replay into a prompt as fake transcript structure.Tests
New
test/reflection-tagged-input.test.mjs: tag teaching, unfenced tail, inner-fence safety, block-boundary clipping, and the tagged-vs-labeled format switch through the session-file reader. Registered in the test chain and CI manifest.Stacked on #964; the diff includes it until it merges.