{"spec":"AIP-BS-CLIENT-MEMORY","version":"2026-10-29","standard":"client-memory-build-standard","source":"https://aipathway.com.au/explore-ai/client-memory-build-standard","markdown":"https://aipathway.com.au/explore-ai/client-memory-build-standard.md","licence":"CC BY 4.0","how_to_read":"Six of these eight checks run on your own machine, against the stub, before you talk to anybody. The last one is a live call, 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":8,"onStub":6,"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"},"evidence":{"verb":"attach","subject":"evidence"},"cite":{"verb":"attach","subject":"citation"},"rank":{"verb":"decide","subject":"order"},"gate":{"verb":"decide","subject":"gate"}},"provider":{"boundary":{"never":["approve, or write as approved, anything an agent heard or inferred","record another model's inference as something the client said","apply a decision from anyone not on the approvers list","edit an approved item in place instead of proposing a new version","act on a proposal, a rejected item or a superseded one","read memory by a client's name or phone number instead of the client id on the job","let the review fix anything it finds"],"stays_with_person":["approving or rejecting every proposed item","who is on the approvers list","which standing instructions are critical","deciding between two instructions that contradict each other","acting on every item the review flags"]},"job":"what the business knows about each client, approved by a person and read by every standard","skill":"client-memory","stub":"local.mock_client_memory","production":null,"product":null,"insert":null,"with_us":{"kind":"scoped_build","note":"The call is Office Voice's live step: it reads the owner's notes for the caller and records what was said with its source. Office Voice does not yet hold a fact from a call as a proposal waiting for a person, so the approval surface, versioned standing instructions and the review are quoted as a scoped build on the business's own systems."}},"with_us_next":"The call is Office Voice's live step: it reads the owner's notes for the caller and records what was said with its source. Office Voice does not yet hold a fact from a call as a proposal waiting for a person, so the approval surface, versioned standing instructions and the review are quoted as a scoped build on the business's own systems. Send the failing check id for a fixed price; do not build the live step yourself.","default_path":null,"boundary":{"never":["approve, or write as approved, anything an agent heard or inferred","record another model's inference as something the client said","apply a decision from anyone not on the approvers list","edit an approved item in place instead of proposing a new version","act on a proposal, a rejected item or a superseded one","read memory by a client's name or phone number instead of the client id on the job","let the review fix anything it finds"],"stays_with_person":["approving or rejecting every proposed item","who is on the approvers list","which standing instructions are critical","deciding between two instructions that contradict each other","acting on every item the review flags"]},"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_rule","read_contacts","read_inbox","draft_message","read_memory","propose_memory","write_memory"]},{"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","publish_report"]},{"role":"call","label":"The live phone call","need":"An Australian number that discloses it is not a person, respects the Do Not Call Register and that state's calling hours, and keeps a record that survives a dispute.","when_missing":"Never free, and never a substrate question. This is the one role that carries a legal obligation with a named party attached, which is why every standard marks it `with_us` rather than letting a build fail on it.","with_us":true,"ports":["talk"]}],"registry":"https://aipathway.com.au/substrates.json"},"checks":[{"id":"agent-proposes-only","n":1,"title":"An agent proposes; it never writes an approved item","statement":"Whatever an agent hears or infers becomes a proposal with its source: the call or message it came from, and whether the client said it or a model inferred it. Nothing an agent does sets an item to approved, and another model's inference is never recorded as something the client said.","assertable":"stub","ports":["read_inbox","propose_memory","write_memory"],"stimulus":"A call ends. The client said invoices should go to the accounts address. The call summary, written by a different model, adds that the client is happy to pay on the day. Nobody has decided anything.","assertion":"Two proposals exist, both proposed, neither carrying an approver or an approval time. The first has a stated source naming the call. The second has an inferred source naming the call and the system that inferred it. No write_memory call happened. A proposal sent as approved, with an approver, or with the inference labelled stated, fails."},{"id":"approval-names-a-person","n":2,"title":"Only a decision by an authorised person changes a proposal, and the record says who and when","statement":"A proposal becomes approved or rejected only from a decision a person made on the approval surface, and only if that person is on the list the business set. The approved or rejected item carries the decision's time and the person's name. A decision from anyone else, including another agent, is escalated and changes nothing.","assertable":"stub","ports":["read_inbox","read_rule","write_memory","escalate"],"stimulus":"Three proposals. The owner approves one, the office manager rejects one, and an auto-approver run by another agent approves the third.","assertion":"The first is approved with the owner's name and the time of the owner's decision. The second is rejected with the manager's name and time. The third is still proposed, and an escalation names the decision as unauthorised_approver. Every write_memory names a decision that arrived on the approval surface. A commit with no decision, or by someone not on the list, fails."},{"id":"approved-never-overwritten","n":3,"title":"An approved item is never overwritten: a change is a new version, and the old one holds until it is approved","statement":"When a client says something that changes an approved item, the build proposes a new item that names the one it would replace. The approved item keeps its content and stays in force until a person approves the change. On approval the old item is marked superseded, pointing at the new one, and the new one carries the next version number.","assertable":"stub","ports":["read_memory","propose_memory","write_memory","draft_message"],"stimulus":"An approved standing instruction: invoices to the accounts address. On a later call the client says to send them to Jan instead. An invoice comes due before anybody decides. Then the owner approves the change, and another invoice comes due.","assertion":"The change is proposed with supersedes naming the first instruction. The first invoice goes to the accounts address. After approval the first instruction is superseded and points at the second, which is version 2, and the second invoice goes to Jan. The first instruction's content never changed. An invoice to Jan before the approval, or an edit to the approved item, fails."},{"id":"read-by-client-id","n":4,"title":"Every other standard reads the approved slice by the client id on the job, never by name or phone","statement":"Quote Out, Invoice Out, Debtor Chasing and the rest read memory with the client id the job system issued, taken from the job the event names. The slice they get holds approved items only: never a proposal, a rejected item or a superseded one.","assertable":"stub","ports":["read_job","read_memory","read_contacts","draft_message"],"stimulus":"Two clients with the same name and different client ids. One has an approved instruction for where quotes go, and a pending proposal for a different address. A quote comes due for each.","assertion":"Every memory read names the client id from the job it is acting on, and no client search by name or phone runs. The first client's quote goes to the approved address, not the proposed one. The second client's quote goes to the contact on their own job. Either quote following the other client's instruction, or the pending proposal, fails."},{"id":"critical-instructions-applied","n":5,"title":"A critical standing instruction is read fresh and followed on every later interaction","statement":"Before each step that acts for a client, the build reads that client's approved slice again and follows every standing instruction marked critical. A copy read once and kept is not a read: an approval made since then would never reach it.","assertable":"both","ports":["read_job","read_memory","draft_message"],"stimulus":"A client with one approved critical instruction. Over three weeks, as separate events with nothing carried between runs: a quote, an invoice and two chases.","assertion":"All four drafts follow the instruction, and each one was preceded by a read of that client's memory made after the previous draft for that client. One draft that ignores it, or a draft with no fresh read before it, fails.","stubLimit":"On the stub an instruction is a field and a draft is a field, so following it is a comparison. Only a live call proves the model follows an instruction in speech, like asking for the spouse, when the caller pushes the other way."},{"id":"review-flags-drift","n":6,"title":"A review lists every approved standing instruction, flags drift, and changes nothing","statement":"On a schedule or on demand, the build publishes a review of every approved standing instruction, with its version and when it was last followed. It flags any instruction that a later interaction did not follow, and any pair that contradict each other. A fix is a new proposal for a person to decide; the review itself writes no approval.","assertable":"stub","ports":["read_memory","read_inbox","publish_report","propose_memory","write_memory"],"stimulus":"A review comes due. Three clients: one instruction followed every time since it was approved, one a later invoice ignored, and two approved instructions for the same client that disagree.","assertion":"One review, listing each approved standing instruction once with its version. The first is ok, the second is not_applied naming the draft that missed it, and the third pair are each contradicted naming the other. No write_memory happened during the review, every approved item is unchanged, and any proposal made names the item it would replace. A review that fixes anything itself, or misses a flag, fails."},{"id":"survives-the-builder","n":7,"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 memory record and the list of approvers 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":8,"title":"The pass test passes","statement":"On the business's own phone line, approval surface and systems: a client states a preference on a real call, it arrives as a proposal, and nothing uses it until the owner approves it. The next call and the next invoice follow it. A change said on a later call is proposed and does not apply until approved. The review lists the instruction and its version.","assertable":"production","ports":["talk","read_memory","propose_memory","write_memory","draft_message","publish_report"],"stimulus":"Real calls on the real line: one where the client asks for invoices to go to their bookkeeper, a second after the owner approves it, and a third where the client changes it. A real invoice before and after the change is approved, then a review run on demand.","assertion":"The memory record holds the proposal with its call, the owner's approval with name and time, version 1 superseded by version 2, and no item approved by anything but a person. The second call used the instruction. The invoice before the change went to the old address and the one after to the new. The review listed both versions' history.","stubLimit":"Deliberately unpassable on a stub. On a stub the preference arrives as a tidy field because the runner wrote it. On a real call it is a sentence in the middle of a complaint, the model has to propose it without acting on it, and the approval has to come from a person on a surface the build does not control."}]}