Skip to content

2026-07-20: Fail open, fail closed

Fail open, fail closed — a technical pencil sketch in the Sanctum docs house style, hand-drawn black-and-white, wide 16:9. Two heavy doors set side by side in a stone wall of a haus cellar. The left door hangs wide open with a worn "READ" plate, warm light spilling through onto the floor. The right door is shut and barred with a "WRITE" plate, a single small deadbolt thrown. Tommy, the haus's force-ghost cat, sits between them facing the barred door, one amber halo resting on the thrown bolt.

Two surfaces touch the deal CRM. One reads. One writes. The audit that prompted this note meant to answer one plain question — do they both work? — and the honest answer came back split. The read side was solid. The write side had picked up a bad habit.

The dealflow pipeline is the reader. affinity-sync pulls both deal lists from Affinity into the local mirror once a night. dealflow-meeting-prep reads that mirror, joins in the live interaction history, and sorts every active deal into needs-you, scheduled, or cold. It never writes back. The only thing that writes to the live CRM is the browser extension. Hand it a LinkedIn conversation and it finds or creates the contact, then posts the messages as a note. The full shape of both surfaces lives in Sanctum CRM; the reader’s brief is Dealflow Intelligence.

That asymmetry matters more than it looks. A reader that guesses wrong shows you a slightly stale brief. A writer that guesses wrong leaves a permanent mark in the system of record.

Before it posts a note, the extension checks whether the conversation is already there, so it does not duplicate. The check fetched the contact’s existing notes and looked for a match. The bug lived in the failure branch. If that fetch errored — a timeout, a rate-limit, a blip — the code caught the error and returned “no existing notes found.” One line later, that reads as “safe to post.” So a transient network hiccup did not skip the send or raise a warning. It re-posted the entire conversation as a fresh duplicate, and reported success.

The comment above it said, in as many words, allow sending on error. Fail open. For a read that is a defensible default — show something rather than nothing. For a write it is exactly backwards.

The fix was to pull those two outcomes back apart. The notes fetch now propagates its failure instead of swallowing it. On that failure, the duplicate check returns a distinct “could not verify” flag rather than “not a duplicate.” The sender sees the flag and stops. It returns a retryable error instead of creating a note. The escape hatch is Force Send — an explicit override for the human who has looked and decided to post anyway.

Said as a rule: a read may fail open and guess, because the cost is a stale glance. A write must fail closed and stop, because the cost is a corrupted record. When you cannot tell whether an action is safe, not acting is the safe action.

The same pass hardened the request layer underneath. Every call to Affinity now carries a 15-second timeout. A hung request can no longer stall the extension’s worker indefinitely. A rate-limit or server error is retried with backoff, instead of silently dropping a field write. And the find-or-create step now reuses an existing company only on an exact name match. A fuzzy search hit used to be enough to link a contact to the wrong firm — the one mistake worse than a duplicate.

The dealflow side had its own, smaller version of the same lesson. Its rollback tool — the documented way to undo a bulk cleanup run — crashed on the most common undo log it was handed. It read the log’s shape after assuming it. And the pager that is supposed to make a pipeline failure loud would go quiet whenever the alert service itself was down. Nothing was written to disk to prove the page was ever attempted. Both now fail loudly. The rollback reads the shape first. Every page leaves a dated breadcrumb on disk, tagged sent or undelivered, so a silent alerter can no longer produce a silent failure. It is the same diagnostic asymmetry the daemon work first named in Asymmetric Doors: the safe default is not fixed, it depends on which way the risk runs.

The deal CRM is not a place for optimism. Optimism is a reader’s luxury.