Jobs OS
How it works, and how to test it.
This page is the manual. The live system is the chat, the lab form, the job card, and the office board. Use a throwaway email.
Open the chat Open the lab form
What this is
Jobs OS is one operating system for a labelled Gold Coast screen company, Harbour Screen Co. A hiring manager can follow a job from inbound to closed, including three must-nots: must not quote unbounded work, must not mail a contact who already replied, must not invoice an amount that is not the accepted quote.
It is not six n8n tutorials. One state machine, one catalogue, one eval pack, one audit log, one office board. Chat lives only on the demo pages. The public contact form on this site is still HubSpot.
How to test it
Use a unique test email each run so identity does not merge onto an old job. Suburb Burleigh Heads is in the service list.
1. Chat (this is the chatbot)
- Open the chat. Enter a test email. Suburb can be Burleigh Heads.
- Tap Two screens in Burleigh, or type a normal sentence: couple of bedroom flyscreens, about 900 by 1200, we are in Burleigh. Send. Status should become Quoted at $290 (two FS-M).
- Click New chat, new email. Send: can you quote the whole house? Status Refused. Total $0. No SKUs.
- New chat. Send: need a security door. Office should ask if 2040 x 820 is standard. Reply yes, Burleigh. Status Quoted at $890 (SD-STD).
- Keep sending short replies on that job. The fourth office turn is the cap. Fifth visitor message should move the job to NeedsHuman.
- Enter sends. Shift+Enter makes a new line. The transcript stays in this browser tab until you hit New chat.
2. Lab form (identity)
- Open intake. Name, email, Burleigh Heads, two screens 900x1200 black. Submit. One job, Quoted $290.
- Submit again with the same email and a follow-up with no size. Same job id. Submit count 2. Status stays Quoted.
3. Job card (money)
- Open the Quoted job from the board. Draft invoice while still Quoted must fail (Quoted cannot jump to Invoiced).
- Accept quote, mark won, draft invoice. Amount $290, match pass, status Invoiced. No charge. Demo draft only.
- On a different Quoted job, walk to Won, then Draft $1 (must fail). Status NeedsHuman. Draft discarded.
4. Follow-up (rules, not a model)
- On a Quoted job with no later inbound, Run day-4 follow-up. Draft queued, sent false, status still Quoted.
- On a Quoted job, send a second inbound first, then run day-4. Skipped. Reason includes no_reply.
- Walk a job to Invoiced, Close (paid), then Run 90-day follow-up. Status Reengaged. Draft not sent.
- Close unpaid, or opt out, or open another job on the same email: 90-day mail is blocked.
5. Board digest
- Open the board. NeedsHuman count must match the Needs a person table.
- Copy an audit id from that table. Paste it into Replay. The JSON row is that decision. Email is not in the digest buckets.
Automation vs AI
A model may read messy language. It may not send mail, take money, or change status on its own.
| Step | Tool | Why |
|---|---|---|
| Email and phone match | Lookup | Known identity, one open job |
| Allowed status moves | State machine | Quoted cannot jump to Invoiced |
| Catalogue unit price | File read | The model may not invent a row |
| Invoice amount | Comparison | Must equal the accepted quote |
| Follow-up eligibility | Rules table | Days, opt-out, unpaid, open job, reply |
| Digest counts | Tally | Must match the jobs file |
| Qualify messy text | AI or rules | Unstructured inbound |
| Quote wording | AI then price-check | Wrong dollars are discarded |
| Send, charge, status | Automation | Never the model |
State machine
Statuses live in states.json. Illegal transitions are blocked in PHP. The happy path for a complete brief is New to Qualifying to ReadyToQuote to Quoted, then office moves Booked to Won to Invoiced to Closed. Closed may move to Reengaged after the 90-day rules pass. Lost and Refused do not reopen.
Catalogue and refuse
Prices are demo AUD in catalogue.json. Two FS-M at 900x1200 is $290. A whole-house, heritage, shopfront, or structural brief is refused in code. A model cannot override that list. Oversized (for example 1800x1800) or an unknown colour (navy) goes to a person with no dollar figure.
Identity
An open job matches on email first, then phone, then name plus suburb. A second inbound on the same email stays on that job. Closed jobs are not open, so a new enquiry on the same email can create a second job. That second open job blocks 90-day reactivation mail on the closed one.
Chat rules
Chat posts to the same qualify and quote path as the lab form. Email is required before a job can be Quoted. The office is capped at four assistant turns. After that the job is NeedsHuman. Chat is not in the site header and not on /contact/.
If an OpenAI-compatible key is on the server, chat uses a language model to read messy wording and to draft the office bubble. Short follow-ups like 900 by 1200 are not treated as spam. The model does not send mail, invent a catalogue row, or override a refuse. Without a key, the same gates run on rules, including sizes written as 900 by 1200.
Follow-up
Eligibility is a table. Day-4: status Quoted, four or more days since quoted_at, not opted out, no inbound after the quote, not already queued. Status stays Quoted. 90-day: status Closed, ninety or more days since closed_at, not opted out, no unpaid invoice, no other open job, no reply after close. Then Closed to Reengaged. Drafts set sent: false. The demo clock is as_of on the job-card buttons, so you do not wait 90 real days.
How it was built
- Static HTML for the site, same header and CSS as the rest of alanvo.com. Demo files live under /demo/jobs-os/.
- A small PHP API at /demo/jobs-os/api/. Jobs are a JSON file. Every decision appends a row to audit.jsonl with an aud_ id.
- Rules first. Chat qualify and quote can use an OpenAI-compatible key on the server for messy language and office wording. Price-check, invoice match, follow-up, and digest never call a model.
- Shipped in six slices: spine, qualify plus chat, quote plus price-check, accept to invoice, follow-up, office digest plus this write-up.
- Eval pack and live tests hit the public API with unique jobs-os-*@alanvo.com addresses. Refusal with dollars fails. A replied contact getting day-4 mail fails. Invoice $1 against $290 fails.
Source of truth for catalogue and states is also in the repo under proof-projects/ai-automation/jobs-os/. The old webhook in front of /contact/ was reverted. HubSpot stays the public form.
What it will never do
- Replace the HubSpot form on /contact/.
- Send the follow-up drafts. They queue only.
- Charge a card. Invoice is a demo draft.
- Let the model invent a catalogue row, override a refuse, or jump Quoted to Invoiced.