Observing a run
A guard sees what it stands in front of. It does not see the boundaries of steps nothing guards, and it does not see the
run end. observeRun fills exactly that gap.
observeRun(monitor, run, { guardedSteps: guardedStepIds(workflow), onEvent: (event) => recorder.record(event),})guardedSteps is how it avoids recording anything twice. And guardedStepIds reports what the guard actually stands in
front of, which is not the same as every key in the step map: mapping, agent and tool entries are declared by id and
built at execution time, so the guard never sees them and the observer must.
Why double-reporting is the failure mode to watch for
Section titled “Why double-reporting is the failure mode to watch for”A framework commonly emits both a step’s outcome and its own bookkeeping for the same step. Mapping both doubles every step event — which halves the effective threshold of any rule that counts, silently. A budget of eight becomes a budget of four, and nothing in the output says so.
That is why guardedStepIds exists rather than a list you maintain by hand.
The end of the run
Section titled “The end of the run”observeRun’s onFinish gives you end-of-run resolution: the verdicts for obligations that were still open when the run
ended. This is the only place unresolved appears — a bounded-future obligation that the run stopped before discharging
is not evidence that a property broke, and it gets its own status rather than being folded into violated.
The verdict lattice is what those four statuses mean and why there are four.