When verify conditions already held
alreadyTrue records declared verify conditions that held before firing. The handler still runs, and its result remains separate.
The failure this prevents
A consumer integration recorded this one from a real agent run.
The agent pressed a control while the thing that control does was already the case: it asked to
open a domain view while it was already inside that domain. The app's store publishes on change —
which is what a store does — so writing the value it already held notified nobody, updateState()
never ran, and the fire sat waiting for a state report that was never coming.
The Session does not turn elapsed time into a settlement. So the settlement stayed open for the
life of the session, did_it_work could only ever answer still-pending, and the
model read that as the app is still working. It waited, re-checked, waited — and spent fifteen of
its thirty steps on an outcome that could never arrive, then gave up with no answer.
The app is not at fault and neither is the netting. The library is the only party standing where both halves are visible, and this is it saying so.
The rule
Matching verify conditions are fire-time evidence, not an action result. When an action's
declarative verify contract covers every key it declares it writes and already holds at fire time,
the fire settles on its handler path rather than waiting for a state report and answers alreadyTrue.
const = ('desk', {
: {
: {
: {
'open-billing': {
: 'Open the billing domain view',
: ['view.domain'],
// The declaration that carries a VALUE — and the one this rule reads.
: { 'view.domain': { : 'billing' } },
},
},
},
},
});
const = .({ : 'workspace', : { 'view.domain': 'billing' } });
.('workspace', { : { 'open-billing': () => } });
const = .('workspace.open-billing', { : 'agent' });
if (.) {
.; // conditions that held before firing, not the handler's result
}The immediate tool result includes this fire-time evidence, even while its handler is pending:
{ "ok": true, "did": "workspace.open-billing", "effectStatus": "pending",
"transitionId": "workspace.open-billing#1",
"alreadyTrue": [{ "key": "view.domain", "op": "eq", "threshold": "billing",
"actualSummary": "\"billing\"", "result": true, "redacted": false }],
"why": "These declared verify conditions held before this action fired. alreadyTrue records that earlier check, not whether the action succeeded or changed anything. Read effectStatus, outcome, outcomeNow when present, and any error for the actual result." }The same marker and sentence ride settledAnswer and the settled did_it_work answer; the official
MCP adapter also folds them into a result when settlement arrives within its wait ceiling. The
conditions remain evidence about fire time, even if the handler later writes or fails. Use the
separate outcome and error fields to decide what happened.
Why the verify contract, and not writes
Effect.writes is key names only, by the law stated on it: this library never learns the value
an action would set, and it does not read your handler to find out. So "the write nets out" is a
fact writes alone can never establish — a rule built on it would have to invent the value it
compared against, which is the one thing this library refuses to do.
verify in its declarative filter form is the one declaration that carries
values: your own statement of what must hold once the action has run. So it is what this asks.
That does not make it a description of the requested input. For example, quantity >= 1 may hold
before a request to set quantity to 0; the handler can still reject that input. A range that
already holds also does not prove a later write left the value unchanged.
An action that declares writes and no declarative verify behaves exactly as it always did.
Nothing is guessed on its behalf. The correction is one line of declaration next to the action, and
you get the verify contract's own settlement verdict with it.
The five conditions
Every one of them is a refusal to over-claim.
| # | the condition | what it refuses |
|---|---|---|
| 1 | the action declares at least one writes key | an action declaring none never joined the state rail, so it cannot hang — marking it would be a word about a fate it does not have |
| 2 | the verify contract is the declarative form | a predicate is opaque by construction; asking it before the action ran asks a postcondition a question it was not written to answer |
| 3 | the contract names every declared write | the partial boundary — an action writing view.domain and view.tab under a contract constraining only the first has a declared effect the contract says nothing about |
| 4 | the contract holds at fire time, evaluably | a failed or unevaluable check supplies no matching fire-time evidence |
| 5 | any declared goTo is the page you are on at fire time | a matching state check does not establish a navigation that has not happened |
Your handler still runs
Read alreadyTrue as these verify conditions held before firing. The library does not skip your
handler. It may write, finish without a change, or fail; none of those results follows from the marker.
What changed is only what the fire waits on: the handler path instead of the state-report queue. A handler that throws is still refused, and an unresolved handler is still pending. The marker does not establish current conditions after execution or suppress a later rejection or rollback.
The fire also never joins the state-report queue, and that is the second half of the fix. A phantom sitting in that queue would let the next real report settle it by FIFO — certifying an agent's no-op as the verified cause of motion somebody else performed.
What the record shows
A record retains the fire-time condition evidence alongside its execution result. In the concrete idempotent example, an open-billing handler finishes successfully without changing the billing view:
- that example's row commits with an empty commit bundle;
- its effect is
'unobservable'because no write report establishes a change; - after successful settlement, a journey can count the step done and any held single flight is released.
Those are results of that example, not guarantees from alreadyTrue. A failed action keeps its
failure, and a later rollback remains visible through the live outcome (outcomeNow when it differs
from the receipt). The condition evidence is not refreshed to describe the later world.
Run it
npm run example:already-true prints one real run of exactly this: the already-true press, the
ordinary press beside it for contrast, and the same press on an action that declares writes and no
verify — the honest limit, shown rather than described. Nothing in the transcript is hand-written;
it drives the port a remote model actually holds. Source: examples/already-true/.
Honest limits
- It checks declarations against Session state. No DOM is watched, no handler code is read, and no intended write value is inferred. An action that declares nothing here serves byte-identical results.
- An allowed unmaterialized fire keeps its own word.
materialized: falsealready says nothing in the app was wired to execute it; a second marker beside it would be two words for one press, soalreadyTrueis never stamped there. - It is not an outcome. A minted
transitionIdidentifies the fire, while settlement and error fields describe its result. The marker alone proves neither success nor failure.
Waiting for the app
The async story in one place — your handler's promise is the completion signal, a transitionId threads identity on the state rail, and the one served await has a ceiling that never becomes a verdict.
When the app is still working
The work ledger — your app says what it is still doing, bound to the fire it belongs to. A row, never a latch: closing it settles nothing, and no clock here will ever close it for you.