Scan the Dispatch Seam, Not a List of Files
Jul 17, 2026
We had a shared secret scanner. We had a comment saying it covered every path that assembled context and sent it to an external model. Both were true right up until they weren't: one assembly path had quietly grown up without ever inheriting the scanner. Nothing detected the gap, because the claim of coverage lived in a comment, not in a test.
The fix was small. The lesson underneath it is not, and it generalises to any system that hands assembled input to an external model or tool.
The problem
Several code paths built up context — required inputs, optional inputs, prior-round rework — and passed it to an external adapter for execution. Most of those paths ran the assembled content through a shared scanner first, refusing to dispatch if it found something that shouldn't leave the building.
One path didn't. It assembled model-bound context and called the adapter directly. Same destination, same risk, no scan.
There was no malicious bug and no second, competing scanner. The single owner existed and worked. The defect was quieter: a path that never got wired into it, hidden behind a stale ownership claim.
This is worth separating from a different failure mode — duplicated security policy, where two owners drift apart and cause false alarms. That problem is solved by consolidating to one owner. This is the mirror image. The one owner already existed. The failure was a coverage gap: a path that bypassed it. Consolidation wouldn't have caught this; only proof of coverage would.
What actually happened
The instinct with "scan for secrets" is to enumerate the sensitive files and scan each one. That approach is brittle. The list goes stale the moment someone adds a new input, and it scatters the security decision across every place that touches a file.
The better move was to ignore the file list and find the one place all of these paths converge: the final seam where assembled context becomes an external dispatch. Every path, however it was built, funnelled through that seam on its way out. Protect the seam, and you protect every path — including ones that don't exist yet.
Concretely, the outbound context was decomposed into ordered, labelled segments. Those same segments were then reconstructed into the request and scanned before dispatch. If the scan refused, the adapter was never invoked, and the refusal came back through the existing refusal path — kept deliberately separate from the path that handles malformed model output, because "this input is unsafe to send" and "this output is malformed" are different failures and deserve different handling.
The hard part was never detection. It was preserving existing behaviour while inserting a new boundary in front of it: exact input ordering and byte fidelity, stable labels, diagnostics that propagated cleanly, and clean requests that still went through untouched. The scope was held tight on purpose — no scanner-policy changes, no prompt changes, no output scanning, no lifecycle redesign. Just one boundary, at one seam.
The lesson
Put the safety boundary at the final external-dispatch execution seam, not on a list of files.
A file list is a description of what's sensitive today. A dispatch seam is a structural chokepoint that every path is forced through regardless of how it was assembled. When you scan at the seam, new paths inherit the protection by default instead of requiring someone to remember to add them to a list.
The corollary is just as important: a shared-owner claim is only as good as the test that enforces it. Our comment said "all assembler families are covered." It was wrong, and nothing told us, because comments don't run. An executable assertion — an inventory test that fails when a dispatch path exists without a scan — would have caught the gap the day it was introduced.
The broader principle
Every path that sends assembled content to an external model or tool should have an explicit, testable pre-dispatch safety boundary. Not per-file. Per seam.
And ownership of shared infrastructure should be enforced by tests or inventories, not asserted in prose. "This is the one scanner everything goes through" is a claim about the whole system. Claims about the whole system decay silently unless something executable holds them to account.
Two smaller principles fall out of this:
- Clean-path fidelity is a proof obligation. When you insert a boundary in front of existing behaviour, "the safe input still gets through unchanged" is a test you owe, not an assumption. A boundary that also breaks the happy path is a regression wearing a security badge.
- Negative tests should prove the old behaviour can't survive. It isn't enough to show the new refusal works. You have to show that after a refusal, the external adapter was genuinely not invoked — that the bypass is closed, not just newly guarded.
How to apply it
If you're adding scanning — or any pre-dispatch safety check — to a system that talks to external models or tools:
- Trace the final external-dispatch seam first. Find where assembled context actually leaves for the model or tool, and list every distinct path that reaches it.
- Scan the thing you send. Prefer a single labelled segment collection that is both scanned and used to build the outbound request. If you scan one representation and send another, you've built a gap.
- Refuse before dispatch. The check has to sit in front of the adapter call, and refusals should reuse the existing refusal semantics rather than inventing a local one.
- Test the shape, not just the happy case. At minimum: one required input, one optional input, one rework input, proof of zero external invocation on refusal, safe diagnostics, no mutation, and clean-path equivalence.
- Decide the edges explicitly. Stub mode, dry runs, truncation, manual invocation outside the normal control path — say in writing whether each is in or out of scope, and why.
- Enforce ownership with an inventory. Add a static test that enumerates every dispatch path and asserts its scanner status. Replace "this is covered" comments with assertions that fail when it isn't.
- Record residual limits without expanding scope. Note what the boundary doesn't catch — secrets split across segment boundaries, paths outside the controlled seam — as explicit, bounded risks rather than reasons to redesign the scanner.
Security work like this is rarely about the cleverness of the detection. It's about placing one reliable boundary at the correct seam, preserving everything that already worked, and making coverage a property the tests enforce rather than a sentence someone hoped stayed true.