For administrators
The exception queue
Bubble’s own version history is reconciled against BuildGate’s records, and anything unaccounted for becomes a dated finding somebody has to close. This is the layer that makes the control defensible.
What reconciliation does
On a schedule, BuildGate reads Bubble's own version history and compares it with what it recorded. A deploy that skipped the gate does not become silence — it becomes a finding with a name, a date and a queue entry.
This is the detective layer, and it is the strongest thing in the product. A browser extension can be disabled; a reconciliation that notices it was is what turns "we have a control" into something an auditor can test.
An unreviewed exception queue is worse evidence than no queue at all. It shows a control that detects and an organization that does not respond. Put a review cadence on this screen and keep to it.
What the findings mean
The name says what happened, because the remediation is different for each:
| Finding | What it means |
|---|---|
unlogged_deploy | A deploy appears in Bubble's history with no matching request. Somebody went around a gate that exists |
unapproved_deploy | A request existed but was never approved, or was rejected |
bypass_used | A break-glass code was used. The stated reason is on the finding, with whether the code was recognised (or that none are configured, so it could not be checked), the error that made the gate offer break-glass, and the extension version |
gate_not_enforcing | Bubble's live deploy timestamp advanced with nothing to account for it. This says the gate itself has stopped working — most likely Bubble renamed an endpoint — rather than that one person went around it |
unlogged_restore | A restore appears in Bubble's change log. Restores are not gated, so every one of these is unaccounted for by construction. Only raised for apps where restore observation is switched on |
observation_failed | Bubble's history could not be read at all. Recorded so that "we cannot see" is never reported as "nothing happened" |
deployed_content_mismatch | What shipped is not what was reviewed: the change moved on between the approval and the deploy. There was an approval and the gate did work — what is in question is whether it was an approval of this |
unapproved_merge | A merge into an app's source branch went ahead with no approved merge request behind it, on an app a ruleset gates merges for. Seen by the extension as it happened, or found afterwards in Bubble's change log. Nothing reached live yet: the next deploy will carry it, inside somebody else's diff |
unlogged_config_change | The live app's settings changed between two readings of them, and no deploy accounts for it — none went through the gate and none is in Bubble's own history in between. Either a setting reached live without a deploy, or a deploy happened that has not been observed yet. The finding names the settings, and a switch's value before and after |
unobserved_config_change | A reading of a branch found a setting changed that no editor running the extension saw being changed — it was changed through the Bubble MCP, or from an editor without the extension. Raised only for an app the extension's live hook reports for, so it means a change went around the record rather than that nothing was watching. The finding names the settings and what they became |
The four that mean the control has stopped working — gate_not_enforcing,
unlogged_deploy, unapproved_deploy and
bypass_used — are sent to owners and admins as they are raised, and a
daily digest covers whatever is still open. Both need a notification channel: with
neither Slack nor email configured the queue is still there, but nobody is told
about it.
Reviewing and closing one
Clicking a finding opens it: its history, the change in Bubble that provoked it, the deploy request it belongs to where there is one, and a box to write in. Reviewers can comment while they work it, and closing one appends the note and the closer to the chain.
Resolving deletes nothing. The finding, the note and the name stay, and the resolution is appended to the record — you are not clearing a row, you are writing down what you checked. Neither a comment nor a resolution can be edited afterwards, which is what makes "we looked into this and here is why it was fine" worth reading a year later.
Approvers can also work this queue from their own tooling over MCP, with a separate pair of scopes from the approval ones: an agent allowed to see what is pending has not thereby been allowed to read, or close, the record of every time the gate was evaded.
A note on restores
BuildGate cannot stop a restore. The endpoint that performs one is undocumented and unknown to this system, so restore observation is exactly that — observation — and every observed restore is a finding by construction.
It is off by default, because turning it on for an app that is restored routinely fills the queue with findings nobody asked for. The consequence is worth stating plainly: with it off, an empty queue says nothing about restores. The Evidence screen's gate health card names the apps that are in that position, so the gap is visible rather than assumed.