{"spec":"AIP-BS-ADVICE-FILE","version":"2026-10-03","standard":"advice-file-build-standard","source":"https://aipathway.com.au/explore-ai/advice-file-build-standard","markdown":"https://aipathway.com.au/explore-ai/advice-file-build-standard.md","licence":"CC BY 4.0","how_to_read":"Twelve of these fourteen checks run on your own machine, against the stub, before you talk to anybody. The last one is a live tenant, and that one is quoted as a scoped build. 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; the live step is quoted as a scoped build (see with_us_next)","evidence_required":"not a software check. Attach what proves it"},"tally":{"total":14,"onStub":12,"productionOnly":1,"partial":2,"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"},"evidence":{"verb":"attach","subject":"evidence"},"cite":{"verb":"attach","subject":"citation"},"rank":{"verb":"decide","subject":"order"},"gate":{"verb":"decide","subject":"gate"}},"provider":{"boundary":{"never":["be the source of a field, a task or a card","write a key the contract does not publish","answer lender policy from memory","move a recommendation past draft with a blocking gap or no card","occupy sent_by, waived_by, accepted_by or decided_by"],"stays_with_person":["recording and transcription consent on the call","the responsible-lending assessment and the not-unsuitable decision","the recommendation issued to the client","accepting a lender-policy change into the live library","waiving a blocking gap","escalating a breach_suspect finding","any statement to the client that a document has been certified, verified or approved"]},"job":"every interview becomes one file the next person can pick up","skill":"advice-file","stub":"local.mock_file","production":null,"product":null,"insert":null,"with_us":{"kind":"scoped_build","note":"No hosted product. The live write into the network's own CRM tenant, against a dated panel library it controls, is the step not to hand-roll, and it is quoted as a scoped build."}},"with_us_next":"No hosted product. The live write into the network's own CRM tenant, against a dated panel library it controls, is the step not to hand-roll, and it is quoted as a scoped build. Send the failing check id for a fixed price; do not build the live step yourself.","default_path":null,"boundary":{"never":["be the source of a field, a task or a card","write a key the contract does not publish","answer lender policy from memory","move a recommendation past draft with a blocking gap or no card","occupy sent_by, waived_by, accepted_by or decided_by"],"stays_with_person":["recording and transcription consent on the call","the responsible-lending assessment and the not-unsuitable decision","the recommendation issued to the client","accepting a lender-policy change into the live library","waiving a blocking gap","escalating a breach_suspect finding","any statement to the client that a document has been certified, verified or approved"]},"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":["resolve_address","evidence","read_rule","cite","record_consent","read_file","write_file","raise_task"]},{"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":"surface","label":"Something a person opens","need":"A view of the book that whoever does this job can read and act on without asking a developer.","when_missing":"Start at tier 0 and stop there until the business asks for more. The cheapest surface is not a screen, it is a scheduled email carrying the list, and for one person doing this job that is not a compromise, it is the right answer. On Microsoft 365 or Workspace you can skip straight past this: a list and a sheet are already what people open. Build upward only when two people need the same list at the same time.","tiers":[{"tier":0,"label":"A scheduled digest carrying the list","cost":"About an hour. Needs nothing you do not already have.","satisfiedBy":"send","whenYouNeedIt":"One person does this job. They want to know what is waiting when they sit down, and they act on it from their inbox."},{"tier":1,"label":"A read-only page showing the book","cost":"An afternoon. One query and one table.","whenYouNeedIt":"Somebody needs to look mid-day without waiting for tomorrow's digest, or to answer a customer on the phone."},{"tier":2,"label":"Editable rows, a queue and a log","cost":"A day or two with a coding agent, which is what agents are best at. It is bounded work: the fields are the schema, the queue is whatever escalate hands off, the log is the trace.","whenYouNeedIt":"More than one person shares the book and they need to correct it without treading on each other, and you need to see afterwards who changed what."}],"ports":["escalate","answer"]}],"registry":"https://aipathway.com.au/substrates.json"},"checks":[{"id":"disclosure-before-first-field","n":1,"title":"Disclosure and consent are recorded before the first field","statement":"On every recorded channel, the recording and transcription notice is given and recorded before the first question. Refused consent means notes typed by a person, not a file the assistant pretends it heard.","assertable":"stub","ports":["record_consent","evidence"],"stimulus":"One interview, opened and worked through to a written file.","assertion":"The consent record on the interview precedes the first verbatim capture and the first field write in the trace. A file written from an interview with no consent record fails."},{"id":"verbatim-before-fields","n":2,"title":"Verbatim is stored before any field is filled","statement":"The client's own words are kept with a span before classification is applied. Classification is applied to a copy, never to the only copy.","assertable":"stub","ports":["evidence","write_file"],"stimulus":"An interview in which the client states an address, employment and a savings figure.","assertion":"Every field written to the file traces to a verbatim capture with a span, recorded earlier in the trace than the write."},{"id":"keys-from-the-contract","n":3,"title":"Only published field keys are writable, and completeness is computed","statement":"\"Client seems comfortable with repayments\" is not a field. Serviceability inputs are. Completeness is a function of the required keys for that purpose, never a flag somebody sets.","assertable":"stub","ports":["write_file","read_file"],"stimulus":"A build that tries to write a free-text summary as a field, then reads completeness back.","assertion":"The unpublished key is refused and the refusal is recorded. Completeness read back equals the required keys for the purpose minus the keys present. A file marked complete with a required key missing fails."},{"id":"lookups-resolved-not-transcribed","n":4,"title":"Anything with a lookup is resolved, read back, and written as resolved","statement":"Address, employer, existing lender, product: the spoken value is the query, the resolved value is what is written. Low confidence becomes a MissingField with reason asked_unclear, not a guessed value.","assertable":"both","ports":["resolve_address","write_file"],"stimulus":"An address said once and slightly wrong, and a savings figure said unclearly.","assertion":"The address on the file is the resolved value, not the spoken one. The savings key is absent from fields and present in MissingField with reason asked_unclear. A guessed dollar figure fails.","stubLimit":"The stub resolves against a table the runner seeded. Only a live geocoder and a live lender register prove the resolution is right for the addresses and lenders real clients name."},{"id":"one-file-per-client","n":5,"title":"One live file per client, updated and never duplicated","statement":"The CRM record is the file. A second interview on the same client updates it. A null external_id is a failed interview, not a completed one.","assertable":"stub","ports":["read_file","write_file"],"stimulus":"Two interviews on the same client, the same day, the second supplying a value the first left missing.","assertion":"Exactly one file exists for the client after both, with the second interview's id appended and the value present. A second file, or a write with a null external id treated as success, fails."},{"id":"gaps-become-owned-tasks","n":6,"title":"Every blocking gap is a task with an owner and a due date, raised once","statement":"Each missing required key becomes a MissingField and, where it blocks the next stage, one task with a named owner and a due date. A reminder in chat does not count, and a task is not raised twice for the same gap.","assertable":"stub","ports":["read_file","raise_task"],"stimulus":"A file with one blocking gap, then a second interview that closes it.","assertion":"One task exists for the gap, with an owner and a due date, and the second interview does not raise another. A blocking gap with no task, or two tasks for one key, fails."},{"id":"panel-answers-carry-a-card","n":7,"title":"Lender policy is a cited answer, never a recalled one","statement":"A panel question is answered from clauses with an edition and an effective date, with a source card, or it is declined to a person. The model's memory of what a lender does is not a source.","assertable":"stub","ports":["read_rule","answer","cite"],"stimulus":"A policy question the library covers, and one it does not.","assertion":"The covered question is answered with a card naming lender, clause, edition and effective date. The uncovered one is declined with a route to a person. An answer with an empty card list fails."},{"id":"superseded-policy-cannot-answer","n":8,"title":"A retired clause cannot answer, and a new one answers only once a person accepts it","statement":"When the lender moves the policy, answers drawn from the old clause change or decline, and the new clause answers only after a named person accepts it.","assertable":"stub","ports":["read_rule","answer","gate"],"stimulus":"A question answered, the clause retired and its replacement loaded unaccepted, then the same question again.","assertion":"The second answer declines or changes. The unaccepted replacement does not reach an answer. Once accepted, the acceptance carries a name and a date."},{"n":9,"assertable":"both","stubLimit":"The stub proves nothing leaves without a release event carrying a named person. Only the live path proves what left is what was released, rather than something regenerated after the approval.","id":"recommendation-is-gated","title":"A recommendation cannot leave draft with a blocking gap or no card, and is sent by a person","statement":"Ready_for_person is a gate, not a vibe. A draft with an open blocking MissingField or an empty source_card_ids stays in draft or blocked. Sent_by is a person, and a machine cannot occupy it.","ports":["gate","read_file"],"stimulus":"A draft with cards but an open blocking gap, then the gap closed, then an attempt to send without a named person.","assertion":"The first draft is blocked. After the gap closes it may be ready_for_person. The send without a person is refused and recorded. No draft reaches sent_by_person without cards and a named person."},{"id":"coaching-cites-the-file","n":10,"title":"Coaching cites a span or a clause, and breach_suspect goes to a named person","statement":"A CoachingEvent without an evidence span or a source card is a note, and notes do not roll into a score. A finding marked breach_suspect is escalated to a named compliance person, not softened into tone.","assertable":"stub","ports":["cite","escalate"],"stimulus":"An interview in which the broker skips the living-expense questions.","assertion":"A coaching event exists with a finding key from the closed list and an evidence span. Any breach_suspect event has a matching escalation to a named person. A coaching event with neither span nor card fails."},{"id":"log-retrievable-by-interview","n":11,"title":"Recording, transcript, extracts, writes, cards, tasks and coaching are retrievable together","statement":"Everything the interview produced can be listed by interview id and by file id. If the recording dies and the fields remain, the file is hearsay.","assertable":"stub","ports":["read_file","evidence"],"stimulus":"One interview worked through to a file, a task and a coaching event.","assertion":"Every write, capture, task and coaching event in the trace carries the interview id, and a read by that id returns all of them. An artefact with no interview id fails."},{"id":"detached-library-declines","n":12,"title":"With the library detached, the assistant declines","statement":"Ask the policy question with the panel library removed. The only conforming output is a decline. A remembered answer fails, even when the words are right.","assertable":"stub","ports":["read_rule","answer"],"stimulus":"The panel library detached, then the covered question from check 7.","assertion":"The answer is a decline with a route. An answered outcome with any text, cards or not, fails. This is the line to run first against any vendor who says the model was trained on the network's data."},{"id":"survives-the-builder","n":13,"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 file, the panel library and the tasks 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":14,"title":"The pass test passes","statement":"Two interviews on a real CRM tenant, one panel move in a dated library the business controls, and the detached-library question, all as the pass test after section 8 describes.","assertable":"production","ports":["write_file","read_rule","answer"],"stimulus":"Interview A and Interview B on the live CRM, then the clause retired and its replacement accepted, then the library detached.","assertion":"One file with an external id, updated not duplicated; the savings gap parked then closed; the draft blocked then ready and never machine-sent; the old card gone from every live answer; a decline with the library gone.","stubLimit":"Deliberately unpassable on a stub. A stub CRM accepts every write and a stub panel moves when you tell it to. Only a real tenant proves the external id is real, and only a library the lender actually moved proves the answers already given can be found."}]}