evoke Get started

Examples · 06 of 06

One person asks. Another approves.

A payment is decided, then waits as plain data until a second person confirms it. A payee is one of your words, never invented, and paying is destructive, so it can never run on its own. A pattern for your app: who may approve is your check.

The request and the result

01

The maker types. The checker says yes.

A decision travels from one person to another through a queue. Interpretation and execution are two moments, in two processes. Every line is what Node printed.

~/desk
$ node checker.ts pay payee="acme" account="ops" amount="12400" ref="invoice 8812" pay payee="acme" account="ops" amount="12400" ref="invoice 8812" · destructive · weakest: route 1.00 paid 12400 from acc_ops, transfer tr_0f3a
  1. 1The maker's sentence becomes a call. The payee and the account are words of the desk. The amount is a number read from the sentence, within its range.
  2. 2The decision waits as plain data. It carries the call, the effect, the weakest judgment and why it stopped. Any queue can hold it.
  3. 3Only confirmed: true runs it. The checker reads the call and the reason, then runs the same decision in another process.

The operations

02

One reflex, two processes.

The payment reflex as a file and as code, the desk's payees, and the checker that runs a queued decision. Each desk compiles its own words with with, in milliseconds.

pay/reflex.tomlthe author's
reflex = 1

description = """
Pay an approved payee from one of our accounts.
One transfer, released after a second person confirms it."""
not_for = [
  "adding or changing a payee",
  "moving money between our own accounts",
  "asking a balance",
]
tags    = ["treasury"]
effect  = "destructive"
confirm = "Pay {payee} {amount} from {account}?"
run     = "pay.mts"

[config]
api   = "The payments API address"
token = { about = "The payments API token", secret = true }

[args.payee]
ask   = "Which payee?"
vocab = "payees"

[args.account]
ask   = "From which account?"
vocab = "accounts"

[args.amount]
ask   = "How much?"
pick  = "number"
range = [1, 250000]

[args.ref]
ask      = "What reference?"
pick     = "quoted"
optional = true

[examples]
'pay Acme 12400 from ops, ref "8812"'              = { amount = "12400", ref = "8812" }
"send 950 to the cleaners from the office account" = { amount = "950" }
"wire 3200 to Acme from treasury"                  = { amount = "3200" }

[tests]
'settle 18000 with Acme, ref "Q3"' = { amount = "18000", ref = "Q3" }
"pay Acme from ops"                = { amount = false }
"add Acme as a new payee"          = false
vocab/payees.tomlyours
acme     = { what = "Acme, the packaging supplier.", value = "py_acme" }
cleaners = { what = "Blitz, the office cleaners.", value = "py_blitz" }
  1. 1A payee is a word. Its value is an id in the payments system. The classifier never sees the id, and no sentence can name a payee the desk did not list.
  2. 2The token is a secret. It is named in the settings and read from the environment for one run. It is never stored.
  3. 3The decision survives a round trip through JSON. run() accepts it back, and refuses one made under another set of reflexes.

One boundary

03

Missing the amount, it asks. Destructive, it asks again.

At a terminal the same reflex asks for what the sentence lacked, then confirms. A no is exit 2, and nothing was paid.

evoke
$ evoke "pay Acme from ops"
  How much?  > 12400
  pay payee="acme" account="ops" amount="12400" · destructive · weakest: route 1.00
  Pay acme 12400 from ops?  [y]es [n]o [t]each > n
[2]

Sure at 1.00, and it still asked. There is no flag that skips a destructive reflex's question, at a terminal or in an app. What the classifier cannot do →

Before you run it

04

What this pattern needs.

The files above are the whole example. Copy them from this page.