Writing output
out is the result. What it holds when the run ends is exactly what the caller receives, so
everything a rule wants to say ends up here.
Writing
Section titled “Writing”out.Set("approved", true);out.Set("customer.address.city", "Chennai");A key is a path. Dots nest, and intermediate levels are created on write, so the second line
produces {"customer": {"address": {"city": "Chennai"}}} rather than a key with dots in it.
| Situation | Behaviour |
|---|---|
| A literal dot in a key | Escape it: "Work Phone Ext\\." |
| A trailing dot | Literal, no escaping needed |
| Appending to a string | out.Append(key, text) |
| Removing a key | out.Delete(path) |
Reading back
Section titled “Reading back”A later rule can read what an earlier one wrote, with the same typed-reader shape as
in — a default, and no failure mode.
| Call | Returns |
|---|---|
out.Has(path) | Whether anything was written there |
out.IsEmpty(path) | Written but empty |
out.Str(path, default) | String |
out.Int(path, default) / out.Double(path, default) | Numbers |
out.Bool(path, default) | Boolean |
out.Date(path) | A date |
out.Map(path) | A nested object |
out.Value(path) | The typed value |
out.GetStr / GetInt / GetFloat / GetBool / GetMap | The same, with the zero value as the default |
rule Escalate "high value goes to a manager" salience 90 { when out.GetStr("tier") == "standard" && out.Double("amount", 0.0) > 10000.0 then out.Set("tier", "manager");}Reading a collector inside a when is memoised, and out.Set invalidates the memo itself — so a
rule that reacts to another rule’s write does not need Changed.
What a written value becomes
Section titled “What a written value becomes”Three rules decide the shape of what lands in the output, and the difference is visible to whoever consumes the result.
| You write | Output holds |
|---|---|
| A map | An object. Not a one-row table |
| A list of records | A table |
| An empty list | A table with no rows — a real result, kept |
| A nil list or map | Nothing. The key is absent, not null |
The first row matters more than it looks. A map is the dominant shape in real rule sets, and coercing one to a single-row table would turn every object in the output into an array — a silent change to the shape every consumer already parses.
The last row is the useful one to write against: a miss produces an absent key rather than a JSON
null, so a caller can tell “this rule did not find it” from “this rule found nothing”, which null
cannot express.
out.Set("borrower", in.GetMemAsMap()); // objectout.Set("lines", bbox.Doc().Table().Records()); // tableAttributes
Section titled “Attributes”A value can carry metadata beside it — most usefully how confident the producer was.
| Call | Purpose |
|---|---|
out.Attr(path, name, value) | Attach one attribute to a value already written |
out.AttrNum(path, name, default) | Read a numeric attribute |
out.AttrStr(path, name, default) | Read a string attribute |
out.HasAttr(path, name) | Whether the attribute exists |
out.Attributes(path) | Every attribute on that value |
This is what lets a rule gate on extraction quality rather than on the extracted text:
rule LowConfidenceAmount "flag a loan amount we are unsure about" salience 80 { when out.AttrNum("CDLoanAmount", "confidence", 1.0) < 0.7 then findings.Warn("check_amount", "extraction", "loan amount extracted with low confidence");}The default of 1.0 is deliberate: a value with no confidence attribute is treated as certain, so
the rule only fires on a value that actually reported doubt.
Provenance
Section titled “Provenance”Every write records the rule that made it. Nothing is required of you.
| Call | Gives |
|---|---|
out.History() | Every change, in order, each attributed to its rule |
out.HistoryFor(key) | The changes to one key |
out.All() | The whole result map |
out.AllAttributes() | Every attribute on every value |
That history is what --history prints on the CLI and what the audit trail is built from — it is
how you answer “which rule set this” without adding logging.
Not callable from a rule
Section titled “Not callable from a rule”out.JSON() returns a value and an error, which the grammar rejects. Read the output from the
run result instead.
See also
Section titled “See also”- Reading facts — the
inside - Writing rules — rule structure and salience
- Findings — the severity-graded validation channel