Skip to content

Say how much Unbent has stored from the files themselves, not Chrome's count of them - #490

Merged
cdeutsch merged 5 commits into
mainfrom
claude/offline-usage-own-bytes
Nov 12, 2026
Merged

cdeutsch merged 5 commits into
mainfrom
claude/offline-usage-own-bytes

Conversation

@cdeutsch

@cdeutsch cdeutsch commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Settings → Offline told two desktop installs "Unbent is using 3.98 GB" when each held about 31 MB. The figure came from navigator.storage.estimate(), and Chrome's count of the origin-private file system was wrong by about 4 GB.

Why Chrome's figure was wrong

Chrome keeps a running counter of each site's file-system usage (File System/<n>/t/.usage in the profile). On both machines it had gone negative (minus 82.4 MB on one, minus 51.6 MB on the other). The reported usage was that counter wrapped round 2^32, plus the real files: 4,295 - 82 + 33 = 4,246 MB, exactly what DevTools showed. A Chrome restart kept it. Deleting the counter file with Chrome closed made Chrome measure the folder again (30.9 MB), so no data was ever wrong.

Reproduced since in a clean Chrome profile (stable and Dev): bytes written through a FileSystemSyncAccessHandle are counted in memory but never saved to that counter, so after a restart Chrome has forgotten them, and deleting the files then subtracts bytes that were never added. Write 1 MiB through a sync handle, restart, delete it, restart: Chrome reports 4,095 MB. The same bytes written with createWritable stay exact. wa-sqlite writes every database file through sync handles, and the sign-out wipe deletes the library and search index, so any Chrome that has run Unbent across a restart and a sign-out can land here. That is what this change is for. It is Chromium issue 436552046 (assigned, with the same diagnosis and a repro), first seen as 401692415.

What changes

  • storedBytes (store/usage.ts) takes the browser's estimate and swaps its usageDetails.fileSystem part for the sizes of the files actually there. Only that part was wrong; the caches and IndexedDB figures matched. Where the browser reports no parts (Firefox, Safari) it says the estimate as before.
  • A walk that meets a file SQLite holds at that moment answers null, and the page keeps its last measured figure, rather than flicking back to the miscount. The walk runs beside the page's other figures, not before them, and only the newest walk's answer is used.
  • The sentence is now "Unbent is using 31 MB, and this device has room for 9.88 GB more." The old "of the 13.98 GB this device allows it" was Chrome's quota, which Chrome reports as its usage plus 10 GiB, so it still carried the miscount, and the panel could say 57 MB of 13.98 GB beside "out of room". The room left is now the same free that Keep offline checks a notebook against, so the two can't disagree.
  • Fitting is unchanged. Whether a notebook can be kept, or a file can wait to upload, is still judged against the browser's estimate, since that is what the browser holds the site to.

Checked

  • usage.test.ts: the swap (4,245 MB counted, 31 MB on disk), no parts reported, a file held open, no estimate at all. OfflineStorage.test.tsx: the sentence with a measured figure and with only the estimate.
  • Web suite (2,739 tests) and both typechecks pass after rebasing on main.
  • In the browser, Settings → Offline reads "Unbent is using 361 KB, and this device has room for 95.04 GB more." for the demo account, and the sum of the files measured in the same page agrees.
  • The walk was run in Chrome beside a window holding the library open, and sized every file, the library included.
  • One review round: no blockers. One suggestion (that the walk might never succeed while the library is open) was declined on the evidence above; the other two and a nit were fixed in their own commits.

To reset an affected Chrome by hand: quit it, delete the negative .usage file under File System/<n>/t/ in the profile, and reopen.

🤖 Generated with Claude Code

cdeutsch and others added 5 commits November 12, 2026 11:54
…s count of them.

Settings → Offline told a desktop install that "Unbent is using 3.98 GB" when the site held about 34 MB on disk. The figure was `navigator.storage.estimate()`, and Chrome put 4,245 MB under its `fileSystem` part while the files in the origin-private file system (the library, its search index, SQLite's scratch directories) came to 31 MB. The counter Chrome keeps for that file system read minus 82 MB. The caches and IndexedDB parts of the estimate were right.

`storedBytes` (`store/usage.ts`) takes the estimate, swaps its `fileSystem` part for the sizes of the files actually there, and keeps the rest. Where the browser gives no parts (Firefox, Safari), or a file cannot be sized because SQLite holds it at that moment, it says the estimate as before, rather than a total that leaves the library out.

Only the sentence changes. Whether a kept notebook fits, or a file can wait to upload, is still judged against the browser's own estimate, because that is the figure the browser holds the site to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Offline.

The walk that sizes the stored files was awaited before the other figures on the page, and runs again whenever the outbox moves. With a notebook kept, that is thousands of files sized one after another, and the waiting uploads and the persistence line waited behind it each time. A slow walk could also land after a newer one and put back an older figure.

The walk now runs beside the other figures, and only the newest one's answer is used. A walk that meets a file SQLite holds open at that moment now answers null rather than the browser's estimate, and the page keeps the last figure it measured: falling back to the estimate there would flick the sentence back to the very miscount it exists to replace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…owser's allowance.

The sentence read "Unbent is using 57 MB of the 13.98 GB this device allows it" once the stored figure was measured. That allowance is the browser's quota, which Chrome reports as its own usage plus 10 GiB, so it still carried the gigabytes of the miscount. Worse, Keep offline checks a notebook against the browser's room, so the same panel could say 57 MB of 13.98 GB and then that the device was out of room.

It now says the room left as the browser judges it ("and this device has room for 9.88 GB more"), which is the same `free` a kept notebook is checked against, so the two lines can no longer disagree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`fileSystemBytes` was exported, but only `storedBytes` in the same module calls it, and an export reads as an API someone else relies on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comment on `storedBytes` said Chrome's count could be wrong by gigabytes, but not how. The counter had gone below zero (minus 82 MB) and was reported wrapped round 2^32, plus the real files: 4,295 - 82 + 33 = 4,246 MB, which is exactly what DevTools showed. A Chrome restart kept it; deleting the counter file with Chrome closed made Chrome measure the folder again, at 31 MB. Written down so the next reader knows the measured figure is guarding against a counter that underflows, not one that drifts upward.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the web label Nov 12, 2026
@github-actions

Copy link
Copy Markdown

Preview: https://unbent-pr-490.of2668.workers.dev

Sign in with Continue on the dev instance: a preview cannot register
its own OAuth callback (its host changes every PR), so it borrows a Google
session from https://dev.unbent.app and comes back signed in. A password with an
allowlisted address still works too.

Runs against the shared unbent-dev database, bucket and note stores (the same
notes as the dev instance), so what you change here changes there too. Production
is untouched. The Worker is deleted when this PR closes.

Worker unbent-pr-490 · deployed c383617

@cdeutsch
cdeutsch merged commit 387a99d into main Nov 12, 2026
12 checks passed
@cdeutsch
cdeutsch deleted the claude/offline-usage-own-bytes branch November 12, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant