Skip to content

Hold 0047's two moments per entry, and answer the age they anchor (#47) - #297

Merged
iderex merged 1 commit into
mainfrom
the-two-moments-a-queued-action-carries-47
Sep 4, 2026
Merged

iderex merged 1 commit into
mainfrom
the-two-moments-a-queued-action-carries-47

Conversation

@iderex

@iderex iderex commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Belongs to #47. Neither of that issue's two conditions is met here, and the
reason is unchanged: both restart the core and restore a server, and nothing in
this tree does either.

It touches two files a second issue owns. src/playback/report.rs is #57's and
src/session/mid_playback.rs is #35's, and neither could be separated from this:
0047 stores the two moments AT ENQUEUE, so the value has to arrive from whoever
calls the enqueue, and those are the only callers in the tree.

git grep -n '\.enqueue(' origin/main -- src/ | grep -v 'src/server/write_queue.rs'
origin/main:src/playback/report.rs:219:        let what_the_queue_did = queue.enqueue(
origin/main:src/playback/report.rs:255:        let what_the_queue_did = queue.enqueue(
origin/main:src/playback/report.rs:287:        queue.enqueue(

What changed

A queue entry carries the two moments docs/decisions/0047-the-write-queue.md
says it stores when it is enqueued - the server's own last stated time and the
device's wall reading at that instant - and answers how long it has been waiting
from them.

The pair, the correction and the two guards are crate::cache::freshness's own
types rather than a second arithmetic in this module. 0047 says the age is
computed the way 0043 computes a cache entry's, on 0102's anchor, with the same
correction and the same two guards, and one type is how that stays true rather
than being kept true by whoever edits next.

Every moment is handed in. Nothing in the module reads a clock, which is what the
module already said of itself and what the no-platform-clock rule in
.github/invariants/rules refuses anywhere under src/.

Two things 0047 leaves open are decided here rather than at a call site later.

A replacement keeps the earlier entry's two moments, as it keeps its position
in the order.
0047 says only the second. Taking the later action's moments is
the shape that reads as obvious, and it reports every actively touched entry as
freshly enqueued, so a queue undelivered for a month says seconds for the items
somebody kept scrubbing.

The age acts on nothing, and that is now held by a case. It was held by there
being no age at all, which is a guarantee that ends the moment one arrives.

Reporting::report, Reporting::observe, Reporting::report_after_a_renewal and
mid_playback::the_renewal_ended take the pair and pass it through, for the
reason they already take an ElapsedInstant.

What failure it prevents

The one 0047 names for a restored queue, arriving from two directions.

A queue restored with no anchor keeps its order and loses every age, so a client
can say only that something is pending. What the record wants it to be able to
say is that an action has been waiting three weeks, which is the difference
between an operator seeing a device that is behind and an operator seeing
nothing.

The second direction is the one this change had to decide rather than inherit. A
replacement that took the later action's moments loses the age of everything
somebody is actively touching, which is the same loss reaching the person who
used the application most - the person 0047 gives as the reason for coalescing at
enqueue rather than at drain.

Neither has happened, because nothing in this tree restores a queue. Both are
stated as what the record argues against rather than as an incident.

Evidence

The commit being pushed:

git rev-parse HEAD
62271e51823e9be89fac858870f3db72c59b75ad

The two commands CONTRIBUTING.md names, run at it:

cargo build --locked --all-targets
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.85s

cargo test --locked
test result: ok. 584 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 125 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Seven cases are new in the module:

git show origin/main:src/server/write_queue.rs | grep -c '#\[test\]'
13
grep -c '#\[test\]' src/server/write_queue.rs
20

Four gate legs run by hand on this machine, each with its own verdict:

bash .github/format/format.sh check
Every tracked source file above is written the way the formatter would write it.

bash .github/lint/lint.sh check
Every lint the groups above carry was refused, outside the register printed with it.

bash .github/invariants/invariants.sh check
Every rule above was applied to its subject and refused nothing.

bash .github/doc-paths/doc-paths.sh check
Every path these documents name resolves against the tracked set.

What a guard here refuses, and the proof it bites

Three, each watched failing on its own violation and each failing alone. Every
run below is cargo test --locked --lib server::write_queue, and the green run
at the head is:

test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 564 filtered out

a_replacement_keeps_the_earlier_entrys_moments refuses a coalescing that
takes the later action's moments. The violation is the one line somebody adds
while thinking the entry should carry what it now says:

             held.assertion = assertion;
+            held.enqueued_at = enqueued_at;
             return WhatTheEnqueueDid::ReplacedInPlace;

test server::write_queue::tests::a_replacement_keeps_the_earlier_entrys_moments ... FAILED
assertion `left == right` failed: the replacement took the later action's moments
test result: FAILED. 19 passed; 1 failed; 0 ignored; 0 measured; 564 filtered out

a_device_clock_that_jumped_forward_is_corrected_out_of_the_age refuses an
age computed without the current skew, which is 0043's correction being dropped
on the way through this module:

-        Age::at_read(self.enqueued_at, the_devices_wall_now, the_skew_now)
+        let _ = the_skew_now;
+        Age::at_read(self.enqueued_at, the_devices_wall_now, None)

test server::write_queue::tests::a_device_clock_that_jumped_forward_is_corrected_out_of_the_age ... FAILED
test result: FAILED. 19 passed; 1 failed; 0 ignored; 0 measured; 564 filtered out

an_entry_is_never_expired_by_age refuses an expiry, which is the thing the
age's arrival makes writable for the first time. The violation is housekeeping at
enqueue, dropping what has waited past the bound beyond which an age is not
believed:

+        let bound = i64::from(365 * 24) * 60 * 60;
+        let now = enqueued_at.the_devices_wall_moment().seconds_from_the_epoch();
+        self.entries.retain(|entry| {
+            now - entry.enqueued_at().the_devices_wall_moment().seconds_from_the_epoch()
+                <= bound
+        });

test server::write_queue::tests::an_entry_is_never_expired_by_age ... FAILED
test result: FAILED. 19 passed; 1 failed; 0 ignored; 0 measured; 564 filtered out

What this does not cover

Nothing is durable, which is what an anchor is for. 0047 puts the bytes in the
store 0040 defines, keyed under #41, and no restore exists to compute an age
across. What landed is the value a restore would restore.

Nothing reads an age. Entry::age_at is answered where somebody asks, and no
caller asks: there is no diagnostics event, no capability call and no client
surface for it, because nothing in this tree holds a queue at a point where one
could be made.

Nothing checks that the two moments were read at one instant, which
WrittenAt::at already says of itself. 0047 makes the server half the LAST
stated time rather than a reading taken at the enqueue, so the gap between the
two is wider here than in the cache, and an entry queued long after the server
last spoke carries that gap in its skew and reports itself older than it is. That
is the record's own shape rather than a defect in this change, and it is written
down here because no reading of the tree says it.

Nothing measured anything. There is no number in this change that came from a run
of the core.

The coverage leg was not run on this machine. .github/coverage/coverage.sh is
the run that judges the bar, src/server/write_queue.rs is already a module on
the pinned surface, and the verdict is the runner's on this pull request rather
than anything asserted here.

The thread detector was not run on this machine either. It names a toolchain of
its own, and the leg on this pull request is where its verdict comes from.

Who has read it

Nobody other than me. There was no second reader available, and the evidence
above stands in place of one.

0047 says an entry stores two moments when it is enqueued - the server's own
last stated time and the device's wall reading at that instant - and that its
age is computed the way 0043 computes a cache entry's, on 0102's anchor, with
the same correction and the same two guards. The queue that landed carried
neither, and its own module documentation said so.

What that costs is the failure 0047 names for a restored queue: one that treats
every entry as freshly enqueued keeps its order and loses every age, so a client
can say only that something is pending rather than that an action has been
waiting three weeks.

An entry now carries `WrittenAt` and answers `Entry::age_at`. Both are
`crate::cache::freshness`'s own types rather than a second arithmetic here, which
is what naming 0043 asks for and what stops the two drifting. Every moment is
handed in, so nothing in the module reads a clock.

A replacement keeps the earlier entry's two moments, as it keeps its position in
the order, which 0047 does not say. Taking the later action's moments reports
every actively touched entry as freshly enqueued, so a queue undelivered for a
month says seconds for the items somebody kept scrubbing - the restored-queue
failure arriving through the coalescing door, landing on the person coalescing at
enqueue exists to protect.

The age acts on nothing, which is 0047's rule. That absence used to be held by
there being no age at all; it is held by a case now, because a guarantee that
rests on a thing not existing ends the moment it does.

`Reporting::report`, `Reporting::observe`, `Reporting::report_after_a_renewal`
and `mid_playback::the_renewal_ended` take the pair and pass it through, for the
reason they already take an `ElapsedInstant`: nothing under `src/` reads a
platform clock.

Neither of #47's two conditions is met by this. Both restart the core and restore
a server, and nothing in this tree does either.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 80eb723 into main Sep 4, 2026
27 checks passed
@iderex
iderex deleted the the-two-moments-a-queued-action-carries-47 branch September 4, 2026 14:28
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