FocusGate
Staying reachable without staying distracted.
Do Not Disturb decides on sender. The decision that actually matters is on content — so move it to a server and let it fail toward delivery.
- Builder
- v1 relay backend, unvalidated
- 2026
- Inbound webhook · operator dashboard
- v1 relay backend and operator dashboard, running locally. The concierge test that would validate it has not been run.
A working student sits down to study. Their manager might text about a shift that pays their rent, or might text nothing important for three hours. Focus mode blocks the app, so both are silenced; turning it off means checking the phone every ten minutes. Neither option is acceptable, so they pick the worse one and stay distracted.
The operating system already does app blocking well and for free. What it cannot do is read the message. FocusGate owns exactly that gap and nothing else.
Message routing
To the relay number, not the student
Signature verified in production
Block state machine
Forward normally. Nothing is triaged, nothing is read.
Triage the content, then deliver, or hold and auto-reply once.
Work messages flow through a number the relay controls. Triage happens server-side, which is why the same backend works on both mobile platforms with no notification interception.
Why a relay and not an app
The obvious build is a native app that intercepts notifications and decides which to show. On iOS that is not permitted, and on Android it is fragile. More importantly, it duplicates something the platform already does better.
Routing the messages through a number the system controls moves the decision to a server, where it is one code path for every platform, inspectable by an operator, and testable without a device. The trade is that the student has to tell their manager to use a different number — which is precisely the assumption that has not been validated yet.
The urgency decision
Contains an explicit urgency keyword
Deterministic floor. Runs before the model, so it holds when inference does not.
Model judges it urgent
Content-level judgment — the thing the operating system cannot do.
Model judges it not urgent
The sender learns the student is studying, so they do not escalate.
Error, timeout, or ambiguity
A false interrupt is annoying. A missed shift offer costs money and trust.
Four paths, three of which deliver. The deterministic keyword floor runs ahead of the model, so the most urgent cases never depend on inference being available.
Every uncertain path biases toward delivery. A false interrupt is mildly annoying; a missed shift offer costs money and trust, and trust is the whole product.
Fail open, deliberately
The system fails open on every error, timeout, and ambiguous verdict. That is an unusual default for a filter — most filters fail closed and drop the uncertain case — and it comes directly from the asymmetry in the cost of the two mistakes.
It also sets the product's ceiling honestly. FocusGate can only be as valuable as it is trusted, and trust is destroyed by exactly one missed urgent message. Optimising the model for precision at the expense of that would be optimising the wrong thing.
What the backend does
Block state machine
Study blocks start and stop from an operator dashboard. Outside a block nothing is triaged at all — messages forward normally.
Hold and auto-reply
A held message auto-replies once per sender, so the manager knows the student is not ignoring them and does not escalate.
End-of-block digest
Everything held is delivered as a single summary when the block ends.
Webhook verification
Inbound webhook signatures are verified in production against the public base URL, so the relay cannot be driven by forged requests.
The test that has not been run
The pain is real — working students described it unprompted. What is unproven is whether they will route their texts through a different number and then trust the result enough to stop checking their phone.
That is a concierge test with one real student and their real manager, not a codebase. It is written up in the repository as the next step, and until it runs, everything here is a well-argued hypothesis with a working implementation attached.
What I built
Solo build. The most valuable decision was about scope rather than code:
Made the call to relay rather than block — identifying that the operating system already owns app blocking and that content-level judgment was the only unclaimed gap.
Designed and implemented the triage path: a deterministic keyword floor ahead of the model, a model verdict for the ambiguous middle, and fail-open behaviour on every error and timeout.
Built the block state machine, held-message queue, once-per-sender auto-reply, and end-of-block digest.
Implemented the inbound webhook handler with production signature verification against the configured public base URL.
Built the operator dashboard for starting and stopping blocks and inspecting what the triage decided and why.
Wrote unit tests for triage and the block state machine, and scoped the repository explicitly for a concierge validation test rather than for scale.
What remains unproven
- The concierge test has not been run. No student has routed real messages through it during a real study block.
- Triage quality has not been measured against real messages — only against unit tests I wrote from my own assumptions about what urgent looks like.
- The adoption assumption is the weakest link and I know it: asking someone to tell their manager to use a different number is a real cost, and it may be the thing that kills the product rather than any classification error.
What I took from it
Name the constraint before choosing the architecture
Once trust was identified as the binding constraint rather than accuracy, fail-open stopped being a compromise and became the specification.
Do not rebuild what the platform gives you
The temptation was to build a full focus app. Everything except content judgment already existed and worked better than a rewrite would have.
Scope the repository to the next question
The backend is deliberately small because the next thing to learn is behavioural, not technical. Building for scale first would have been building for a question nobody had answered.
Stack
- The product decision to relay rather than block
- Server-side triage with a deterministic trust floor
- Fail-open routing on every uncertain path
- Block state machine, held messages, and end-of-block digest
- Twilio signature verification and operator dashboard