Skip to content

feat(app): a Repeat reads switch in the main window and a Repeat reads card on the Optimizer tab - #59

Merged
HackPoint merged 17 commits into
mainfrom
agents/t4-repeat-reads-toggle-card
Oct 13, 2026
Merged

HackPoint merged 17 commits into
mainfrom
agents/t4-repeat-reads-toggle-card

Conversation

@HackPoint

@HackPoint HackPoint commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

What

Repeat reads are on by default in 1.8.0, and until now the only way to turn them off was an environment variable or a hand-edited JSON file, with nothing in the app showing what they had done. This adds a switch to the main window and a card to the Optimizer tab.

Nothing the MCP server sends changes (no tool output, tools/list or hook), and nothing is added to the hook path. In lumen-core only the unusable-settings-file message changes: it now names the switch.

The switch

  • On the Context tab of the main window, on by default. It writes lumen_settings.json (atomically, readable by its owner only, the other keys of a valid file kept). The server reads that file on every tool call, so a change applies to the next Lumen read in every running Claude Code session, with no restart, and survives a restart. The window says so.
  • It shows the settings file, not the server. It cannot see a LUMEN_REPEAT_READS in the MCP config Claude Code gives the server (which outranks the file), nor a LUMEN_DB there that points the server at another ledger. Its text says both.
  • A file that cannot be used is shown as off, with the reason: the same off the server applies. Switching on rewrites it. While a write is in flight the switch stays focusable (marked aria-disabled) and ignores a second flip, so a keyboard user does not lose their place. A setting that cannot be read shows neither state and offers Retry. A change that cannot be saved puts the switch back and says why.
  • lumen doctor and the unusable-file message name the switch as the repair.

The card

On the Optimizer tab, below every total: unchanged answers served, edits served as a delta, tokens not re-sent, and how often the full result was asked for again with fresh=true (n of m, labelled as re-requests). It is counted apart from every total (token totals, by-tool and by-channel breakdowns, net value) and shows no dollar figure and no percentage. The token figure covers only answers whose count was measured: the card says how many when that is fewer than all, and shows a dash when none. It says when the settings file turns repeat reads off, and counts the reads answered in full because Lumen could not tell the conversation, with a link to Setup. An empty ledger says "No repeat read has been answered short yet." with no figure. A backend that does not send the figures gets no card.

lumen-stats: the optimizer report gains repeatReads; no schema change. Tauri: two commands read and write the setting through lumen_core::settings.

Styles

The card shares the Optimizer page's existing card, title, row-list, figure and note rules by adding its selectors to them, instead of repeating their declarations. With its own copies the page's stylesheet was 8.80 kB, over the 8 kB build budget; it is 7.83 kB now (main: 7.13 kB, limit 8 kB). home.css is at 7.93 kB of 8 kB: the next rule on either page has to share, or the budget has to move. The existing blocks render as before; the card takes their values (rows 0.5rem apart, figures 0.84rem). The template has no class without a rule; a guard in lumen-core enforces it.

One wording decision, yours to change

The brief says "tokens saved". The card says "tokens not re-sent", counted against reading the whole file again: with repeat reads off a read returns the outline, usually far smaller, so a saving against the whole file would overstate what repeat reads add. The card and the README name the baseline.

Tests

  • Frontend, each state driven by a fixture and asserted on the rendered text: 19 for the switch (every file state, failed read and Retry, failed write, switching on over a bad file, the switch held still with the focus kept while a write is in flight, nothing claimed before the first read), 25 for the card (none yet, off, each figure and its baseline, partial and no measurement, "0 of m", "1 delta" not "1 deltas", the unplaced-reads line and Setup link, no card without the figures), 22 for the service (read and write ordering, errors cleared, answers that are not a setting).
  • Rust: 10 in lumen-stats for the new aggregate, one of which seeds all four served routes and checks that none reaches any of the six totals; 17 for the Tauri commands (every file state, a failed write names the file and the operating system's reason, the variable not read, every command the window invokes is registered, and the argument keys the window sends are the parameters each handler takes); 1 in doctor; the settings message test updated; in lumen-core one test that a reader holding the file open still sees the whole old file after a write.

Gate

Local gate green on 31772f8 (446 s): fmt, clippy -D warnings (workspace and Tauri crate), workspace tests with RUSTFLAGS=-D warnings, frontend build under the style budgets and tests with the coverage thresholds, e2e, Tauri crate and hook e2e tests, MSRV 1.94.0, install verify, debug app build. Run with LUMEN_RANKED_TIME_BUDGET_MS=60000: the lumen-core ranked-outline budget test is wall-clock bound and flakes on a loaded machine; nothing here touches it.

Each new test was proved to fail without its fix: the settings write done in place, the handler removed from the list, the operating system's reason dropped from the message, the switch disabled while writing, the plural, the argument sent as { enabled: on }, the handler's parameter renamed, and any one of the four served routes added to any of the six route lists in lumen-stats (24 changes; the test before this seeded two routes and passed 12 of them).

Not run locally: the tauri-driver app e2e, which runs on CI Linux (GUI build + test). No e2e touches the switch, and the argument test compares the two sources rather than running the Tauri runtime.

CRLF clone (core.autocrlf=true): the lumen-core, lumen-stats and lumen-cli tests and the new Tauri tests, none failed.

HackPoint and others added 17 commits October 12, 2026 12:06
…art from the savings totals

Served answers (unchanged and delta, from smart_read and recall_file) are not in LUMEN_ROUTES, so the
token totals, by-tool, by-channel and net value never included them and still do not. The report now
carries their own counts under repeatReads: unchanged and delta answers, tokens not re-sent against
reading the whole file again (measured rows only), fresh=true requests, and reads that could not be
tied to a conversation.
…in the settings file

get_repeat_reads_setting reports what the file and the default say, never the server's effective mode,
since a LUMEN_REPEAT_READS value in a Claude Code MCP config outranks the file and the app cannot see
it. set_repeat_reads_setting writes the choice and returns the file read back, so a write that did not
take is visible instead of assumed. A corrupt file reads as off with the reason, and switching on
rewrites it.
…aved in the settings file

The switch shows the settings file and the default, and says so: a LUMEN_REPEAT_READS value in an MCP
config takes priority and is not visible here. A file that cannot be read shows why, that repeat reads
are off, and that switching on rewrites it. A failed read claims neither state and offers Retry; a failed
write puts the switch back and shows the error. Reads and writes run one after another so a read never
overtakes the write before it.
… switch, which now exists

The writer's doc comment said nothing in the app calls it, and the file-problem message
and its test said there was no switch to go back to. The Home window has one now, and
choosing a setting there rewrites a file that cannot be used. A bad environment value is
still not something the switch can fix, so that message keeps not mentioning it.
…ext tab of the main window

The docs and the tab bar say "main window" and "Context"; "Home" is only the component's
name, so a message sending someone to the Home window sent them to nothing they can see.
…from every total

A card after the not-optimized block shows the answers served short (unchanged notes,
deltas), the tokens not re-sent counted against reading the whole file again, how many
full results were asked for again, and the reads Lumen could not place in a conversation.
It states that these are in none of the token totals, the by-tool and by-channel
breakdowns or the net value, and it says when the settings file turns repeat reads off.
It never words a figure as a saving and shows no dollars or percentage; a backend older
than 1.8.0 sends no figures and then there is no card.
… the card's baseline

README: the switch as a third way to turn repeat reads off, what it can and cannot show
(the settings file and the default, never the environment), and the Repeat reads card: what
each figure counts, that its tokens are counted against the whole file and so are not a
saving over having repeat reads off, and that its answers are in none of the totals while the
provenance counts still include their rows.

CHANGELOG: one entry under Unreleased.
…s, so its styles stay under the build budget

With the card's own rules optimizer.css was 8.80 kB, over the 8 kB component-style
error budget, and `pnpm build` failed. The card now shares the breakdown card, title,
rows, value and amber-note rules and inherits its text style from the card: the file is
7.83 kB. The existing rules render as before; in the card, rows are 0.5rem apart and
names are 0.74rem.
frontend_styling requires every class a template uses to be styled in its component.
The card's paragraphs, names and lead now take their text style from the card instead
of from classes of their own, so the template no longer names them; the one modifier
left is repeat-card__problem. No spec, e2e or other file used those classes.
The switch is no longer disabled during a write, which dropped the focus it
held; it is marked aria-disabled and a second click is ignored until the first
write settles. The Optimizer card says "1 delta", not "1 deltas".

New tests, each shown to fail without its fix: a reader holding the settings
file open still sees the whole old one after a write (the rename is atomic);
every command the window invokes is registered in the Tauri handler list; a
failed write names the file and the operating-system cause.
The window sends set_repeat_reads_setting its argument as { on } and the handler takes `on`, and nothing compared the two: renaming either end compiled and passed every test. A test now reads the argument keys from tauri-bridge.ts and the parameters from the handlers in lib.rs and requires them to match, for both commands, and a second test pins the scan itself.

The test that served answers stay out of every total seeded two of the four served routes, so adding smart_read_delta or recall_file_unchanged to any of the six route lists in lumen-stats passed it. It now seeds all four.

Each is proved to fail without it: the old test passed under all 12 additions of the two unseeded routes and the new one fails under all 24 single additions; the new pair fails with the key sent as { enabled: on }, with a key sent to the get command, with the handler parameter renamed, and with the scan keeping the spaces around a key.
@HackPoint
HackPoint merged commit b8c7c0c into main Oct 13, 2026
17 checks passed
@HackPoint
HackPoint deleted the agents/t4-repeat-reads-toggle-card branch October 13, 2026 08:32
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