{"spec":"AIP-BS-JOB-BRIEF","version":"2026-11-01","standard":"job-brief-build-standard","source":"https://aipathway.com.au/explore-ai/job-brief-build-standard","markdown":"https://aipathway.com.au/explore-ai/job-brief-build-standard.md","licence":"CC BY 4.0","how_to_read":"Seven of these nine checks run on your own machine, against the stub, before you talk to anybody. The last one is a live tenant, and that one runs against your own live systems. One is not a software check at all: it asks what you measured before you started.","verdicts":{"pass":"asserted green here","fail":"asserted red here. This is a fixture: it names the check to price","shape_only":"shape holds on the stub, truth needs the live provider","with_us":"cannot be proved on the stub; runs against your own live systems (see with_us_next)","evidence_required":"not a software check. Attach what proves it"},"tally":{"total":9,"onStub":7,"productionOnly":1,"partial":1,"preconditions":1},"envelope_version":"1.0","port_shape":{"read_ledger":{"verb":"read","subject":"ledger"},"read_job":{"verb":"read","subject":"job"},"read_asset":{"verb":"read","subject":"asset"},"read_rule":{"verb":"read","subject":"rule"},"read_contacts":{"verb":"read","subject":"contacts"},"read_segment":{"verb":"read","subject":"segment"},"resolve_address":{"verb":"read","subject":"address"},"write_job":{"verb":"write","subject":"job"},"read_pricebook":{"verb":"read","subject":"pricebook"},"draft_quote":{"verb":"write","subject":"quote"},"write_invoice":{"verb":"write","subject":"invoice"},"write_ledger":{"verb":"write","subject":"ledger"},"record_promise":{"verb":"write","subject":"promise"},"record_service":{"verb":"write","subject":"service_record"},"release_payment":{"verb":"write","subject":"payment"},"talk":{"verb":"say","subject":"turn"},"disclose":{"verb":"say","subject":"disclosure"},"send_sms":{"verb":"say","subject":"sms"},"notify":{"verb":"say","subject":"alert"},"escalate":{"verb":"say","subject":"handoff"},"issue_notice":{"verb":"say","subject":"notice"},"answer":{"verb":"say","subject":"answer"},"publish_report":{"verb":"say","subject":"report"},"record_consent":{"verb":"write","subject":"consent"},"read_file":{"verb":"read","subject":"file"},"write_file":{"verb":"write","subject":"file"},"raise_task":{"verb":"write","subject":"task"},"read_node":{"verb":"read","subject":"node"},"write_node":{"verb":"write","subject":"node"},"read_register":{"verb":"read","subject":"register"},"write_register":{"verb":"write","subject":"register"},"index_passage":{"verb":"attach","subject":"passage"},"search_index":{"verb":"read","subject":"index"},"read_inbox":{"verb":"read","subject":"message"},"file_message":{"verb":"attach","subject":"message"},"write_order":{"verb":"write","subject":"order"},"read_quote":{"verb":"read","subject":"quote"},"write_log":{"verb":"write","subject":"log"},"read_sales":{"verb":"read","subject":"sales"},"read_sale":{"verb":"read","subject":"sale"},"draft_message":{"verb":"write","subject":"draft"},"send_message":{"verb":"say","subject":"message"},"read_memory":{"verb":"read","subject":"memory"},"propose_memory":{"verb":"write","subject":"proposal"},"write_memory":{"verb":"write","subject":"memory"},"close_job":{"verb":"write","subject":"job"},"raise_exception":{"verb":"write","subject":"exception"},"list_exceptions":{"verb":"read","subject":"exception"},"evidence":{"verb":"attach","subject":"evidence"},"cite":{"verb":"attach","subject":"citation"},"rank":{"verb":"decide","subject":"order"},"gate":{"verb":"decide","subject":"gate"}},"provider":{"boundary":{"never":["put a line on a brief that does not name the record it came from and that record's date","summarise, reword or characterise a note, or add anything about a customer's mood or character","fill in an access, hazard or asset record the job system does not hold","put a price, a discount or a promise on a quote line","draw on another customer's records, even at the same address","send a brief on a channel the tech did not agree to, or send it twice for unchanged slots","write to the job, the quote, the slot or the note"],"stays_with_person":["what goes in the job system: the notes, the access and hazard records, the quotes","whether customer notes appear on the brief at all","the time the brief must arrive, and each tech's agreed channel","asking the customer on site about anything the brief shows as missing","anything said to the customer about an open quote"]},"job":"every tech starts the day knowing each site's history, with every line traceable to a record","skill":"job-brief","stub":"local.mock_job_history","production":null,"product":null,"insert":null,"with_us":{"kind":"own_systems","note":"No hosted step. The production check runs on the business's own job system: a real job with a real history, a record deleted there, and a rebuilt brief that no longer cites it. Office Voice's read_job reads one job by id today; it does not read the day's slots or a site's history, and it does not send the brief."}},"with_us_next":"No hosted step. The production check runs on the business's own job system: a real job with a real history, a record deleted there, and a rebuilt brief that no longer cites it. Office Voice's read_job reads one job by id today; it does not read the day's slots or a site's history, and it does not send the brief. There is nothing to buy: your build does this step on the business's own systems. No production run is offered yet, because the live port map is not published, so do not run AIPATHWAY_ENV=production. Run the pass test by hand on the real systems and attach the evidence.","default_path":null,"boundary":{"never":["put a line on a brief that does not name the record it came from and that record's date","summarise, reword or characterise a note, or add anything about a customer's mood or character","fill in an access, hazard or asset record the job system does not hold","put a price, a discount or a promise on a quote line","draw on another customer's records, even at the same address","send a brief on a channel the tech did not agree to, or send it twice for unchanged slots","write to the job, the quote, the slot or the note"],"stays_with_person":["what goes in the job system: the notes, the access and hazard records, the quotes","whether customer notes appear on the brief at all","the time the brief must arrive, and each tech's agreed channel","asking the customer on site about anything the brief shows as missing","anything said to the customer about an open quote"]},"substrate":{"how_to_read":"These are roles, not products. Work out which you already have by matching `providers[].detect` against the repository, then build or add only the gap. No check asserts against a vendor, so any substrate that answers the role passes the same checks.","requires":[{"role":"record","label":"Somewhere the book lives","need":"Durable storage that the workflow owns the write path to, keyed so the same record can be found twice.","when_missing":"Half a day. Any Postgres will do and the free tiers are ample at this size. The trap is not the database, it is letting people edit around the app that is supposed to own the value.","ports":["read_job","read_asset","read_rule","cite","read_quote","raise_exception"]},{"role":"schedule","label":"Something that runs it on time","need":"A trigger that fires on a cadence and can call an authenticated endpoint.","when_missing":"An hour. Expose one authenticated endpoint that starts the work and let anything call it, so the schedule is never welded to whoever is hosting you.","ports":["gate"]},{"role":"send","label":"Something that sends","need":"Email or SMS from a domain the recipient recognises, with delivery visible enough to tell a bounce from a silence.","when_missing":"An hour for the wiring, longer for the DNS. Sending from a domain you do not own is the single fastest way into a spam folder.","ports":["notify"]}],"registry":"https://aipathway.com.au/substrates.json"},"checks":[{"id":"jobs-from-the-slots","n":1,"title":"The jobs come from the tech's slots for the day, by job id","statement":"Each tech's brief lists exactly the jobs slotted to that tech for the day, by the job system's own job id, with the window copied from the slot. A job slotted to somebody else, an unslotted job at the same street, or a window restated from memory is a failure.","assertable":"stub","ports":["read_job"],"stimulus":"Two techs with slots on the brief's day, a third job slotted to one of them for the day after, and an unslotted job for a customer on the same street.","assertion":"Each brief holds exactly that tech's job ids for the day, and each job's window equals its slot's start and end. An extra job, a missing job, or a window that differs from the slot fails."},{"id":"every-line-has-a-source","n":2,"title":"Every line carries the record it came from and that record's date","statement":"Every line in the brief names the system, the record id and the record's date, and that record exists and belongs to the job's customer. A line with no record or no date is dropped, not shown. This is the rule that stops a fluent brief inventing history: Cited Answer's \"every claim carries its source\", applied to a site visit.","assertable":"stub","ports":["read_job","cite"],"stimulus":"A site with a past visit, an access note, and one note in the history that carries no date.","assertion":"Every line's source names a record the world holds, with that record's own date. The undated note appears on no line. A line whose source is missing, undated, or names a record that does not exist fails."},{"id":"quote-do-not-characterise","n":3,"title":"Notes are quoted or shortened, never reinterpreted, and no sentiment is generated","statement":"A note in the brief is the note's own words, or the first part of them cut with an ellipsis, with its author and date. Nothing is summarised into a claim the note does not make, and no word about the customer's mood or character is added. Where the business has set customer notes to be left out, they are left out.","assertable":"both","ports":["read_job","read_rule"],"stimulus":"A customer note reading \"Customer was difficult about the invoice last time, wants a call before arrival\", run once with customer notes set to quote and once set to leave out.","assertion":"With notes quoted, every note line's text is the note's words or a prefix of them followed by an ellipsis, and carries the note's author and date. With notes left out, no line of kind customer_note exists. A line that rewords the note, or adds words it does not contain, fails.","stubLimit":"The stub proves a seeded note is quoted or cut, never reworded. Only the real job system's notes prove it holds against the long, messy prose techs and office staff actually write."},{"id":"hazards-and-access-first","n":4,"title":"Hazard and access lines lead each job, and a missing one is shown as missing","statement":"For each job, hazard and access lines come before every other line. A site with no hazard record or no access record shows that gap under missing, so the tech knows to ask. Nothing is filled in.","assertable":"stub","ports":["read_job"],"stimulus":"Two jobs: one site with a hazard note and an access note, one site with neither.","assertion":"On the first job, every hazard and access line sits before any other kind. On the second, missing holds both access and hazard and no hazard or access line exists. An invented gate code, or a gap not shown, fails."},{"id":"open-quotes-with-age","n":5,"title":"Open quotes on the site are listed with their age, and never priced","statement":"Each quote still open for the job's customer at that site is one line, with its quote id and its age in days from the quote's own date. Accepted, declined and expired quotes are not listed. The line carries no price, no discount and no promise: the tech may mention the quote, and the brief never sells it.","assertable":"stub","ports":["read_quote"],"stimulus":"A site with one open quote 41 days old, one accepted quote and one declined quote.","assertion":"Exactly one open_quote line exists, citing the open quote, stating its age correctly. No line cites the accepted or declined quote, and no line contains a dollar figure. A price on any line fails."},{"id":"client-material-stays-scoped","n":6,"title":"A brief for one customer never draws on another customer's records","statement":"Every record a brief cites belongs to the job's own customer, even at the same address. A house that changed hands keeps the old owner's visits, notes and quotes with the old owner. Reads are made by the job's customer id, never by the address alone.","assertable":"stub","ports":["read_job","read_quote","read_asset"],"stimulus":"An address where the previous owner had three visits, a note and an open quote, and the new owner has one visit and an asset record.","assertion":"No line cites a record whose customer is the previous owner, and every history, quote and asset read is made with the new owner's customer id. A line citing the previous owner's record fails."},{"id":"delivered-once-on-an-agreed-channel","n":7,"title":"Delivered once, on a channel the tech agreed to, by the set time, and again only when a slot changes","statement":"Each tech gets one brief for the day, on a channel they agreed to, no later than the business's set time. Running again with nothing changed sends nothing. When a slot changes, that tech gets exactly one fresh brief. A tech with no agreed channel gets nothing sent and is parked for a person, never sent on a channel somebody guessed.","assertable":"stub","ports":["gate","notify","raise_exception"],"stimulus":"Two techs with jobs tomorrow, one with an agreed channel and one with none. The run happens twice with nothing changed, then one of the first tech's slots moves and the run happens again.","assertion":"The first tech has exactly two deliveries, each after a channel check: one for the day at or before the set time, and one after the slot moved. The second tech has no delivery attempt, and one parked exception names them. A third delivery, a delivery with no channel check before it, or an attempt to the second tech fails."},{"id":"survives-the-builder","n":8,"title":"It survives the builder","statement":"Someone other than the builder can explain what it does, and it runs on an account the business owns.","assertable":"precondition","ports":["read_rule"],"stimulus":"No stimulus. The builder names who else can explain it and whose account the brief runs on.","assertion":"The runner reports what was declared and never scores it green on its own say-so.","stubLimit":"Not a runtime property. Software cannot prove a second person understands it, and outliving its author is precisely what is being asked about."},{"id":"pass-test","n":9,"title":"The pass test passes","statement":"On the real job system: tomorrow's job at a site visited before, with one open quote and no access note. The brief lists the last visit with its date and record, the open quote with its age, and no access note under missing. Delete the last visit's note and rebuild: that line disappears.","assertable":"production","ports":["read_job","read_quote","cite"],"stimulus":"A real job slotted for tomorrow in the business's own ServiceM8, Simpro, AroFlo or Fergus, then a real note deleted from it and the brief rebuilt.","assertion":"The first brief cites the real last-visit record by its id and date, the real open quote with its age, and lists access under missing. After the deletion, no line cites the deleted note and no line restates what it said.","stubLimit":"Deliberately unpassable on a stub. A stub holds whatever history you seed and forgets what you delete because you told it to. Only the real job system proves the ids and dates are the ones it issued, and that a deleted note is gone from the next brief rather than remembered, cached or rephrased by the model."}]}