Skip to content

feat(rollup): keep ten minutes of transaction detail by default - #407

Merged
argszero merged 1 commit into
mainfrom
feat/ten-minute-detail-window
Oct 15, 2026
Merged

argszero merged 1 commit into
mainfrom
feat/ten-minute-detail-window

Conversation

@argszero

Copy link
Copy Markdown
Owner

What changed

Acceptance items 1 and 5 of the detail-retention rant (2026-10-15T16:25:04). Item 2/3 (the list reading the read model) landed in #406; this PR makes the window short, which is only safe because the list no longer depends on it.

The window defaults to ten minutes

[rollup] retain_minutes goes from 43200 (30 days) to 10, and config/config.example.toml follows. Nothing about the deletion changes: rows are still deleted only once they are folded and archived, so the folded part stays answerable as minute roll-ups and the originals stay in the JSONL archive. The default only decides how far back individual records are kept.

This is the host's ruling of 2026-10-15, quoted in the rant: "the details I keep are still too many — I think ten minutes of real detail is enough. There is no need at all to query individual records [...] when the page queries detail, just give the minute roll-up as the detail." (translated from the Chinese original)

The note names the boundary instant instead of counting days

The transactions note rounded the retention boundary to whole days. That was already lossy and a ten-minute window makes it visibly wrong — it would read "kept for the last 0 days". It now names the boundary instant, which is unit-free and needs no second calculation on the client (the server derives the boundary from the same expression the deletion uses).

tx.empty.archived changes accordingly in both languages.

tx_retention_gate R1 rewritten

Its premise was exactly what #406 removed ("the list can always only answer from the detail table"), so it now reads:

  1. the window must be expressible in minutes — a config that went back to spelling it in days turns this red, because day granularity cannot express ten minutes at all: below one day there is only 0, and 0 already means "keep no detail", which is a different rule;
  2. when the window is shallower than the deepest preset the list offers, the difference can only be carried by the list no longer reading the detail table (the criterion is that FROM transactions no longer appears in the list endpoint's body).

The rule is not relaxed to nothing. Putting the list back on the raw table makes it apply again — that is what the rule was always protecting: a page answering from the raw table answers less than the window allows, and nothing on screen would say so.

Mutation verification (acceptance item 5)

Both directions were run against the real corpus, and in each case only R1 goes red:

mutation reading
example config back to retain_days = 30 r1=false r2=true r3=true r4=true · 窗口 None 分钟
list endpoint back to FROM transactions r1=false r2=true r3=true r4=true · 窗口 Some(10) 分钟 · 列表读读模型 false

The second one is the mutation the acceptance item names ("put the list back to reading the raw table"), and the positive control the_scanner_sees_both_sides now also asserts the list payload is really being read, so a renamed handler cannot make the criterion silently false.

Verification

  • cargo test — 562 passed, 0 failed.
  • cargo fmt --check clean; cargo clippy --all-targets -- -D warnings rc=0; cargo build rc=0.
  • Live verification on dev (acceptance items 2 and 3: rows come from the read model, every row says how many records it stands for, no empty fields, and filter / sort / paging / CSV all work) needs a deployed build, so it follows with the release.

Note on existing deployments

A deployment that already set retain_days keeps that value — the legacy key is still accepted and takes precedence. The dev instance has retain_days = 3 set, so it would keep three days until that line is removed; the deploy step for this change removes it.

With the transactions page reading the read model (#406), the detail
window no longer decides how far back the page can answer — the folded
minute roll-ups carry that history. So the window can now be set to what
the record actually needs, and the host's ruling of 2026-10-15 is that no
one queries individual records at all: ten minutes.

`[rollup] retain_minutes` therefore defaults to 10 instead of 43200 (30
days), and the example config follows. The deletion itself is unchanged —
rows are still only deleted once they are folded *and* archived, so
nothing is lost: the folded part stays answerable as minute roll-ups and
the original rows stay in the JSONL archive.

The transactions note no longer converts the retention boundary into
days. It used to round the boundary to whole days, which was already
lossy and would now be wrong in a visible way — a ten-minute window
rounds to "the last 0 days". The sentence names the boundary instant
instead, which is unit-free and needs no second calculation on the
client (the server computes the boundary from the same expression the
deletion uses).

`tx_retention_gate` R1 is rewritten, because its premise was exactly
what #406 removed ("the list can always only answer from the detail
table"). It now has two halves:

- the window must be expressible in minutes — a config that has gone
  back to spelling it in days turns this red, because day granularity
  cannot express ten minutes at all: below one day there is only 0, and
  0 already means "keep no detail", a different rule;
- when the window is shallower than the deepest preset the list offers,
  the difference can only be carried by the list no longer reading the
  detail table at all — the ledger is that a page answering from the raw
  table would silently answer less, with nothing on screen saying so.

The rule is not relaxed to nothing: putting the list back on the raw
table makes it apply again. Both halves were verified by mutation on the
real corpus (config back to days => only R1 goes red; list back to the
raw table => only R1 goes red).

Tests: `cargo test` 562 passed, 0 failed. `cargo fmt --check` clean,
`cargo clippy --all-targets -- -D warnings` rc=0, `cargo build` rc=0.
@argszero
argszero merged commit d07c2ef into main Oct 15, 2026
2 checks passed
@argszero
argszero deleted the feat/ten-minute-detail-window branch October 15, 2026 10:46
argszero added a commit that referenced this pull request Oct 15, 2026
…409)

#406 moved the transactions page onto the `tx_facts` read model, which
retired the premise "the list needs per-row identity (`id`/`counterpart`),
the roll-up has none, so the list can always only be answered from the
detail table". #407's own commit message says its premise "was exactly what
#406 removed", and `ui/README.md` calls it the premise that disappeared
this round - but the two module docs that state it were never updated, so
both still carry it in the present tense.

- `src/tx_retention_gate.rs:6-7` opens the rule list with it, so the file
  argues from a premise that its own bullet, ten lines later, says is gone.
- `src/usage_retention_gate.rs:4-5` reasons across files from it, and names
  the rule wrong as well (that rule was rewritten in #407).

Both sentences now say what holds today: the window must be expressible in
minutes, and the history outside it is carried by the minute roll-ups.

Comments only - no behaviour, no gate rule, no config changes.
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