sift
sift reads your team's shared inbox and sends each email to the person who should handle it. It spots the ones with a deadline hidden inside, like a city letter that gives you 14 days to respond, and writes a draft reply for someone to check and send.
the problem
The shared inbox fills up overnight. A contractor question that blocks Thursday's concrete pour, two consultant invoices, a plan-review letter from the city with a 14-day clock that reads like a form email, vendor spam, and a client asking for the second time why nothing has moved.
Someone sorts all of it by hand every morning. The letter that looks routine gets filed under “read later”, and its clock runs anyway. That sorting, and the clock hiding inside it, is what sift takes.
the tool you already run
Shared-inbox tools already ship AI triage. Missive lists plans at $14, $24 and $36 per user a month, with AI running on your own API key. Front lists $25, $65 and $105 per seat a month, with AI add-ons at $10 to $20 per seat. If your team shares an inbox, you probably already pay for one of them.
sift does not replace any of that. It is a retrofit: the inbox tool keeps running, and a person still sends every reply from it. This adds the part that needs to know your firm.
where it stops
Generic triage can tell a question from a pitch. It cannot tell that this question is about the project pouring concrete on Thursday, that the RFI it names is already past its response window, or who on your team holds that project. That needs your schedule, your RFI and submittal logs, and your contacts.
For the checkable version of the gap: Missive and Front ship AI-assisted triage; reaching a firm's project, RFI, submittal and CRM records to judge firm-specific urgency and draft with live data is not part of that, as of their pricing pages on 2026-09-21. That is a statement about published pages on a date, and nothing more.
what sift does with a message
sends it to the right person
an RFI to the project's coordinator, a submittal to the reviewing architect, a bill to accounting, a city letter to the principal. a message about two things reaches both people. vendor pitches reach no one.
ranks it from your own project data
urgency comes from the firm's schedule and logs, not from words in the email. an RFI on a project with a pour on Thursday outranks one on a project in design, however calm it sounds.
flags every clock
a separate question asks only whether a contractual or regulatory clock has started. it sits on a deliberately low line: a false alarm costs a glance, a missed clock costs a deadline.
drafts the reply, never sends it
for RFIs, submittals and client check-ins it drafts an answer from the project records: the log number, the due date, who has it. a person reads it and sends it from the tool you already run.
hands you the unsure ones
when it is not confident, the message goes to a person with the matched project rows and every reason attached, instead of guessing. that path is a normal outcome, not an error.
the numbers
Measured on a fixed inbox of 91 hand-labelled messages for an architecture firm, 30 of them carrying a real clock. The three numbers below are printed together on purpose: flagging every message would catch every clock too, and would show up here as a pile of false alarms. No before/after time saving is claimed, because the old way was never timed.
The weak spot is invoices. The model reads payment terms like “Net 30” as a running clock, which accounts for 5 of the 11 false alarms and most of the invoice routing misses. The labels were kept as written rather than bent to agree with the model.
The demo switches between seven trades: architecture, law, property management, dental, accounting, general contracting and recruiting. Each has its own hand-labelled inbox and its own measured run. The technical track has all seven.
the stack
one message, six stages, shared across every build
Only classify spends. decide works out route, priority and deadline from the firm's records, and nothing a message says can widen what act is allowed to do.
how the judgment is shaped
The classifier asks one yes/no question per topic, not one pick-one question across eight labels. A thread that is both an RFI and a client chasing progress has to reach two people; a pick-one answer would route it to one and drop the other. Topic classes come from what the code must do differently: if two classes triggered the same route, priority and action, they would be one class.
The clock is its own question, separate from topic, because the hardest case is a clocked item whose surface looks routine. Its line is set low on purpose. It earned that: on the ordinary set the model named only 2 of 6 agency letters as agency letters, yet every clocked one still raised an alert.
| act | 0.55 | a topic applies and its route runs unattended |
| review | 0.50 | between this and act, the topic goes to a person |
| clock | 0.50 | a clock alert goes to the principal |
Priority is computed, not judged. The model never says how urgent something is; code looks up the matched project's next scheduled activity, the RFI's contractual response window, the submittal's review date, and ranks from those. That is what makes urgency firm-specific instead of a reading of tone.
The decision is plain TypeScript, the same function in the pipeline and in the browser. Because only the probabilities are bought, the dashboard's autonomy slider costs nothing to move: the judgment is unchanged and only the policy re-runs. Left, it checks more with you; right, it handles more on its own.
what breaks, and what happens
- Idempotency on every routing record, alert and draft, keyed on the message and the topic, so a two-topic message writes two records and a rerun writes none.
- Retry with backoff on every model call; an exhausted retry is written to the audit log as a failed result, never a silent success.
- An audit row per decision: one for each stage that ran and one for each action taken.
- A hard spend cap per run; the guard refuses a call before it spends. Both scored runs together cost under a cent.
- Escalation is a normal outcome. Below threshold, ambiguous, or nothing confident: a person gets the message, the matched project rows and every probability.
the numbers, per class
Measured on a fixed test inbox of 91 messages for an architecture firm, run 2026-09-23, model jev-1.13.0. 30 carry a real clock, 9 of them disguised as routine mail; 26 are deliberate boundary cases. There is deliberately no single accuracy figure: vendor pitches are the plurality, as in a real shared inbox, and a system that called everything a pitch would post a respectable overall number while missing every expensive message.
| clocked messages that raised an alert | 100% (30/30) |
| unclocked messages that raised one anyway | 18% (11/61) |
| handled with no person asked | 69% (63/91) |
| class | n | precision | recall | routed | priority |
|---|---|---|---|---|---|
| rfi | 12 | 100% (11/11) | 92% (11/12) | 83% (10/12) | 92% (11/12) |
| submittal | 8 | 100% (8/8) | 100% (8/8) | 100% (8/8) | 100% (8/8) |
| agency_letter | 6 | 100% (2/2) | 33% (2/6) | 100% (6/6) | 83% (5/6) |
| invoice | 6 | 100% (5/5) | 83% (5/6) | 17% (1/6) | 17% (1/6) |
| client_status | 6 | 86% (6/7) | 100% (6/6) | 100% (6/6) | 100% (6/6) |
| other | 7 | 78% (7/9) | 100% (7/7) | 100% (7/7) | 100% (7/7) |
| vendor_pitch | 12 | 100% (12/12) | 100% (12/12) | 100% (12/12) | 92% (11/12) |
| internal | 8 | 80% (8/10) | 100% (8/8) | 100% (8/8) | 100% (8/8) |
| class | n | precision | recall | routed | priority |
|---|---|---|---|---|---|
| rfi | 5 | 100% (5/5) | 100% (5/5) | 60% (3/5) | 100% (5/5) |
| submittal | 4 | 80% (4/5) | 100% (4/4) | 100% (4/4) | 100% (4/4) |
| agency_letter | 5 | 80% (4/5) | 80% (4/5) | 100% (5/5) | 80% (4/5) |
| invoice | 2 | 100% (2/2) | 100% (2/2) | 50% (1/2) | 50% (1/2) |
| client_status | 3 | 75% (3/4) | 100% (3/3) | 33% (1/3) | 67% (2/3) |
| other | 2 | 25% (1/4) | 50% (1/2) | 100% (2/2) | 50% (1/2) |
| vendor_pitch | 6 | 100% (6/6) | 100% (6/6) | 83% (5/6) | 67% (4/6) |
| internal | 2 | 40% (2/5) | 100% (2/2) | 0% (0/2) | 0% (0/2) |
Invoices are the weak spot. Every invoice that states payment terms (“Net 30”, “due on receipt”, a dated late fee) scored 0.86 to 0.97 on the clock question; the two that state none scored under 0.2. The labels say a bill's date is accounting's calendar, not a response clock. That is 5 of the 11 false alarms and almost every invoice miss, and the labels were kept as written.
Run 1 is kept, not replaced. Its class questions carried generic criteria instead of the boundaries the labels were written to, and it handled only 37 of 91 with no person asked. The criteria were rewritten from the labelling rules committed before run 1, and no label changed between runs. Cost was 0.0050¢ per message, median machine time 465 ms.
seven trades, one codebase
The demo's firm switcher is not a reskin. Each of the other six trades has its own hand-labelled inbox of about 90 messages and its own records, built to the same bar, and was scored the same way at the lines its own sweep chose.
| trade | n | clocks flagged | false alarms | no person asked |
|---|---|---|---|---|
| law firm | 90 | 89% (24/27) | 11% (7/63) | 77% (69/90) |
| property management | 92 | 90% (26/29) | 10% (6/63) | 76% (70/92) |
| dental practice | 90 | 96% (27/28) | 15% (9/62) | 90% (81/90) |
| accounting firm | 90 | 100% (25/25) | 15% (10/65) | 77% (69/90) |
| general contractor | 90 | 100% (27/27) | 16% (10/63) | 69% (62/90) |
| recruiting agency | 90 | 84% (21/25) | 6% (4/65) | 78% (70/90) |
the code
The whole decision is deterministic code over the probabilities: routing, priority and deadlines come from the firm's records, and a draft is a template that never reads the message body. There is no send path and no write path, by construction. The pipeline, the policy, every trade's labelled inbox and every committed run are in the public repo, linked in the demo header above.
Someone sorting the shared inbox by hand every morning? I'll tell you in 15 minutes whether a system can safely take it off you.
see if sift fitsfree · 15 min · “no, it can’t” is a real answer