fix(claude): keep IDE-context envelopes out of first_message previews - #1265
fix(claude): keep IDE-context envelopes out of first_message previews#1265adammw wants to merge 8 commits into
Conversation
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
58dabd8 to
5193f4a
Compare
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
The VS Code extension often prepends a <ide_opened_file> or <ide_selection> wrapper directly onto a real prompt in the same user entry. PR kenn-io#1252 promoted standalone envelopes to hidden system metadata, but this mixed case (envelope + real prompt in one message) falls outside that strict standalone match by design, so the raw markup stayed in first_message and the visible transcript. Split a leading IDE-context envelope off from the rest of the message: the envelope becomes its own hidden system-metadata message (same ide_opened_file/ide_selection subtype as the standalone case), and the remaining real prompt becomes an ordinary user message. first_message and user turn counts are computed from the same firstMessageAndUserCount pass used before, so they now derive from the real prompt only. Bumps the parser data version to 75 so existing rows re-parse.
The IDE-envelope split gave both the synthetic hidden message and the real prompt the same source UUID. Pins and Recall evidence resolve messages by (session_id, source_uuid) and require the key to be unique: the duplicate made Recall endpoint resolution ambiguous (permanently revoking previously trusted evidence on the v75 reparse) and let pin restoration attach a pin to both rows. The prompt now keeps the entry's own uuid and the synthetic envelope row derives a distinct '<uuid>:ide-context' identity (empty stays empty). Full-rebuild pin migration in CopySessionMetadataFrom mapped pins across DBs by (session_id, ordinal) alone, so inserting the hidden envelope row shifted every later ordinal and moved pins onto unrelated rows. The copy now prefers the source_uuid natural key when the old row has one that resolves to exactly one fresh row, falling back to ordinal only for pins the uuid pass could not resolve. Records the mixed envelope-plus-prompt transcript shape in docs/internal/session-format-sources.md per the provider format provenance rule. Adds parser coverage for the split identities, a metadata-copy test for pins across the ordinal shift (including the legacy ordinal fallback), and a Recall evidence test that the split shape remaps endpoints instead of revoking provenance.
The cross-DB pin copy fell back to plain ordinal matching whenever a nonempty source_uuid did not resolve to exactly one fresh row. When the pinned message disappeared in the re-parse and another message took over its ordinal, the pin silently attached to unrelated content. The fallback now applies only to legacy pins with no source_uuid, or when the row at the old ordinal carries the pin's own uuid (the duplicate-uuid case, where the old coordinates pick which copy was meant). A nonempty uuid with no matching fresh row drops the pin. Adds coverage for a vanished uuid whose ordinal was reused and for a duplicated uuid resolved by old ordinal.
The uuid pin-copy pass verified uniqueness only in the fresh DB. When the old DB itself held duplicate source_uuids and the re-parse removed all but one, a pin on a removed duplicate matched the lone survivor and transferred to a message it was never on. The uuid now has to be unique on both sides; old-side duplicates go through the guarded ordinal fallback, which restores a pin only when the row at its old ordinal carries the same uuid and drops it otherwise. Adds coverage for a pin on a removed old-side duplicate (dropped) and on the surviving duplicate (restored at its ordinal).
splitLeadingClaudeIDEEnvelope runs after preprocessClaudeUserText, so a command envelope hidden behind an IDE-context wrapper bypassed command and system-reminder preprocessing: the revealed remainder was stored as raw command XML and could become first_message. The remainder now goes through preprocessClaudeUserText in both extraction paths, normalizing command XML (e.g. to /clear) and honoring the skip result for command envelopes that cannot be normalized. Covered by the already-pending v79 reparse in this change; no further data version bump is needed.
26c1417 to
af011ba
Compare
roborev: Combined Review (
|
Legacy pins without a source_uuid still restored by bare ordinal in the cross-DB copy. After an ordinal-shifting re-parse such as the IDE envelope split, the old combined prompt's ordinal belongs to the hidden envelope row, so a legacy pin silently attached to system metadata instead of the prompt. The ordinal fallback now also requires the candidate row to match the pinned row's role and content, both when the old DB has an empty source_uuid and when it predates the column entirely; pins whose identity cannot be established are dropped. Adds coverage for a uuid-less pin on a split combined prompt.
roborev: Combined Review (
|
Fixes #1238 (the previous PR did not fix the issue) for me.
I haven't evaluated if the
internal/parser/claude.gochanges are the best solution or if there's a way to combine the work from the previous change.Claude Sonnet 5 explanation follows:
The VS Code extension often prepends a <ide_opened_file> or <ide_selection> wrapper directly onto a real prompt in the same user entry. PR #1252 promoted standalone envelopes to hidden system metadata, but this mixed case (envelope + real prompt in one message) falls outside that strict standalone match by design, so the raw markup stayed in first_message and the visible transcript.
Split a leading IDE-context envelope off from the rest of the message: the envelope becomes its own hidden system-metadata message (same ide_opened_file/ide_selection subtype as the standalone case), and the remaining real prompt becomes an ordinary user message. first_message and user turn counts are computed from the same firstMessageAndUserCount pass used before, so they now derive from the real prompt only.
Bumps the parser data version to 75 so existing rows re-parse.