Skip to main content
Version: Target · roadmap

The submit

The submit ends the playbook: it evaluates its submit guard, runs the reserved writes in order — the skills' calls and the playbook's —, and closes the journey.

    submit_guard:
form: {accept_terms: {type: boolean, required: true, label: "I accept the terms and conditions"}}
confirm: true
guards: [{policy: commande-inhabituelle}]
submitted_by:
- {who: agent, when: "total <= 50 and agent.mode == 'autonomous'"}
- {who: {role: support-manager}, when: "total > 50"}
- {who: owner}

submitted_by — who can submit​

Rules in order; the first one that matches applies.

whomeaning
ownerthe verified owner of the room: a person whose identity was proven by a provider (Identity). A number that was only typed in is not enough
principalthe person who sent the agent
user.approverthe authority above the user: the approver set by the client organisation (product), their manager (process)
{role: x}a user who has this role
agentthe agent itself

All are users. An admin never approves or submits a business action. See Human In The Loop.

  • Without submitted_by, a human in the room submits, even anonymous: the customer's click. This is today's behaviour. Requiring owner closes this path to anyone who only holds the room code.
  • No autonomy without a condition: a who: agent rule without when is refused at load time. An agent rule that covers a high and irreversible write gives a warning.
  • Evaluated by Core, at submit time. Neither hiding nor showing a tool grants or removes this right.
  • The receipt says who submitted, and under which rule.

When several layers apply (the playbook, a sub-identity, a profile), the strictest wins: if one requires a human, it is a human.

An order is declared on the playbook​

A call lives where what it files lives:

  • A call that files one action of a skill — a refund, a delivery, a pickup — lives on the skill or on its kind (Skills).
  • A call that sends the lines of several tasks in one request — an order: the dishes, the drink, the cutlery, in a single placeOrder — is declared once, on the playbook, in submit, next to its submit_guard.
  • A skill used outside any playbook keeps all its calls.
playbooks:
- name: take-meal-order
tasks:
plats: {skill: takeMealSelection}
boisson: {skill: takeDrinkSelection, after: [plats]}
submit:
- name: order
tool: placeOrder
args:
body.items: "$selection[task=plats,boisson]"
body.customer.phone: "$submit.phone"
body.idempotency_key: "$action.key"
line: {product_id: "$line.ref", quantity: "$line.quantity", variant_id: "$line.fields.variante"}
extra_lines:
- {ref: couverts-avec, quantity: "$submit.couverts", when: "$submit.couverts > 0"}
- {ref: couverts-sans, when: "$submit.couverts == 0"}
output: {order_id: {from: "$output.id", type: id}}
submit_guard:
form:
phone: {type: phone, required: true, label: "Téléphone"}
couverts: {type: quantity, required: true, label: "Combien de couverts ?"}
  • Lines are taken by task: $selection[task=plats,boisson] sends the lines of those two tasks; $selection[kind=dish] those of a kind, among the playbook's tasks. $selection alone is refused in a playbook's call: what it sent would depend on the lines present in the room.
  • A line leaves through one call only. In a playbook where a call takes a task's lines, the calls of that task's skill do not run for those lines: the playbook's call sends them. Outside that playbook, the skill keeps its calls.
  • A line no call sends is refused: at load, a skill whose lines leave through no call — neither its own, nor that of a playbook that uses it; at submit, a line chosen outside a playbook by a skill with no call of its own is held, with the sentence that says to open the journey. It never leaves for nowhere.
  • A playbook's call does not read $item: it sends several lines. What comes from a line goes through line:; what comes from the person, through the submit's form ($submit.<field>).
  • It is cited by its name alone: $submitted[order].order_id. A skill's call is cited by its task and its name ($submitted[geste.pay]). The names of a playbook's calls are unique in that playbook. A task whose params read $submitted[order] waits for the tasks whose lines the call takes.
  • Two playbooks that place the same order each declare it: each submit has its call, never two copies of one call in one submit. A YAML anchor (&order, *order) avoids copying it.

line: — the shape of a line​

line: says what one line looks like to the tenant. It is declared on the call that sends the lines: the playbook's, or a skill's.

formsends
line: {product_id: "$line.ref", quantity: "$line.quantity"}one object per line, with the tenant's names
line: "$line.ref"the bare reference, for a tenant that expects a list of identifiers
nothing{slug: "$line.ref", qty: "$line.quantity"}, Convoy's shape

The sources: $line.ref (the item's reference), $line.quantity, $line.price (the line's billed price), $line.fields.<field> (a field of the line). All are checked at load:

  • $line.fields.<field> must be a declared field of the line's kind: a field of present.fields or a line field. A name no kind of the call's lines declares is refused.
  • A key whose source does not exist on a line is left out, never sent empty: an optional field left unanswered, a field this line's kind does not have.
  • The "$line.ref" form is refused on a task that has quantities: the quantity would be lost.
  • Check line: against the tenant's schema. A key it does not know, it ignores and applies its own default, with no error: without line:, a tenant that expects quantity receives qty and ships a single piece.

extra_lines — lines sent without being chosen​

Some lines leave with the order without anyone choosing them: the cutlery, when the restaurant keeps it as two products at 0 F.

        extra_lines:
- {ref: couverts-avec, quantity: "$submit.couverts", when: "$submit.couverts > 0"}
- {ref: couverts-sans, when: "$submit.couverts == 0"}
  • ref is a reference in the tenant's catalogue. quantity is of type quantity: a number, or $submit.<field>; 1 without it. A quantity that is not a positive integer holds the call, with the sentence that says so: never a line at 0.
  • when is written in the condition language. It is the only place where a condition reads $submit.<field>: a field of the form of the same playbook's submit_guard, or of the station's for a skill's call outside a playbook (checked at load). A field of that form read as a quantity refuses a negative number: no when then lets an order leave without the line planned.
  • The added lines join the call's lines: it takes them from one $selection[…] source, or it is refused. They take the shape of its line:; an added line has only ref and quantity, and the keys that read $line.price or $line.fields are left out of it.
  • A ref must exist at the tenant. The provisioner checks it at every provisioning. A missing ref is a provisioning error: the station does not go live, rather than send a ghost line. Through which read of the tenant it checks is not fixed yet: an added line was shown by no skill.

Execution​

  • In order: a call that reads $submitted[…] runs after the one it reads; otherwise, the tasks' calls in the order of the tasks, then the playbook's in the order it writes them. Irreversible ones last.
  • A write that reads the result of a failed write is put on hold, never run on an empty value.
  • Idempotency per action ($action.key): a retry, a double click, an automation triggered twice produce only one effect.
  • Partial failure: what was executed stays executed. Cancelling goes through the operation declared as reversible, as an explicit action.

A sub-playbook has its own submit​

Its submit runs only its actions. The parent resumes with its result. See Playbooks.