Skip to content

Adds ADR-0074: delayed sends across a persisted resume - #349

Merged
johnnyt merged 1 commit into
mainfrom
st-yamg-delayed-sends-across-resume
Sep 26, 2026
Merged

johnnyt merged 1 commit into
mainfrom
st-yamg-delayed-sends-across-resume

Conversation

@johnnyt

@johnnyt johnnyt commented Sep 26, 2026

Copy link
Copy Markdown
Member

What

ADR-0074, at Status: proposed, decides three points about delayed sends across a persisted resume that ADR-0060 decision 7, ADR-0054 and ADR-0069's 2026-09-23 Note leave to the durable host without stating them. Statifier.Send.Processor's moduledoc points at it.

  1. The fire time. The absolute fire time lives on the host's own timer row, computed by the host once, when it is first handed the %SendDelayed{} (due_at = now + delay_ms). Re-arming after a resume reads that row and never recomputes from delay_ms. The position still carries no instant, so ADR-0060 decision 7 stands.
  2. The cancel's transaction. A step's timer-store writes commit in the same transaction as the position that step produced. That includes the removal of every row under {session scope, send_id} for a %Cancel{}. The record states what each order loses when the writes are split.
  3. A stale fire. The host drops it, the engine does not. Before delivering, the host atomically claims the send's own row, found by the dedup key whose first two components are {session scope, send_id}, and then runs ADR-0054 decision 4's liveness check. It does not check the session's configuration: a delayed send is not scoped to the state that armed it. The record gives the reasons the engine cannot make the check (no armed-send set in the position, no send identity on a fired event). Since the engine drops nothing, no test pins a drop, and the record lists what the host checks.

No function, struct, effect field or position field changes. There is no changelog fragment, because changelog.d/README.md lists "documentation, ADRs, or plans" under "Do not write a fragment for".

Record rule applied

From docs/adr/README.md: "New ADRs: next number, same three-section format (Context, Decision, Consequences), drafted or reviewed at the direction level per docs/workflow.md. Pick the number against a freshly fetched remote - git fetch origin && git ls-tree origin/main --name-only docs/adr/ - so a branch does not start behind a record that has already landed". The number 0074 was picked after a fresh fetch. The highest record on origin/main was 0073, and the one open request touches no docs/adr/ file. The README row is added at proposed.

Direction check (in-turn review)

I re-read the diff against the bead's acceptance and checked every claim in the record on main at eb114947, by anchor:

  • Statifier.Effect.SendDelayed's struct carries delay_ms and no instant.
  • Statifier.MachineState's defstruct has send_counter and timer_counter and no field naming an armed send.
  • Statifier.Session's timers and held_sends fields are session state.
  • Statifier.Send.Event.build/3 sets sendid from id_from_author?, and %Statifier.Event{} has no ordinal.
  • c:Statifier.Send.Processor.cancel/2 exists.
  • Decisions 3, 4, 6 and 7 of ADR-0060, decisions 2, 3 and 4 of ADR-0054, and ADR-0069 decision 4 with its 2026-09-23 Note say what the record cites them for.
  • The SCXML 6.2 and 6.3 sentences are quoted from the local spec cache.

git diff origin/main -- docs/adr/ shows zero removed lines: one new record file and one added README row.

Provenance

  • I chose a new record over an Amendment on ADR-0060. The three decisions span ADR-0054, ADR-0060 and ADR-0069, and they amend none of them.
  • Decision 2 also covers the insert of a %SendDelayed{} row, not only the cancel. The same crash window applies to it, and decision 1's due_at rides on that row.
  • The host-side code the record calls for lives in the durable host packages, not in this repository: storing due_at at hand-off, committing timer rows with the step's position, and the atomic claim at fire time.

Gate

  • Full mix quality is green on the committed tree.
  • mix quality --profile merge (the ADR judge) is green.
  • The terminology and planning-id scans are clean.

Refs: st-yamg

ADR-0074 at Status: proposed decides three points the resume record's
decision 7, ADR-0054 and ADR-0069's 2026-09-23 Note leave to the durable
host. The absolute fire time lives on the host's own timer row, computed
by the host once when it is first handed the send, never in the position.
A step's timer-store writes, a cancel included, commit in the transaction
that saves that step's position. The host drops a stale fire by an atomic
claim on the send's row under the cancellation key, plus ADR-0054's
liveness check; the engine drops nothing, so no test pins a drop.

Statifier.Send.Processor's moduledoc points at the record. No function,
struct, effect field or position field changes.

Refs: st-yamg
@johnnyt
johnnyt merged commit 5d579cc into main Sep 26, 2026
1 check passed
@johnnyt
johnnyt deleted the st-yamg-delayed-sends-across-resume branch September 26, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant