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”rule Approve salience 10 { when true then out.Set("approved", true); out.Set("customer.address.city", "Chennai");}{}{ "approved": true, "customer": { "address": { "city": "Chennai" } } }A key is a path. Dots nest, and intermediate levels are created on write, so the second line produces a nested object rather than a key with dots in it.
| Situation | Behaviour |
|---|---|
| A literal dot inside a key | Escape it with a backslash: "release\\.notes" writes the key release.notes |
| A trailing dot | Literal, no escaping needed |
| 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. A float default needs a decimal point: 0.0 |
out.Bool(path, default) | Boolean |
out.Date(path) | A date |
out.Map(path) | A nested object |
out.Value(path) | The typed value; out.Value(path).Rows() reads back a list of records |
out.GetStr, GetInt, GetFloat, GetBool and GetMap still work, with the zero value as the
default, but are deprecated and load with a warning: write out.Str, out.Int, out.Double,
out.Bool and out.Map. out.Get(path) returns the raw value; comparing it for a key nobody wrote
aborts the execution, so compare through a typed reader instead.
rule Standard "every claim starts at standard" salience 100 { when in.Double("amount", 0.0) > 0.0 then out.Set("tier", "standard"); out.Set("amount", in.Double("amount", 0.0));}
rule Escalate "high value goes to a manager" salience 90 { when out.Str("tier", "") == "standard" && out.Double("amount", 0.0) > 10000.0 then out.Set("tier", "manager");}{ "amount": 12500.0 }{ "tier": "manager", "amount": 12500 }A rule’s when that reads out sees what earlier rules wrote, so Escalate fires on the cycle
after Standard. See Rule patterns.
What a written value becomes
Section titled “What a written value becomes”These 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: a list of objects |
| An empty list of records | An empty list. A real result, kept |
Nothing: a missing in.Rows, in.Map, or a document read that missed | 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.
rule Shapes salience 10 { when true then out.Set("borrower", map.Of("name", in.Str("name", ""), "state", in.Str("state", ""))); out.Set("lines", in.Rows("lines")); out.Set("credits", in.Rows("credits")); out.Set("fees", in.Rows("fees"));}{ "name": "Jane Doe", "state": "MA", "lines": [ { "sku": "A-1" } ], "credits": [] }{ "borrower": { "name": "Jane Doe", "state": "MA" }, "lines": [ { "sku": "A-1" } ], "credits": [] }Attributes
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 |
Attributes ride beside the value; they are not part of the output object. This is what lets a rule gate on extraction quality rather than on the extracted text:
rule ExtractAmount salience 90 { when true then out.Set("CDLoanAmount", "412000.00"); out.Attr("CDLoanAmount", "confidence", 0.62);}
rule LowConfidenceAmount "flag a loan amount we are unsure about" salience 80 { when out.AttrNum("CDLoanAmount", "confidence", 1.0) < 0.7 then findings.Warn("LowConfidenceAmount", "extraction", "loan amount extracted with low confidence"); out.Set("needs_review", true);}{}{ "CDLoanAmount": "412000.00", "needs_review": true }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 kis rules --history prints and what rules.svc returns under history when a
call sets options.history. 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 engine cannot call from a rule. It loads,
then aborts the execution when reached with multiple value returns are not supported. 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