Skip to content

Pins timer fires off the routing ledger - #156

Merged
johnnyt merged 1 commit into
mainfrom
sr-31nh-timer-fires-stay-off-ledger
Oct 2, 2026
Merged

johnnyt merged 1 commit into
mainfrom
sr-31nh-timer-fires-stay-off-ledger

Conversation

@johnnyt

@johnnyt johnnyt commented Oct 2, 2026

Copy link
Copy Markdown
Member

What

Records that a fired timer is not a routed delivery and writes no routing
ledger row, and pins it on a real timer queue. No behaviour changes.

  • docs/adr/0004-the-refusal-and-drop-vocabulary.md: a dated Note at the
    foot. Fires stay off the ledger in this release, with no timer outcome
    added (ruled by the operator, 2026-10-01). The Note restates the
    ledger's claim as every routed delivery, gives the reason fires stay off
    (the timer queue's own job delivers a fire and its job row records it;
    a row per fire would change what the ledger answers for every host
    whose charts arm timers), and says where a host sees a fire into a
    finished execution: the timer job, cancelled with the discard on its
    row.
  • lib/statifier_router.ex: the moduledoc bullet "every delivery attempt
    ends in one named outcome" now says every routed delivery attempt, and
    that a fired timer is not one.
  • test/statifier_router/timer_fire_ledger_test.exs: a parcel chart arms
    a delivery window. The window is scheduled on statifier_oban, on the
    suite's Oban instance and a queue of the file's own, and fired by
    draining the queue through a delivery module written the way a
    process-less host writes one. The fire into a live execution steps it
    and the fire into a finished execution is cancelled on its job; neither
    writes a ledger row or changes the address table, and a routed delivery
    to the finished execution then writes its dropped: finished row. The
    executor hands every effect to StatifierRouter.SendHandler, and the
    fire step arms a second window, so the fire also passes the router's
    own effect seam.

Why ADR-0004 and not ADR-0003

ADR-0004 is where the ledger's claim is made: section 1 gives each
binding one outcome per routing attempt and section 4 writes one row per
recorded outcome. ADR-0003 decides the delivery transaction, and its
Amendment of 2026-10-02 already lists a timer job's step as one the
router did not drive; the Note cites it.

Direction check (records)

Every claim in the Note was read by anchor against main at 184d96d:
StatifierRouter.Delivery.deliver/4 and deliver_event/4 (the doors
that write ledger rows), StatifierRouter.BasicHTTP.Front.handle/3
(delivers through deliver_event/4), StatifierRouter.TimerQueue
("Firing a row") and ADR-0003's Amendment of 2026-10-02 (the per-shape
table that names a timer job's step). statifier_oban cites were read at
its v0.17.0 tag, the version mix.lock resolves:
StatifierOban.Timer.Worker (a delivery that answers {:discarded, reason} cancels the job with the answer recorded),
StatifierOban.Timer.Delivery ("The contract for a process-less durable
host") and StatifierOban.Telemetry (the [:statifier_oban, :timer, :discarded] event). The cancelled job and its recorded discard, and
step/5's {:discarded, execution} for a terminal execution, are pinned
by the new tests. git diff origin/main -- docs/adr/ shows zero removed
lines.

Gate

mix quality green locally on this head (Elixir code and tests
changed). No changelog fragment: documentation and tests only, per
changelog.d/README.md.

Sabotage

Each mutation was applied, run, and restored byte-equal before the next.

  • The test's delivery module also handed the fired event to
    StatifierRouter.Delivery.deliver_event/4 after the step (the
    router-owned timer delivery this pins the absence of): both tests went
    red on their ledger assertion.
  • StatifierRouter.SendHandler.handle_effect/3 wrote a ledger row for
    the second window's delayed send, which only the fire step emits: the
    live-execution test went red on its ledger assertion.
  • The test's delivery module also stamped the address row: both tests
    went red on their address assertion.
  • finished/6 in StatifierRouter.Delivery skipped its ledger write:
    the routed control in the finished-execution test went red.

Provenance

Rebased onto the commit that runs the corpus timers on an Oban queue,
which made statifier_oban a test dependency; the tests use it rather than
an in-process fire. No conflict. The delivery module in the test reuses
the corpus runner's public step_fired/4.

Not decided here

A delivery of a fired timer that this package would own, writing a ledger
row of its own, is left for a later decision.

A fired timer is not a routed delivery: the timer queue's own job steps
it into its execution, and no door of this package runs. A dated Note
on ADR-0004 records that fires stay off the ledger, why, and where a
host sees a fire into a finished execution (the timer job, cancelled
with the discard on its row). It restates the ledger's claim as every
routed delivery, and the StatifierRouter moduledoc bullet now says the
same.

New tests schedule a parcel delivery window on statifier_oban, fire it
by draining the queue into a live and a finished execution, and find
no new ledger row and an unchanged address table either way; a routed
delivery to the finished execution then writes its dropped: finished
row. No behaviour changes.

Refs: sr-31nh
@johnnyt
johnnyt merged commit 50fea7a into main Oct 2, 2026
1 check passed
@johnnyt
johnnyt deleted the sr-31nh-timer-fires-stay-off-ledger branch October 2, 2026 10:46
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