Skip to main content
Before the NSP daemon dispatches any action to a runtime probe, it classifies the action into a reversibility tier and enforces the conditions that tier requires. This classification is a safety contract — not a suggestion, and not something you can opt out of at call time. The intent is to make it structurally impossible for an agent to perform a permanent, real-world side effect without explicit, verifiable confirmation that the operation succeeded. This page explains the three tiers, the gate that guards irreversible operations, and how to write verify_expression strings that satisfy the gate correctly.

The Three Reversibility Tiers

Every action schema carries a reversibility field set when the action is discovered from the target application’s runtime. The daemon reads this field and applies the corresponding tier rules before touching the probe.
A read action retrieves data without modifying any application state. There is no confidence gate and no verification requirement. You can call read actions at any time, regardless of the probe’s current confidence score.Confidence required: Noneverify_expression required: NoExamples: get_thread, search, list_contacts, read_document, get_calendar_eventsIf you call a read action and the probe is still initializing (confidence below 0.40), the daemon returns a 503 confidence_too_low error. This is not a safety gate — it is simply the probe not being ready yet.
A reversible_write action modifies application state in a way that can be undone — for example, archiving an email (which can be unarchived), adding a label (which can be removed), or creating a draft (which can be deleted).Confidence required: >= 0.80verify_expression required: No (optional but recommended for critical workflows)Examples: archive, label, move_to_folder, mark_read, star, create_draftIf the probe’s confidence is below 0.80 when you dispatch a reversible_write action, the daemon rejects the request immediately with 503 confidence_too_low. Wait for the probe to reach a higher confidence before retrying — this usually resolves within a few poll cycles.
An irreversible_write action causes a side effect that cannot be undone: sending an email, permanently deleting a record, submitting a purchase, sending a message, or submitting a form.Confidence required: >= 0.95verify_expression required: Yes — the daemon rejects the request with 400 missing_verify_expression if you omit itExamples: send_reply, delete_permanently, purchase, submit_form, send_messageThe verify_expression is evaluated against the post-action state. If the expression evaluates to true, the daemon returns a 200 success response. If it evaluates to false — or if the timeout elapses before it becomes true — the daemon returns 422 verification_failed. The action has already executed at this point, so design your expressions to confirm a durable state change, not just that the action ran.

The Irreversible Write Gate

When you call POST /action with an irreversible_write action, the daemon enforces the gate synchronously before dispatching. A request missing verify_expression is rejected outright — the action does not execute. Rejected request — missing verify_expression:
Accepted request — verify_expression present:

Dangerous Name Heuristic

As an additional safety layer, the daemon inspects the action name itself before dispatching — even when a verify_expression is present. If the action name contains any of the following substrings, the daemon treats the action as potentially irreversible and requires a verify_expression regardless of the schema’s declared reversibility tier: delete · destroy · purge · wipe · reset · clear_all · terminate · shutdown · kill This heuristic catches cases where an action’s schema metadata may not yet be available — for example, when the PID cannot be resolved and the daemon cannot look up the action schema. In that situation, an action named delete_attachment or purge_queue would be rejected with 400 dangerous_action_unverified unless a verify_expression accompanies the request.
The dangerous name heuristic is a fallback, not a substitute for correct schema metadata. If you author custom action schemas, set the reversibility field accurately. The heuristic only fires when schema lookup fails or the name pattern matches.

Writing verify_expression Strings

A verify_expression is a JavaScript-like expression evaluated against the post-action SubstrateState. It must return a boolean. The daemon polls the state until the expression returns true or the verify_timeout_ms deadline passes.

Supported Operators

Expression Examples

verify_timeout_ms

The verify_timeout_ms field controls how long the daemon polls the post-action state waiting for your expression to return true. The valid range is 100–30000 ms, and the default is 5000 ms (5 seconds). Choose a timeout that reflects the real latency of the state change you are verifying:
  • For UI changes that are visible within milliseconds, 10002000 ms is plenty.
  • For operations that trigger server-side writes (sending an email, submitting a form), use 500010000 ms.
  • For long-running background operations, go up to 30000 ms — but consider whether a separate polling loop is a better pattern for your use case.

Python SDK Enforcement

The Python SDK enforces the irreversible write contract at the client level, before the request is even sent. If you call session.execute() on an irreversible_write action with verify=False, the SDK raises NSPIrreversibleNotConfirmedError locally — no network request is made.

Verification Failure Response

If the action executes but the post-action state never satisfies your verify_expression before the timeout, the daemon returns a 422 verification_failed response:
A 422 verification_failed response means the action did execute — the side effect happened. The verification expression simply did not confirm a state change in time. Do not automatically retry on a 422: check the actual application state before deciding whether to act again.