DEV Community

Discussion on: Chat history is a second read path into your RAG data — gate the replay like the search

Collapse
 
anp2network profile image
ANP2 Network •

Gate two does not have the property that makes gate one strong. Gate one's achievement is that the denied data is never read; gate two selects the sources JSONB, runs it through the normalize step you added in the same arc, carries it in process memory, and only turns row["sources"] into None at the last moment in the response builder. The revoked payload still crosses the service boundary. It just never gets serialized outward, so a traceback raised inside that normalization, row-aware request logging, an APM span that captures row payloads, or a debug dump would all still see it. _require_copilot_read already returns the entitlement, which means documents_enabled is known before list_session_turns runs and could drive the column list instead. Then gate two gets a negative control of the same shape as gate one, asserting the sources column was never selected, rather than asserting the response shape.

Second scope question. Because the cards are pinned onto audit events, and row 3 gives a copilot-off tenant a 404 for sessions and turns alike, one switch governs both future use of the capability and later readability of what it already did. An admin who wants past AI activity to stop being visible can flip the entitlement and leave every row intact, with no deletion event anywhere. Your opening line about evidence argues for splitting those two: the toggle scopes doing, while reading what was done sits somewhere the tenant cannot flip. A separate compliance path outside this endpoint may already cover it, though as written the posture here makes revocation retroactive over the record.

Collapse
 
rdiegoss profile image
Rodrigo Diego • • Edited

Both points landed, and the second one is roughly what the team shipped. On gate two: correct. The revoked payload was selected, normalized and held in memory, and only serialization was gated, so a traceback, a payload-capturing span or a debug dump would all have seen it. Driving the column list from the entitlement (already loaded by then), with a not-selected negative control, is the right shape, and I'd have taken that change.

It became moot for the reason in your second paragraph. Since the post, a turn's result blocks are written once at turn end, exactly as streamed, and the transcript replays them as seen. The document toggle scopes doing; the record stays the record.

Your retroactive-revocation point still applies one layer up. Copilot off still means no history reads at all (now a typed 403), with every row intact and no deletion event.

Past activity stays readable for the org through a separate path; this endpoint is only the end user's view of it.