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
/cache/ without a fragment is now a login form: petname
(autocomplete=username) and cache id (type=password,
autocomplete=current-password), the shape password managers fill from
the entries the save button makes. Its inputs have no name, so a native
submit (script failed) sends nothing; the key stays in memory, out of
the address bar and history. Home links to it.
Generated-by: Claude Opus 5.5
Past the transitional idiom: the cache logic (DiigoCacheLogic,
PublicCacheLogic in cache.ts) requires its Clock, with no default, so
cache.ts reaches no ambient time. The worker's entry module is the
edge: it makes the live clock and exports the runtime-constructed
DiigoCache/PublicCache as (ctx, env) subclasses that supply it (the
Cloudflare constraint: the runtime constructs DOs with ctx and env
only). Binding class names are unchanged, so stored state is too. The
tests' FixedClockCache extends the logic class.
Generated-by: Claude Opus 5.5
Ocap discipline: no ambient time in logic. PagedCache reads time and
waits only through a Clock { now, sleep }, a default constructor
argument (the live clock; the runtime constructs DOs with ctx and env
only). The fill job's timestamps and alarms, the etch time, and the
diigo-call pacing all go through it.
Tests interpose through the constructor: tests/test-worker.ts (now the
tests' main module) adds FixedClockCache, bound as CLOCKED_DO, and a
test pins startedAt, updatedAt, and the alarm time to the fixed clock
(an ambient new Date() fails it).
Generated-by: Claude Opus 5.5
Starting a fill no longer POSTs the cache id to /_fill. Instead:
- the created page links the cache's webkey, /cache/#<cache id>: a
skeleton page, the same for every key, that reads the key from the
fragment (never sent to the server or in a Referer)
- its script trades the key in-band for a capability over Cap'n Web at
/api: CacheApi.cache(key) returns a CacheCap facet (fill, status,
fingerprint, the public companion's fill) and never the DO's wiring
methods; malformed and unknown keys get one refusal that never echoes
the key, and each refusal is logged without it
- then calls startFill(): trade and call pipeline into one round trip
- the worker serves capnweb's browser build itself at /_capnweb.js (a
wrangler Text rule), so no third-party script sits beside the key
The /_fill route and its forms are gone. Tests run the real capnweb
client against the worker (fetch routed to worker.fetch).
Generated-by: Claude Opus 5.5
POST /_fill (cache id in the body; target private|public; action
start|stop) starts or stops a background walk of every diigo page into
the cache. DO alarms advance it pagesPerStep pages per step (default
20), a second apart; progress (FillStatus) is stored in the DO after
each page, so it survives eviction. Cached pages are skipped; starting
while running rejoins the job; a stop mid-step wins; a failure is
recorded with its reason and logged once. The created page gets fill
buttons; the public page shows "filling: n of m pages".
Fill state is new, additive DO storage (fill, fillStep): no existing
state changes meaning.
The 13 fill specs are unskipped and green, plus one: a fill makes one
diigo call per page fetched.
Generated-by: Claude Opus 5.5
A button that grabs every page into a cache: owner-only POST /_fill
(cache id in the body; target private|public; action start|stop),
advanced by DO alarms a few pages per step, progress stored in the DO
(new, additive state) so it survives eviction, cached pages skipped,
a rerun rejoins the running job, failures recorded and logged once.
The created page gets the button; the public page shows progress.
startFill/fillStatus/stopFill on PagedCache are JSDoc'd stubs; 13
specs are describe.skip until it lands (alarms driven by
runDurableObjectAlarm, eviction by evictDurableObject).
Generated-by: Claude Opus 5.5