`out`, writing results
out is the result. What it holds when the execution finishes is the answer: there is no
separate return step, and no rule needs to assemble a final envelope.
It is also readable, and that makes out the way rules communicate. A later rule’s when that
reads out sees what an earlier rule wrote, with nothing else to call:
rule Classify "a statement names its period" salience 20 { when in.Has("statement.period") then out.Set("type", "statement");}
rule ExtractPeriod "statements carry their period through" salience 10 { when out.Str("type", "") == "statement" then out.Set("period", in.Str("statement.period", ""));}{ "statement": { "period": "2026-09" } }{ "type": "statement", "period": "2026-09" }Writing
Section titled “Writing”out.Set(path, v)
Section titled “out.Set(path, v)”Writes a value at path.
| Parameter | Type | Meaning |
|---|---|---|
path | string | Where to write. A dotted path nests: "decision.status" writes status inside decision |
v | any | The value: text, a number, a boolean, a date, a map, rows (a list of maps), or a document read |
How each kind of v lands:
v | Written as |
|---|---|
| Text, number, boolean | Itself |
A date (in.Date, to.Date) | YYYY-MM-DD text |
A map (map.Of, in.Map) | A JSON object |
Rows (in.Rows, Records()) | A JSON array of objects |
| A document read that missed, or a null | Nothing. The key is not written |
| Any other list, such as a list of strings | Its text form, "[a b c]". Put such a list inside a map.Of to keep it a list |
A document read that carries metadata, a confidence, the text that actually matched, keeps those attributes, so you do not have to unpack the value to keep its provenance.
rule Decide "nested output from dotted paths" { when in.Double("application.score", 0.0) >= 700.0 then out.Set("decision.status", "approved"); out.Set("decision.tier", "prime");}{ "application": { "score": 742.0 } }{ "decision": { "status": "approved", "tier": "prime" } }A write under a key that already holds a plain value (out.Set("decision", "x") then
out.Set("decision.status", …)) is refused and nothing changes.
rule CaptureLoanAmount "read the loan amount and keep its confidence" { when bbox.Doc().Right("Loan Amount").Found() then out.Set("loanAmount", bbox.Doc().Right("Loan Amount").Currency());}out.SetValue(path, v)
Section titled “out.SetValue(path, v)”out.Set for a value that is already typed, such as one from
in.Value.
| Parameter | Type | Meaning |
|---|---|---|
path | string | Where to write |
v | Value | An already-typed value |
out.Delete(path)
Section titled “out.Delete(path)”Removes the value at path.
| Parameter | Type | Meaning |
|---|---|---|
path | string | What to remove |
Useful where a later rule supersedes an earlier classification and the earlier key would otherwise remain alongside the correction.
rule ProvisionalType "assume an invoice until proven otherwise" salience 20 { when in.Has("document") then out.Set("type", "invoice"); out.Set("provisional", true);}
rule ConfirmedCreditNote "a credit note replaces the provisional type" salience 10 { when in.Eq("document.kind", "credit") then out.Set("type", "credit_note"); out.Delete("provisional");}{ "document": { "kind": "credit" } }{ "type": "credit_note" }out.Attr(path, name, v)
Section titled “out.Attr(path, name, v)”Attaches one attribute to an already-written path. Nothing happens when path holds nothing.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path that already holds a value |
name | string | The attribute name |
v | any | The attribute value |
This is the escape hatch for metadata the producing value could not supply. It should stay rare, because a value that carries its own provenance is better than one annotated afterwards by a rule that happened to know something. Attributes travel beside the value: they do not appear in the output map.
rule ReadAmount "the amount and the confidence the extractor reported" salience 20 { when in.Has("extraction.amount") then out.Set("loanAmount", in.Double("extraction.amount", 0.0)); out.Attr("loanAmount", "confidence", in.Double("extraction.confidence", 1.0));}
rule MarkManualReview "this amount came from a low-confidence read" salience 10 { when out.Has("loanAmount") && out.AttrNum("loanAmount", "confidence", 1.0) < 0.7 then out.Attr("loanAmount", "needsReview", true); out.Set("queue", "manual-review");}{ "extraction": { "amount": 412000.0, "confidence": 0.55 } }{ "loanAmount": 412000.0, "queue": "manual-review" }Reading back
Section titled “Reading back”Every read takes the same shape as the in equivalents, takes a
dotted path, and returns a default rather than raising.
out.Has(path) → bool
Section titled “out.Has(path) → bool”Reports whether anything is present at path.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to test |
rule ClassifyDisclosure "the form code names a closing disclosure" salience 20 { when in.Eq("form.code", "CD") then out.Set("docType", "closing_disclosure");}
rule ClassifyFallback "only classify if nothing else did" salience 1 { when !out.Has("docType") then out.Set("docType", "unknown");}{ "form": { "code": "CD" } }{ "docType": "closing_disclosure" }The low salience plus the Has guard is the standard “nobody claimed this” idiom: it runs last,
and only when it is still needed.
out.IsEmpty(path) → bool
Section titled “out.IsEmpty(path) → bool”Reports whether path is absent or holds an empty value: an empty string, an empty map or no
rows.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to test |
Prefer it to Has when a blank counts as unclassified: Has is true for a key written as an empty
string, and usually that is not what the rule meant.
out.Get(path) → any
Section titled “out.Get(path) → any”Returns the raw value at path, or nil.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
out.Value(path) → Value
Section titled “out.Value(path) → Value”Returns the typed value at path, or Null, with its attributes. The methods are listed under
in.Value.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
out.Str(path, def) → string
Section titled “out.Str(path, def) → string”| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
def | string | Returned when absent |
out.Int(path, def) → int64
Section titled “out.Int(path, def) → int64”| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
def | int64 | Returned when absent |
out.Double(path, def) → float64
Section titled “out.Double(path, def) → float64”| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
def | float64 | Returned when absent |
Write the default with a decimal point, 0.0, not 0, for the same type-dispatch reason as
in.EqNum. out.Double("k", 0) loads and
then aborts the execution when it runs.
out.Bool(path, def) → bool
Section titled “out.Bool(path, def) → bool”| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
def | bool | Returned when absent |
out.Date(path) → Date
Section titled “out.Date(path) → Date”Returns the date at path, or the zero date. The Date methods are listed under
in.Date.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
out.Map(path) → map
Section titled “out.Map(path) → map”Returns the map at path, or nil.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to read |
Attributes
Section titled “Attributes”Attributes are opaque producer metadata recorded alongside a value: a confidence score, the text
that matched, the page it came from. They ride along with out.Set and are readable afterwards,
but they are not part of the output map.
out.Attributes(path) → map
Section titled “out.Attributes(path) → map”Returns everything recorded for path, or nil.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to inspect |
out.HasAttr(path, name) → bool
Section titled “out.HasAttr(path, name) → bool”Reports whether path carries the named attribute.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to inspect |
name | string | The attribute name |
out.AttrNum(path, name, def) → float64
Section titled “out.AttrNum(path, name, def) → float64”Returns the named numeric attribute, or def.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to inspect |
name | string | The attribute name |
def | float64 | Returned when the attribute is absent |
The confidence gate is the usual use, and the default is load-bearing: pass 1.0 so a value
carrying no confidence is treated as certain rather than as zero-confidence.
rule LowConfidenceExtraction "flag anything read below the threshold" { when out.Has("borrowerName") && out.AttrNum("borrowerName", "confidence", 1.0) < 0.85 then findings.Warn("LowConfidenceExtraction", "extraction", strings.Sprintf("Borrower name read at %s confidence", to.Fixed(out.AttrNum("borrowerName", "confidence", 1.0), 2)));}out.AttrStr(path, name, def) → string
Section titled “out.AttrStr(path, name, def) → string”Returns the named string attribute, or def.
| Parameter | Type | Meaning |
|---|---|---|
path | string | The path to inspect |
name | string | The attribute name |
def | string | Returned when the attribute is absent |
Inspecting the whole result
Section titled “Inspecting the whole result”out.All() → map
Section titled “out.All() → map”Returns everything collected so far.
out.AllAttributes() → map of map
Section titled “out.AllAttributes() → map of map”Returns every path’s attributes, keyed by path.
out.History() → list
Section titled “out.History() → list”Returns every write in order, with the rule that made it.
out.HistoryFor(path) → list
Section titled “out.HistoryFor(path) → list”Returns the writes to one path.
Both return a list a rule can only count, with .Len(). Do not count them with
array.Len(to.List(...)): a write history is not a list to.List recognises, so it comes back as
a one-element list and the count is always 1.
History is the debugging tool for the question that otherwise takes a bisect: which rule set this,
and did anything overwrite it? On a ruleset where several rules can claim the same key, that is
the first thing to look at. rules.svc returns the history when the request asks for it
(options.history), and kis rules --history prints it.
rule DraftType "a first guess" salience 30 { when in.Has("document") then out.Set("docType", "letter");}
rule FinalType "a notice overrides the guess" salience 20 { when in.Eq("document.heading", "NOTICE") then out.Set("docType", "notice");}
rule TraceDocType "record how many times the classification changed" salience 1 { when out.Has("docType") then out.Set("docTypeWrites", out.HistoryFor("docType").Len());}{ "document": { "heading": "NOTICE" } }{ "docType": "notice", "docTypeWrites": 2 }out.JSON(): not callable
Section titled “out.JSON(): not callable”It returns a value and an error, and a rule cannot take two return values: the call loads, then
aborts the execution with calling function JSON which returns multiple values. The caller already
receives the output as JSON.
Deprecated
Section titled “Deprecated”These still load and run, with a warning from the load-time check naming the replacement.
| Deprecated | Write instead | Notes |
|---|---|---|
out.GetStr(key) | out.Str(path, def) | Takes a default |
out.GetInt(key) | out.Int(path, def) | Takes a default |
out.GetFloat(key) | out.Double(path, def) | Takes a default |
out.GetBool(key) | out.Bool(path, def) | Takes a default |
out.GetMap(key) | out.Map(path) | Same shape, current name |
out.Append(key, val) | out.Set | Appends text to what the key holds, always joined with ", ": 5 then Append("k", "x") is "5, x". There is no separator option. Lineage it was used for is in the write history |
out.UnwrapColumn(...) | delivery.ExplodeColumn(...) | Moved to the delivery plugin |
out.UnwrapColumnWithTemplate(...) | delivery.ExplodeColumn(...) | Moved to the delivery plugin |
Not for rules
Section titled “Not for rules”The collector also carries the methods the engine uses to run an execution: BindInvalidator,
SetCurrentRule, SetCurrentCycle and EnablePathKeys. They are not part of the rule surface.
The load-time check does not stop a rule calling them, and one that does changes how every later
write in the execution is attributed or nested. Do not call them.
See also
Section titled “See also”- Writing output: the same surface as a guide
in: reading facts: the other half of the pair- Findings: for validation results rather than extracted values