/public/<username> takes days, after, before, in (year, month,
quarter, ISO week), by=updated|created, and q (words, "phrases",
#tags, -negation, gmail/Slack/GitHub date operators). JSON answers
echo the parsed filter; a malformed one is a 400.
Filters query the stored items in sqlite through the read-only
viewer instead of walking diigo pages: the public page keeps no
fetch authority, and a filled cache already holds every item.
Results say `complete` when every diigo page is stored; freshness
is 1f's head refresh. So the fetch budget and the O(log pages)
walk test go, and the fake deep account shrinks to 300.
Generated-by: Claude Opus 5.5
Docs per the docs-files-api skill: a 1–2 line @file; what a declaration is or does in JSDoc on it, not in // comments or the file header.
Generated-by: Claude Opus 5.5
Comparing a cache with diigo's RSS export: the size-100 walk had missed
exactly the 100 newest bookmarks (everything updated since 2026-09-16).
diigo's page_num is 0-based, and we sent our 1-based page number
unchanged, so "page 1" was really page 2 and the newest page was never
fetched. diigo's total (16744) was right; the "total-overstated"
reading was a misdiagnosis.
- page() asks for page_num = n - 1
- BREAKING for stored state, agreed beforehand, migrated in place:
schema v2 renumbers every stored page +1 (pages and page_members, via
negation so no number collides), once, recorded as kv "schema" = 2;
nothing is refetched, page 1 just becomes a miss
- fill jobs record their numbering; a job saved without it fails ("page
numbering changed …; start the fill again") instead of resuming
- the fake pages from 0 like diigo (7 tests failed until the fix);
tests feed v1-shaped storage and a v1-numbered job to the new code
- the next-page peek guard stays; its comments no longer claim diigo
overstates its total
- the fixed test clock moves to 2099: pinned to "today" its alarms came
due in real time and fired on their own
Generated-by: Claude Opus 5.5
Seen live: "filled: 168 of 1 pages". diigo's total overstated by 100
(16744 vs 16644), so the walk went on to page 168, which diigo answered
empty with a total ≤ 100; the fill recomputed its page count from that,
ceil(total/100) = 1.
Now a page whose own total says the data ended before it ends the walk,
with the count set to the last page with items ("167 of 167"). Judged
by total, not by items, since the public cache may keep none of a page
that isn't the end. The known-pages estimate ignores zero totals. The
fake answers past-the-end pages as diigo does (no items, total 0).
Generated-by: Claude Opus 5.5
The real fill failed at "diigo returned 44 items for page 167 of 168":
diigo's `total` overstates what load_user_items returns, so the true
last page looked short and the guard took it for a capped page size.
A short page that isn't the last by `total` now fetches the next page
(paced like any diigo call): empty means the data really ends there —
accepted, with a "total-overstated" notice logging total vs actual;
anything else means items would be skipped — refused, the message now
giving both counts. The fake gains an account whose total claims 60
more than it has; a fill on it runs to done.
Generated-by: Claude Opus 5.5
Asked for 128, diigo returned 100 (the short-page guard caught it on the
first real fill), so PAGE_SIZE = 100: ~168 pages for 16.7k bookmarks.
The same run showed a fill job outliving a page-size change: progress
counted in 24s ("page 25 of 698") resumed as if counted in 128s. Stored
state reinterpreted, so: FillStatus records pageSize (additive; jobs
saved without it counted in 24), and a job whose size differs from
PAGE_SIZE fails with "page size changed …; start the fill again"
instead of resuming; starting again replaces it. Tested with the old
shape in storage.
Generated-by: Claude Opus 5.5
PAGE_SIZE = 128 (was 24), one constant for the fill job, public pages,
dumps, and DiigoBridge, so a cache never mixes sizes: ~131 pages instead
of ~698 for 16.7k bookmarks.
Cache invalidation, agreed beforehand: pages are cached by (page number,
size), so pages cached at 24 become misses and refetch at 128. Items are
keyed by link_id and just re-upsert; nothing is lost.
Unknown whether diigo honors count=128, so a short page that isn't the
last is refused loudly (logged, thrown; a fill fails with the reason)
rather than silently skipping items. The fake gains a capped account to
test it; the plain account grows to 300 (3 pages), the deep one to 6000.
Generated-by: Claude Opus 5.5
The public page's filters, toward the weekly PESOS roundup (#23):
?days=N (7/30/90/365 presets), gmail-style after/before (UTC days,
half-open: after inclusive, before exclusive; yyyy/mm/dd accepted), and
q (words, "phrases", #tags exact against diigo's comma-separated tags,
- negation); q alone searches the last 365 days and says so; malformed
filters are a 400. Filtering walks diigo's updated-desc pages from 1 and
stops at the window, so ?days=7 costs one page.
src/query.ts is the typed, JSDoc'd API (stubs that throw); 22 specs are
describe.skip until it lands. The fake webapp gains a dated account.
Generated-by: Claude Opus 5.5
A hit must replay diigo's order as fetched (page_members.pos), not a
column sort. The fake webapp's link_id and updated_at are scrambled so
neither column sorts back into diigo's order; ordering the replay by
updated or item_id fails the new test.
Generated-by: Claude Opus 5.5
The dump worker becomes a page cache: a hit is served from DO SQLite
storage with no diigo call; a miss asks the webapp instance in the
cache's wiring and etches the page.
- two independent caches, one behavior (PagedCache): DiigoCache keeps
every item; PublicCache keeps shared items only, dropping private
rows before they are stored; served at /public/<username> (HTML page
form, or JSON)
- create flow on one port: /_connect relays webapp's mint form, /_mint
proxies the mint, /_wire wires a private or public cache; /_publish
adds a public cache later
- ids are webkeys: kept out of URLs (POST bodies), shown and logged
only as a hash fingerprint; saved to the password manager under the
holder's petname (never sent to the server)
- items keyed on diigo's link_id; username from u_name; unkeyable
items and every failure path are logged; each page read logs hit/miss
- tests run the real worker + DOs in the vitest pool against a fake
webapp (tests/fake-webapp.js)
Generated-by: opencode (GLM)
Generated-by: Claude Opus 5.5