Skip to main content
Version: Current

Policies

Today, what restricts an action is written as guards: a skill's select guards, and the station's submit guard. Core applies them whatever the model says. Other keys restrict too: depends_on and on_loss (a skill's item can be chosen only once the part it depends on holds something), and the tasks of the open playbook: their order (after), which refuses a choice made too early, their choices (one_of) and conditions (when), which refuse a method that is not the one, and the required tasks, which block the submit. There are no reusable policies, permissions, budgets or smart policies yet (Policies).

Select guards: before an item enters the selection​

    select:
max_items: 1 # one only: choosing another replaces the first
guards: # an ordered list; each guard is ONE of these four
- form: # what the PERSON fills in, in the card
address: {type: text, required: true, label: "Delivery address"}
zone: {type: text, required: true, enum: [dakar, thies, rufisque]}
- name: zone-desservie # a condition that must hold
require: "$item.fields.zone in ['dakar', 'thies']"
reason: "We do not deliver to Rufisque yet."
- check: checkDeliverable # the tenant says whether the item is eligible
- update: {price: getDeliveryPrice} # the tenant computes the price for THIS request
- {check: checkThiesRoute, when: "$item.fields.zone == 'thies'"}
guarddoesphase
formvalues the person gives; they become the item's fields3
requirea condition that must hold (see Conditions)3
checkthe tenant's operation says whether the item is eligible5
updatethe tenant's operation sets a value of the item; only price exists6

Core runs them by phase, and within a phase in the order written: first what the person answers and the station's own conditions, then what calls the tenant. So the tenant is never called for an item already refused. A refused item does not enter. A check's reason is the tenant's; the values the person typed are replaced in it by their field's name.

  • At most one form and at most one update in the list; a guard that names two of the four, or none, refuses the station at load.
  • max_items says how many items the skill's part may hold. It is on the skill only: kinds.<k>.select holds guards alone ({} means "no guard").
  • Every guard may carry when:: it applies only when the condition holds. A form whose when is false for an item is not asked for that item.
  • A require also carries name (what its refusal is called, unique in the list) and reason (the sentence the person and the agent read). Without reason: this choice does not meet the condition '<name>', or this choice does not meet a condition of this offer without name. name and reason are written on a require only.
  • A require alone is no step on the card: Core adds the item, or says why not. It does not make the line a one-of choice and does not remove the −/+ buttons; it runs on every add.

Each form field has a type, required, taken from the table of types; a type outside the table refuses the station at load, naming the field. Its other keys: required, label, placeholder, enum (a closed list of answers) and identifies. Core checks each answer by its type: a price expects a number, a quantity a whole number.

The form values become the item's fields: $item.fields.address. A field marked identifies: true also identifies the room, which no longer asks for it (Auth).

Do not call a form field name

A name field overwrites the item's displayed name: the delivery line would show the customer's name instead of "Tomorrow 8am-12pm". Write customer_name.

Conditions: require and when​

A condition is a text Core reads itself, against a closed grammar; it is never run as code.

writemeaning
==, !=, <, <=, >, >=compare
in, not inin a list ([...], exact), or in a text (ignoring case)
and, or, not, ( )combine
5, 'text' or "text", true, false, null, ['a', 'b']values
$item.fields.<name>a field of the item: declared in present.fields, or asked by this select's form
$item.price, $item.price_cadence, $item.ref, $item.kind, $item.idwhat the item itself carries
  • Refused at load: a syntax error; any other reference ($params…, identity…, $fields…, an undeclared field); a bare value (5, 'yes'), which is not a condition; more than 500 characters, 16 levels, 50 list items or 18 digits.
  • A condition reads a form field only after the form guard in the list, and a form's when does not read its own fields. Only the answers to the form's fields enter the item: a value under another name is not kept.
  • No $item.price beside an update: the update sets the price last, so a condition would read the price from before the quote. No $item.quantity: a guard sees the quantity of one add, not the line's.
  • Values. true is never a number. A number written as text is a number ("460" > 0). == and != are exact: 149 and "149" differ. An absent value equals null: != and not in are therefore true for it ($item.fields.country != 'US' admits an item with no country; write $item.fields.country != null and … to require one).
  • What cannot be known. An absent or unreadable value is not compared with <, >… Then Core fails closed: a require not known to hold refuses; a when that cannot be known applies its guard.

The submit guard: once, before anything​

submit_guard:
form:
accept_terms: {type: boolean, required: true, label: "I accept the terms and conditions"}
confirm: true # an explicit yes from the person

It applies to the whole selection, before the first operation. If it fails, nothing is executed. Core requires the person's confirmation on every submit, whatever the door, and whether confirm is declared or not.

Who submits​

The model cannot submit: it takes the confirmation that Core hands to the person's screen, never to the model. This is not authentication: a script that reads the room gets this confirmation just as the widget does (see Auth). See The submit.