Outputs¶
A playbook's outputs block names where a DONE run's findings are announced besides the store. What is announced:
a stored open or confirmed finding never announced before (new), one last announced repeatWindowHours ago or
more (repeat), and a finding the run resolved that had been announced (resolved). A run finds a candidate's row by
its fingerprint (sha256(template | collection | candidate id)), else by the same playbook, collection and candidate
id, so a row stored under another fingerprint continues and takes today's. A finding that comes back after
a run resolved it reopens its own row (same id, its notes and verdicts kept) and is announced again as new, even
inside the repeat window. Hidden and suppressed findings are never announced. No output failure changes the run.
A resolved finding says why it closed. Its entry in the session message, its MQTT message and the
callback's finding dict carry reason — the resolve reason of section 8 ("When a finding closes": fire-passed,
stretch-passed, not-screened, quiet, fell-back, within-band, not-checked, not-found); the key is absent when none was recorded. The
email digest's Resolved list adds it in plain words after the subject, for example "(closed: no longer screened)".
Every other key is unchanged.
| Output | Key | What |
|---|---|---|
| Session message | perspectiveMessage: { "type": "artekisFinding", "scope": "session" } |
at most one system.util.sendMessage per run to the playbook's own project, session scope, and only when the run has a finding that is new (reopened included), resolved, or escalated (its severity rose past the highest it has had; sent even inside the repeat window and not moving last_notified_at); a finding that merely continues (repeat) sends none, and is left to the email digest and MQTT. The views refresh on it, and Findings, FindingDetail and the Anomaly Watch queue also poll every 60 s, so a continuing finding's new last seen and count show without a message. It is opt-in per playbook (the Playbooks form's notification switch, on for a playbook created there); without it the views refresh only by polling. Never leaves the gateway |
| Email digest | email: { "profile", "from", "to": [], "on": ["severity>=high"] } |
one HTML email per run through the named gateway email profile (from is required: a profile has no default sender; on filters by severity, nothing is sent when no finding passes). Severity badges, title, the subject as the plant names it (the context model's names, else its path), narrative, the "Do now" callout, the type-level reach line and each open finding's Cost with its kind (section 7.8: attributed, above normal, risk or not priced) render, findings worst first, then newest, in a standard HTML mail client |
| MQTT | mqtt: { "connection", "topic", "qos": 1 } |
one JSON message per finding through the Artek MQTT suite (a priced new or repeated finding's message carries cost {kind, label, amount, currency, reason, figure, arithmetic, overlaps}, section 7.8; the callback's dict the same); the connection must be on policy.outputs.mqttConnections; without the suite the row records NO_SERVICE |
| Incident tags | tags: true (default) |
the per-asset Incident tags (section 8.3) |
| Memory tags | memoryTags: { "likely_cause": "[default]Line2/Out/Cause", … } |
maps likely_cause, confidence, action_now, finding_id, updated_at, open_findings, highest_severity to memory tags under a policy.outputs.memoryTagAllowlist prefix; checked when saved through playbooks.put (a Designer save of the file skips that check; the write-time check still applies) and before every write. Written on every DONE run that maps them: the two counts from the collection's store, the five finding fields from the most severe finding the run announces (an Incident run's follow its headline) |
| Callback | callback: "package.module.function" |
a function in the playbook's own project under a policy.outputs.callbackAllowlist prefix, called once per announced finding with the finding dict, on its own pool, 30 s from being queued |
Email and MQTT sends are egress rows with an outcome (SENT, PUBLISHED, QUEUED, REJECTED,
NO_CONNECTION, NO_SERVICE, FAILED). Memory tags and callbacks never leave the gateway, so a success leaves no
row; a failed write or callback does (REJECTED or FAILED), and the Status page's delivery failures list it.
The [Artekis] tag provider is created read-only when the module loads and rebuilt from the database at every
start and whenever this gateway becomes the database owner. Its tree is a contract — bind to it, never write to it:
| Path | Written |
|---|---|
[Artekis]Collections/<c>/Health/OpenFindings, HighestSeverity |
after every DONE run on the collection |
[Artekis]Collections/<c>/Runs/<project>/<playbook>/LastRunAt, LastState, LastDurationMs |
at every run end; a cron playbook's go Bad_Stale (value kept) when its last DONE run is older than twice its interval |
[Artekis]Collections/<c>/Assets/<asset>/Incident/… |
by Incident (section 8.3) |
[Artekis]System/Provider/<name>/UsdToday, TokensToday, LastError |
after each run that called the provider and once a minute; reset at local midnight in the settings timezone |
[Artekis]System/Owner, System/Scheduling |
on change |
A collection or asset id holding . appears with : in these paths (system.artekis.outputs.tagPath builds the right
one). On a gateway that does not own the Artekis database every tag reads Bad_Stale with its last value.