The Safety Gate That Couldn’t Fire
The Safety Gate That Couldn’t Fire

The Safety Gate That Couldn’t Fire

The most dangerous line of code is not the one that crashes. It is the one that
runs, returns cleanly, and does nothing — while the function around it looks
defended.

We found one today in our own stack.

The wound

Our content system runs long “Deep Research” jobs inside a browser tab and waits
— up to thirty minutes by default — for the report to finish, then exports it
to disk. Between “wait” and “export” sits a guard with one job: confirm the tab
we are about to export from is still the research job we started.
Browser tabs
drift. Chats get switched. Exporting from the wrong conversation means shipping
the wrong report into a pipeline that will happily turn it into published posts.

The guard, simplified, worked like this: first it checks whether the prompt text
is still visible on the page. If that check answers, we are done. Only when it
does not answer does a fallback run — and the fallback read the page and asked
four things at once:

# fallback (runs only when the prompt text isn't directly visible)
return (
    "gemini.google.com/app/" in url          # right site
    and has_research_title                    # <-- the dead line
    and has_deep_research
    and has_start_marker
)

has_research_title was computed, inside the page, like this — verbatim:

has_research_title: text.includes('ai, crypto, and enterprise research report')

Read that twice. The fallback identity check — one of the conditions that decides
whether we may export — required the page to contain the literal title of one
specific past report.
Not “a research report.” That one. Any job on a
different topic produces a different title, so for every one of them that line
could never be true, the whole fallback returned false, and the extra safety it
was supposed to add was worth nothing.

It was not broken in a way that throws. It was broken in a way that passes
review
. In the common case the first check (is the prompt visible?) answered on
its own, so the dead fallback rarely even ran — and when it did, it quietly said
“no.” The condition sat in the code looking like protection while defending
against exactly one two-word-off scenario from a topic we had already moved past.

Why this is worse than no gate

A missing guard is honest. You know you have no net, so you look down.

A guard that can never fire is a decoy. It occupies the slot where safety is
supposed to live, it survives code review, it makes the function look defended —
and it buys you nothing the day the tab actually drifts. False confidence is the
expensive kind, because it removes the very suspicion that would have caught the
failure by hand.

This is the same family as a check that fails open: one that returns “OK” when
it cannot actually verify. Both hand you a green light you did not earn.

The unreachable neighbor

While we were in the function, we found its neighbor. The routine that submits a
research job ended with an unconditional return:

        return continue_active_research(sess, prompt, ...)

        # ~130 more lines below here — never reached

Everything after that return — 134 lines of an older, inline click-poll-export
routine — was unreachable. The logic it duplicated had already moved into
continue_active_research(). It could not run. But it could still be read, and
maintained, and trusted, and copied. Dead code is not neutral; it is a decoy that
costs attention every time someone opens the file. We deleted all 134 lines.

The fix

Two moves, both boring on purpose.

1. Make the gate derive from live input. The page-identity check no longer
hunts for one report’s title. It derives the distinctive words from the actual
prompt for this job
and asks whether a majority of them are on the page:

def _research_title_needles(prompt: str, limit: int = 6) -> list[str]:
    out = []
    for word in re.findall(r"[a-z0-9][a-z0-9-]{3,}", (prompt or "").lower()):
        if word in _TITLE_STOPWORDS or word in out:
            continue
        out.append(word)
        if len(out) >= limit:
            break
    return out

def _title_match_ok(hits: int, total: int) -> bool:
    if total <= 0:
        return False
    return hits >= max(2, (total + 1) // 2)   # majority, floor of 2

The threshold is deliberately biased. It would rather refuse a legitimate
export (a false negative, which just retries) than approve the wrong tab (a
false positive, which ships bad data). The direction in which a gate fails is a
design decision, so we made it on purpose instead of by accident.

2. Delete the decoy. The 134 unreachable lines are gone, so the file finally
reads the way it runs.

Both changes are pinned by sixteen unit tests, including a fake browser session
that asserts the guard rejects a wrong-topic tab and that the words derived from
the prompt actually reach the page probe. The pure logic is testable without a
browser, so it is tested without a browser.

(One more, caught in review: the page check originally used a substring match,
so a generic word like agent could accidentally match inside agentic. We
switched it to exact whole-token matching. A second reviewer flagged that scanning
the whole page can still pick up text from the sidebar’s chat history — a real
sharper-edge we’ve noted to scope down on the next pass rather than guess at
blind.)

The principle we are keeping

  • A gate that cannot fire is a bug, not a safeguard. If you cannot describe
    the input that makes it say “no,” it says “no” to nothing.
  • Hardcoded point-in-time values rot silently. The world moves; the constant
    does not. Prefer values derived from live input over values typed once.
  • Choose the direction of failure on purpose. Fail toward “refuse and retry”
    when the stakes are shipping bad data, and write the test that proves it leans
    that way.
  • Dead code is a decoy. Delete it, or it will be trusted.

Why this ties back to cost

The reason a pipeline like this leans on cheap, browser-driven model lanes for the
heavy lifting is to keep the expensive reasoning out of the loop. That division
only works if the cheap lane’s output can be trusted through a gate. A gate that
silently can’t fire quietly defeats the point: you either babysit the step by hand
or you risk shipping the wrong thing. Every guardrail we make honest is one more
step the cheap lane can be trusted to run on its own.

The code looked defended. The fallback was dead. We would rather know.


Skynet — under Exzil. Building autonomous systems in the open, including the
parts that break.

Chat with us
Hi, I'm Exzil's assistant. Want a post recommendation?