fix(attachments): preserve pdf/audio kind when reloading attachments from the DB

_reconstruct_attachment_refs collapsed every non-image attachment to the 'document' placeholder kind, so a reloaded session's pdf/audio placeholder type ({type:document}) mismatched the live-injection type ({type:pdf}/{type:audio}). Harmless today (resolution keys on attachment_id + blob kind) but a latent footgun for any consumer branching on the pre-resolution placeholder type. Preserve image/pdf/audio verbatim; only a stored 'text' blob collapses to 'document'.
This commit is contained in:
Patrick Buckley
2026-06-15 20:40:01 -07:00
parent 5b2a9480a1
commit 797a8e0404
2 changed files with 48 additions and 5 deletions
+39
View File
@@ -75,6 +75,45 @@ class TestSniffAudio:
assert sniff_audio_mime(b"") is None
class TestReconstructAttachmentRefs:
def test_preserves_pdf_audio_kind_on_reload(self) -> None:
from turnstone.core.storage._utils import _reconstruct_attachment_refs
atts = {
1: [
{
"attachment_id": "a1",
"kind": "pdf",
"filename": "r.pdf",
"mime_type": "application/pdf",
},
{
"attachment_id": "a2",
"kind": "audio",
"filename": "a.wav",
"mime_type": "audio/wav",
},
{
"attachment_id": "a3",
"kind": "image",
"filename": "i.png",
"mime_type": "image/png",
},
{
"attachment_id": "a4",
"kind": "text",
"filename": "t.txt",
"mime_type": "text/plain",
},
]
}
refs, meta = _reconstruct_attachment_refs(atts, 1)
# pdf/audio/image kept verbatim; only 'text' collapses to 'document'
# (so the placeholder type can't collide with a real text content part).
assert [r.kind for r in refs] == ["pdf", "audio", "image", "document"]
assert [m["kind"] for m in meta] == ["pdf", "audio", "image", "text"]
class TestSafeAttachmentLabel:
def test_strips_frame_breakers(self) -> None:
from turnstone.core.attachments import safe_attachment_label
+9 -5
View File
@@ -410,11 +410,15 @@ def _reconstruct_attachment_refs(
if not attachments_by_msg or row_id is None:
return refs, meta
for att in attachments_by_msg.get(row_id, []):
# AttachmentRef.kind is the by-reference content kind ('image' |
# 'document'); the stored blob kind ('image' | 'text') drives the actual
# resolution. 'document' (not 'text') keeps the placeholder type from
# colliding with a real text content part on the dict round-trip.
ref_kind = "image" if str(att.get("kind") or "") == "image" else "document"
# AttachmentRef.kind is the by-reference placeholder kind: the stored
# blob kind verbatim for image / pdf / audio, else 'document' for a
# stored 'text' blob (so the placeholder type can't collide with a real
# text content part on the dict round-trip). The blob kind drives the
# actual resolution; preserving pdf / audio here keeps the reloaded
# placeholder type ({type:pdf} / {type:audio}) consistent with the live
# injection path, which already emits those.
kind_str = str(att.get("kind") or "")
ref_kind = kind_str if kind_str in ("image", "pdf", "audio") else "document"
refs.append(
AttachmentRef(
attachment_id=str(att.get("attachment_id") or ""),