016ba66176
Resumed sessions loaded message dicts from state.db WITHOUT the _DB_PERSISTED_MARKER, so any flush that lost the identity boundary (compression durable-snapshot adoption, incremental tool-call persists, rotation preflight on cold resume) re-appended the ENTIRE loaded transcript as new rows. Compression cycles then doubled the copies: the incident session grew 998 -> 1995 -> 3990 -> 7981 rows across three aborted rotations (15,962 active rows, only 472 distinct). Fix at the architectural chokepoint: SessionDB._rows_to_conversation (shared by get_messages_as_conversation and get_resume_conversations) now stamps the marker at row materialization time - a dict built FROM a durable row is persisted by construction, regardless of which caller loads it or how the list is later handed to a flush. Safety: - Wire-safe: every transport strips underscore-prefixed keys before the API request (chat_completion_helpers, anthropic_adapter), same contract as the existing _row_id stamp in the same function. - Rotation handoffs still write: compression's assembly copies strip the marker (_fresh_compaction_message_copy + the terminal _strip_persistence_markers sweep), so compacted transcripts still flush to the child session (#57491 invariant preserved). - Branch/seed copies unaffected: /branch and _persist_branch_seed build fresh field-projected dicts and write via append_messages_batch directly, not through the marker-gated flush. Tests: new regression suite (marker sync, load stamping, 3-cycle amplification repro, new-tail write guard, compaction-copy handoff); updated the #68454 control test that asserted the old double-write behavior and the ACP restore shape test.