Scúp
A business operating system for owners, which reads what is already happening across their email and accounts and returns a short list of what actually needs attention today.
Evidence for
- AI integrations and automation
- Custom business systems
- Internal tools and operational software
Built on
- React
- TypeScript
- Supabase
- PostgreSQL
- Deno edge functions
- Gmail API
- Xero API
Measured
- Tables
- 53
supabase/migrations- Row-level security policies
- 66
grep: create policy- Server-side jobs
- 23
supabase/functions- State enums in the database
- 9
grep: create type
Its screens are full of real customer correspondence belonging to businesses that are not ours. None are published here, and none are retouched — an anonymised screenshot of a real inbox is still a real inbox. What is shown is the architecture.
The problem it was built for#
An owner's information is spread across email, accounting, quotes, calendars and conversations. Nothing is missing, exactly. But working out who to chase, what money is likely to land, and where opportunities are quietly dying is manual — and it is the first thing to slip in a busy week.
The obvious product here is a dashboard that aggregates everything. We think that product is worthless. An owner who is too busy to read their inbox is not going to read a second, prettier inbox.
Connecting the systems is the easy half#
Mailbox and accounting are connected over OAuth, synchronised incrementally, and kept in their own state tables so a failed sync resumes rather than restarts. That work is fiddly and well understood. It took a fraction of the time.
The difficult half is everything after the data arrives:
- Classifying intent in real correspondence, where a reply saying "sounds good, send it over" is an acceptance and a reply saying "thanks, will look" is nothing at all.
- Reconciling quotes and invoices across two sources of truth that disagree, because one is the mailbox and one is the ledger and they were never designed to agree.
- Resolving people — the same human writing from three addresses, quoted under a company name, invoiced under a trading name.
- Removing transactional noise: receipts, newsletters, calendar invitations, automated notifications. Most of a business mailbox is not correspondence.
- Deciding what deserves attention, which is the only part the owner sees.
Everything on the list has to survive one question#
Why is this on my list?
That constraint shaped the architecture more than any other. Three things follow from it.
Every automated run leaves a receipt. Not a log line — a structured account of what the run looked at and what it decided: how many threads were watched, how many were surfaced, how many were deliberately rested, how many were ignored as automated, which quotes were detected, which chases fell due, which replies were noticed, which files were closed, which phantom records were cleared. If the owner disagrees with the list, the receipt says how it was arrived at.
Every pipeline stage is traced. A trace carries a stage-by-stage record with timings, and on failure it names the stage that failed rather than reporting that something went wrong. Software that acts on your behalf and cannot say what it did is not something we would ask a client to trust.
The state model lives in the database, not in prompts. Relationship state, quote status, quote source, match method and the rest are Postgres enums. A language model can propose a transition; it cannot invent a state that the schema does not have.
Autonomy is earned, and fenced#
The part we are most pleased with is the permission model, because it is the answer to the question everyone actually has about this kind of software: what will it do without asking me?
Scúp does not ship with autonomy switched on. It offers to take over a specific
class of work — chase_quote.first_chase, say — only after it can cite the
evidence that it should: how many times you approved that exact action, how
often you sent its draft unchanged, over how many weeks. The offer carries that
provenance with it.
If accepted, the grant is fenced by a scope derived from your own behaviour: a ceiling on the value it may act on, a cap on how many it may send in a week, and a restriction to contacts you already correspond with — which is not configurable and is always on.
We test the judgement, not just the code#
The hard part of this system is not whether a function returns the right shape. It is whether the thing it decided was the right decision — and that is not something a unit test can assert.
So there is a separate evaluation job that scores pipeline output against expected judgements, run as part of the system rather than as a notebook on someone's laptop. It is the only honest way we know to change a classifier and find out whether it got better or just different.
What we learned#
The software has to earn the right to interrupt. An assistant that surfaces everything is just another inbox. The discipline is entirely in what it decides to stay quiet about — and almost none of the engineering effort went into finding things, which is easy. It went into the far harder problem of throwing almost all of them away.
The second lesson is narrower and cost us more time: two sources of truth never reconcile cleanly, and pretending they might is how you build a system that is confidently wrong. The mailbox says a quote was accepted; the ledger has no such invoice. Both are correct. Modelling that disagreement explicitly — recording the decision and what it was based on — was the change that made the rest of it work.