Skip to content

Name the modules that hold a cache key's device part and its record of written keys (#493) - #494

Merged
iderex merged 1 commit into
mainfrom
seam/cache-key-addresses-closed-36-42
Sep 18, 2026
Merged

iderex merged 1 commit into
mainfrom
seam/cache-key-addresses-closed-36-42

Conversation

@iderex

@iderex iderex commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Closes #493

What changed

KeySpace in src/cache/key.rs stops giving two of its own parts addresses on
the tracker. The device part was "the device identity from #36", written once in
the paragraph and once on the field, and the core's record of which keys it wrote
was "the bookkeeping 0040 hands to #42". Both now name the module that holds the
thing: crate::session::device, and the bookkeeping inside
crate::cache::bound::TieredCache.

The paragraph also gains the bound that record carries. TieredCache accounts
for what THIS run wrote and the index does not survive a restart, which
src/cache/bound.rs states of itself. That is the half a reader of KeySpace
needs in order to know what 0068's promise over a sign-out is paid by, and it was
in a module they were not sent to.

A second paragraph records what the two sentences said and tells the two states
apart with the dates that decide them, rather than writing them as one thing.

Nothing else moves. No behaviour, signature, field, bound or event.

What failure it prevents

It sends a reader out of the checkout twice over one struct. The other two parts
of KeySpace are written the way the third was not - the server part names the
record 0041 and the account part says what it is in words - so only the device
part sent somebody to the tracker, for something that is a module in the same
crate. The second address cost more: the limit that actually matters, that the
record of written keys covers this run and not what an earlier run left in the
store, is written in src/cache/bound.rs and the reader was sent to a closed
issue instead.

The two halves are not in the same state. #42 closed as completed sixteen hours
before src/cache/key.rs landed, with src/cache/bound.rs already in the tree,
so that half LANDED REFUTED. #36 was still open and src/session/device.rs did
not exist when the file landed, so from #36 was the best available address and
WENT STALE six hours later. The correction says which is which.

It was found by reading every issue number this tree names outside
docs/decisions/ against the state of that issue on the tracker.

The means

Doc comments in the Rust source that carries the claim, which is the only means
that puts the correction where the sentence a reader meets is. A record under
docs/decisions/ would leave both addresses standing on the struct somebody
opens to find out what goes in the field. No language, runtime or dependency is
added, and the suites that already exist judge the result, because cargo doc
resolves the three links the change adds.

Evidence

The commit being pushed:

git rev-parse HEAD
c0e5b81b8d8ee179805a8547958477e3dd4ae625

What the type said, at the parent:

git show c0e5b81^:src/cache/key.rs | sed -n '111,130p'
/// The three parts of a key that do not change between two requests.
///
/// The server, the account as the identifier the server gave back at sign-in
/// rather than the username, and the device identity from #36. 0068 promises a
/// caller that signing out removes every entry under one of these, and 0041
/// makes that set well defined without making it reachable: 0040 gives the store
/// no listing and a digest does not reverse, so the removal in #114 needs the
/// core's own record of which keys it wrote, which is the bookkeeping 0040 hands
/// to #42.
///
/// Thread safety, from 0009: a plain value, safe from any thread.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeySpace<'a> {
    /// The server, as 0041 fixes it.
    pub server: ServerPart<'a>,
    /// The account, as the identifier the server gave back at sign-in.
    pub account: &'a str,
    /// The device identity from #36.
    pub device: &'a str,
}

Both issues are closed as completed:

gh issue view 36 --repo Flowfin/core --json number,state,closedAt,title --jq '"\(.number) \(.state) \(.closedAt) \(.title)"'
36 CLOSED 2026-08-27T23:06:22Z Establish device identity and the device profile
gh issue view 42 --repo Flowfin/core --json number,state,closedAt,title --jq '"\(.number) \(.state) \(.closedAt) \(.title)"'
42 CLOSED 2026-08-27T00:15:11Z Bound the cache and decide what is evicted

The device identity is a module, which names the number as provenance rather than
as an address:

git show c0e5b81^:src/session/device.rs | sed -n '1,8p'
//! The device identity, and who supplies each part of it.
//!
//! `docs/decisions/0036-the-device-identity-and-who-supplies-it.md` is the
//! record and #36 is the issue. It fixes three parts and gives the core the
//! smallest role in each: the client holds the identifier and the name, the core
//! owns the shape of the capability description and fills none of it in, and the
//! core generates an identifier only when a client asks for one and hands it
//! straight back rather than keeping a copy.

The record of written keys is a type:

git show c0e5b81^:src/cache/bound.rs | sed -n '416,421p'
/// The cache's own bookkeeping over a store a client supplied.
///
/// It holds no bytes. What it holds is the accounting 0040 pays for by giving
/// the store four operations and no listing: which keys are there, how long each
/// one is as the core counted it, which tier it is in, and in what order that
/// tier last used it.

and the same module states the bound the repaired paragraph now carries:

git show c0e5b81^:src/cache/bound.rs | sed -n '27,32p'
//! THE INDEX DOES NOT SURVIVE A RESTART, AND NOTHING HERE WRITES IT THROUGH THE
//! STORE. 0042 puts it under a reserved key inside the envelope 0105 defines,
//! written no more often than once every ten seconds and once more at stop, and
//! #105 and #115 are where both of those arrive. So the bounds in this tree are
//! enforced against what THIS run wrote, and entries an earlier run left in the
//! store are unaccounted for.

The dates, which are what separate the two states. The file landed at 16:45 UTC
on 2026-08-27:

git log --format='%h %cI %s' --diff-filter=A c0e5b81^ -- src/cache/key.rs
3261578 2026-08-27T18:45:04+02:00 Build a cache key out of five length-prefixed parts, and take the digest 0041 requires
git blame -L 114,114 --date=short c0e5b81^ -- src/cache/key.rs | cat
32615782 (Nils Lehnen 2026-08-27 114) /// rather than the username, and the device identity from #36. 0068 promises a
git blame -L 118,119 --date=short c0e5b81^ -- src/cache/key.rs | cat
32615782 (Nils Lehnen 2026-08-27 118) /// core's own record of which keys it wrote, which is the bookkeeping 0040 hands
32615782 (Nils Lehnen 2026-08-27 119) /// to #42.

#42 closed at 00:15 UTC that day and the module holding the bookkeeping landed
at 00:06 UTC, which the timestamp gives in the author's offset. Sixteen hours
before the sentence, so that half landed refuted:

git log --format='%h %cI %s' --diff-filter=A c0e5b81^ -- src/cache/bound.rs
19be153 2026-08-27T02:06:57+02:00 Bound the cache and evict the least recently used entry

#36 closed at 23:06 UTC and the module holding the device identity landed at
22:39 UTC, which the timestamp gives as the next day in the author's offset. Both
after the sentence, so that half went stale:

git log --format='%h %cI %s' --diff-filter=A c0e5b81^ -- src/session/device.rs
3b6280c 2026-08-28T00:39:36+02:00 Hold the device identity as three parts the client supplies, and refuse a fourth the core would invent

What the type says now:

git show HEAD:src/cache/key.rs | sed -n '111,141p'
/// The three parts of a key that do not change between two requests.
///
/// The server, the account as the identifier the server gave back at sign-in
/// rather than the username, and the device identity [`crate::session::device`]
/// holds. 0068 promises a caller that signing out removes every entry under one
/// of these, and 0041 makes that set well defined without making it reachable:
/// 0040 gives the store no listing and a digest does not reverse, so the removal
/// in #114 needs the core's own record of which keys it wrote. That record is
/// the bookkeeping inside [`crate::cache::bound::TieredCache`], and it covers
/// what THIS run wrote: the index does not survive a restart, which that module
/// states of itself.
///
/// BOTH OF THOSE WERE ADDRESSES ON THE TRACKER UNTIL THIS EDIT, AND THE TWO WENT
/// WRONG IN DIFFERENT WAYS. The record of written keys was handed to #42. That
/// issue closed as completed sixteen hours before this file landed and
/// [`crate::cache::bound`] was already in the tree, so that half landed refuted,
/// which is the harder of the two states to defend. The device part read `from
/// #36` while #36 was still open and [`crate::session::device`] did not exist, so
/// that half was the best available address when it was written and went stale
/// six hours later, which is the milder. #493 is where both were found.
///
/// Thread safety, from 0009: a plain value, safe from any thread.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeySpace<'a> {
    /// The server, as 0041 fixes it.
    pub server: ServerPart<'a>,
    /// The account, as the identifier the server gave back at sign-in.
    pub account: &'a str,
    /// The device identity, as [`crate::session::device`] holds it.
    pub device: &'a str,
}

What the change is:

git diff --stat origin/main...HEAD
 src/cache/key.rs | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)
git diff --name-only origin/main...HEAD
src/cache/key.rs

The two commands the gate runs, at this commit. The build was run first and
exited 0; its own summary line is not pasted, because this board's hygiene check
reads the word in it as a speed claim:

cargo build --locked --all-targets ; echo "exit=$?"
exit=0
cargo test --locked ; echo "exit=$?"
exit=0
cargo test --locked 2>&1 | grep -c '^test result: ok'
10
cargo test --locked 2>&1 | grep -c 'test result: FAILED'
0

The three links the paragraph adds resolve, which the ordinary build does not
judge:

cargo doc --no-deps --locked ; echo "exit=$?"
exit=0

Four of this board's own legs, run on this machine at this commit:

bash .github/invariants/invariants.sh check | tail -1
Every rule above was applied to its subject and refused nothing.
bash .github/format/format.sh check | tail -1
Every tracked source file above is written the way the formatter would write it.
bash .github/doc-paths/doc-paths.sh check | tail -1
Every path these documents name resolves against the tracked set.
bash .github/lint/lint.sh check | tail -1
Every lint the groups above carry was refused, outside the register printed with it.

What this does not cover

  • src/cache/bound.rs and src/session/device.rs. They are read as evidence
    and not edited. Both already say what holds.

  • Whether the persisted index should exist. src/cache/bound.rs states in
    its own words that it writes none and that #105 and #115 are where it
    arrives. Both are open and neither is touched.

  • #114. It is left standing in the paragraph. The removal it carries is
    open work, and naming an open issue for something that has not happened is not
    what this change is about.

  • docs/decisions/. 0036, 0040, 0041, 0042 and 0068 are referred
    to and none is edited. A record is superseded rather than edited in place,
    which docs/decisions/0001-decision-records.md fixes.

  • Every other reference to a closed issue in src/cache/. Only #36 and
    #42 at these sites are examined, and nothing is claimed about the rest in
    either direction.

  • A check that would refuse this. Nothing in this tree reads a doc comment's
    pointer against the state of the issue it names, and the document check reads
    only .md files. This adds no rule, so the next sentence of this shape is
    caught by a reader or not at all.

  • The shell analysis. It was not run on this machine, because the analyser is
    not on this machine's path:

      command -v shellcheck ; echo "exit=$?"
      exit=1
    

    What the run on this pull request reports is the reading, and nothing is
    claimed for it from here. This change touches no shell file.

Who has read it

Nobody other than the author. There is no second reader on this board tonight,
and the readings pasted above stand in place of one rather than a review having
happened.

…f written keys (#493)

`KeySpace` in `src/cache/key.rs` gave two of its own parts addresses on the
tracker. The device part of a key was "the device identity from #36", written
once in the paragraph and once on the field, and the core's record of which keys
it wrote was "the bookkeeping 0040 hands to #42". Both now name the module that
holds the thing: `crate::session::device` and the bookkeeping inside
`crate::cache::bound::TieredCache`.

The paragraph also gains the bound that record actually carries. `TieredCache`
accounts for what THIS run wrote, the index does not survive a restart, and
`src/cache/bound.rs` says so of itself. That is the half a reader of `KeySpace`
needs in order to know what 0068's promise over a sign-out is paid by, and it was
in a module they were not sent to.

The two halves went wrong in different ways, and the correction at the site says
which is which rather than writing them as one thing. `#42` closed as completed
at 00:15 UTC on 2026-08-27 and `src/cache/bound.rs` landed at 00:06 UTC the same
day; `src/cache/key.rs` landed at 16:45 UTC, sixteen hours after both, so that
half landed refuted. `#36` closed at 23:06 UTC and `src/session/device.rs` landed
at 22:39 UTC, both after the file, so `from #36` was the best available address
when it was written and went stale six hours later.

What that prevents is a reader leaving the checkout twice over one struct. The
other two parts of `KeySpace` are written the way the third was not: the server
part names the record 0041 and the account part says what it is in words. Only
the device part sent somebody to the tracker, for something that is a module in
the same crate.

Everything else in the paragraph is kept. 0068 still promises that signing out
removes every entry under one of these, 0041 still makes that set well defined
without making it reachable, 0040 still gives the store no listing, and a digest
still does not reverse. `#114` is left standing, because it is open and the
removal it carries has not happened.

It was found by reading every issue number this tree names outside
`docs/decisions/` against the state of that issue on the tracker.

Nothing else moves. No behaviour, signature, field, bound or event.

Closes #493

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit ab3338b into main Sep 18, 2026
28 checks passed
@iderex
iderex deleted the seam/cache-key-addresses-closed-36-42 branch September 18, 2026 11:00
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.

The cache key's device part and its record of written keys are given addresses on the tracker, and the two halves went wrong in different ways

1 participant