Runbook + executed offline reliability lab

Treat completion notifications as a delivery protocol

A task hook can fire without a person ever seeing its message. A notification service can accept a message while the dispatcher loses the acknowledgement. A process can restart between those two facts. Design for those states instead of labeling one callback “exactly once.”

This offline lab models terminal task events as durable local records and sends them to a fake sink. It tests stable event IDs, duplicate input, retry limits, a process restart after send but before acknowledgement, out-of-order input, and a dead-letter record. It does not use a notification product, network, account, sound, desktop surface, or real task transcript.

Keep four facts separate

  1. Intent recorded: the event is durably known to the dispatcher.
  2. Attempt made: the dispatcher called a sink. The call may fail or return no trustworthy acknowledgement.
  3. Sink accepted: the fake sink stored the stable event ID. This is not proof of operating-system display or human attention.
  4. Dispatcher acknowledged: local state records the sink response, so a restart need not attempt that event again.

The gap between sink acceptance and dispatcher acknowledgement is unavoidable in this toy protocol. If the process stops in that gap, it cannot know whether the sink accepted the event. On restart it sends the same stable ID again. The sink must handle that ID idempotently or a duplicate side effect is possible.

State transitions in the fixture

SituationDurable dispatcher stateFake-sink stateNext action
New eventpending, zero attemptsAbsentAttempt delivery
Synthetic sink failurepending, error and attempt retainedAttempt counted, not acceptedRetry until limit
Accepted and acknowledgedacknowledgedStable ID acceptedDo not resend
Accepted, then process stops before acknowledgementStill pendingStable ID acceptedRetry same ID after restart
Retry of already accepted IDacknowledged, ack: duplicateOne accepted record, two attemptsStop retrying
Retry limit reacheddead-letter, final error retainedNot acceptedRequire separate review

“Duplicate” here is a successful idempotency response from the fake sink. It does not mean the dispatcher achieved exactly-once delivery. The demonstrated contract is narrower: at-least-once attempts, stable IDs, and sink-side deduplication for this fixture.

What the run exercised

The input deliberately arrives in sequence order 2, 1, 3, duplicate 2, then 4. Ingestion stores four unique records and counts one byte-equivalent duplicate. Dispatch sorts by sequence without collapsing the distinct needs_attention, completed, and failed terminal kinds.

The first child process accepts evt-completed-002 at the sink and exits with code 75 before it can store the acknowledgement. A fresh child process opens the same file-backed state, retries the stable ID, receives duplicate, and acknowledges it. Another event fails once, retains that error in its history, and succeeds on retry. The final event fails three times and retains its complete error history plus its last error in a dead-letter record.

Three events are acknowledged, three unique IDs are accepted by the fake sink, and one event is dead-lettered. No human-notified result was observed or claimed.

Reproduce it

Prerequisite: Node.js 24 or newer. The recorded run used Node.js 24.19.0 and built-in modules only. It used a temporary directory and no network, credentials, third-party packages, notification service, or user repository content.

Download the three files into one directory, rename the script from .mjs.txt to .mjs, then run:

node notification-delivery-lab.mjs lab notification-delivery.synthetic.json result.json

Two isolated recorded runs both exited 0 and produced byte-identical results. Inside each run, the deliberately interrupted child exited 75 and the restarted child exited 0. The exact byte count and hash are recorded with the build evidence. Matching hashes show deterministic serialization for these inputs and this runtime, not universal delivery reliability.

The fixture demonstrates state surviving a controlled process exit after a completed file write. It does not test power loss, filesystem corruption, atomic replacement, write barriers, or fsync; production durability requires a storage design that covers those failure modes.

The downloadable toy accepts at most 100 events, 200 characters per message, and deliberately narrow lowercase identifiers. Child phases have a 10-second timeout and 1 MiB output cap. Those bounds make the fixture inspectable; they are not production sizing guidance.

Apply the protocol carefully

Use stable IDs derived from a durable lifecycle record, not a timestamp generated inside a hook. Minimize payloads: repository names, task titles, commands, and failure text may be sensitive. Keep retry limits and dead letters inspectable. Make the real sink’s idempotency behavior explicit, because some notification surfaces cannot deduplicate.

Before integrating a real host or notification system, verify its current event schema, ordering and retry behavior, configuration scope, data egress, credentials, and acknowledgement semantics from primary documentation. Then test each delivery surface separately. A sink acknowledgement is not evidence that a sound played, a banner appeared, a mobile device received anything, or a person paid attention.

Continue

Keep reading

Bounded agent loop →

All library entries →