Skip to content

feat(ledger, around): build both pages at deploy, fetch their live rows as islands - #928

Merged
oddharsh merged 4 commits into
mainfrom
claude/ledger-around-islands
Sep 25, 2026
Merged

oddharsh merged 4 commits into
mainfrom
claude/ledger-around-islands

Conversation

@oddharsh

Copy link
Copy Markdown
Owner

What changes

/ledger and /around stop rendering per request. build.ts step 5b bakes both shells, in the lib/island.ts shape /whoareyou (#924) and /garage/dyno (#926) took:

  • /ledger: the invoice paper, the terms and the CSS are baked. The line items, the total and the cost line arrive from /ledger/lines.html.
  • /around: the lede and the footer are baked. The crawl's meta line and its 20 rows arrive from /around/snapshot.html.

Both islands are the same for every visitor, so each sits behind cachedRender at the 5-minute edge TTL the page used to take, rather than islandResponse's no-store default.

What it buys

Measured against production and a local build of the Worker. Fragment sizes are the edge's q4 brotli, which is what a Worker response ships at.

today shell (q11) fragment first visit return visit
/ledger 5,539 B 4,502 B ~710 B 5,212 B (−6%) ~710 B (−87%)
/around 5,967 B 4,022 B ~1,747 B 5,769 B (−3%) ~1,747 B (−71%)

A return visit revalidates the shell to a 304, or takes a dcz delta after a deploy (2,631 B and 2,129 B), and pays for the fragment only. These come out ahead on a first visit where dyno came out even, because their live halves are small. Dyno's chart was about half of its page.

The placeholders

Each placeholder is its renderer's own frame with the live row count, and it names nothing:

  • /ledger: one unread line per crawler the table can name. That's 16, which is what production's trailing window shows today.
  • /around: one unnamed row per neighbour. The crawl always reports all 20.

Contract tests compare each placeholder against a live render sized independently: from CRAWLERS and from NEIGHBORS. Both tests go red when their placeholder loses a row. My first ledger test sized its "live" read from the mount itself, so it could only agree with itself; its control passed when it should have failed, and that is what caught it.

bun run cls reads both pages clean on desktop and phone, with the control seen. /around was measured against production's real snapshot seeded into local KV.

One known gap: when the ledger has fewer crawlers than 16 in its window, or no read token, the island is shorter than the placeholder. A local Worker has no token, so there it collapses to a one-line note. The shift happens inside the replaced island, and the lab read it clean anyway.

Also

  • The owner's /around?bust=SECRET still works. A built page can't take a query, so the bust moved into refreshAroundSnapshot. It re-crawls, then overwrites the colo's cached snapshot, awaited, before the shell goes out. The island the shell then fetches is the new crawl, and not whatever KV's cacheTtl still holds. A wrong or missing secret does nothing.
  • Copy fix: /around said "Refreshed: every 30 min by cron". The crawl has run daily at 05:41 UTC since 2026-08-14. It now says "once a day", and the route-oracle comment that said */30 agrees.
  • Bug fix: Accept: text/markdown on /around now serves the hand twin. Same exact-route gap as dyno's.
  • Config: "/around*" folds three exact run_worker_first rows and covers the fragment. Dev gets the "/ledger/*" glob production already had, which clears the build's standing warning about the two allowlists disagreeing.

Verification

  • bun run build, bun run lint, bun run typecheck: clean, including after merging feat(garage): /garage/dictionary, the dictionary the runtime dropped #927.
  • bun run test: 923 pass. bun run test:node: 913 pass, 10 skipped (the HTMLRewriter-only tests), 0 fail.
  • bun run routes:check: 183 routes, 0 failures. New rows cover each island mount and fragment, plus /around's Markdown negotiation.
  • Browser, against the built Worker: both islands go live with one fetch each (initiator link, so the preload was used), no console errors, and each failure note shows only under data-state="failed".

Not in this PR

/serendipity, which is now the one large page still on the loose script policy, since #925 covered everything that goes through lunaPage.

…ws as islands

/ledger and /around rendered per request, so each shipped at the edge's
q4 brotli with no dcz delta and no ETag, although most of each page is
fixed prose and CSS. build.ts step 5b now bakes both shells, in the
lib/island.ts shape /whoareyou and /garage/dyno took. /ledger's line
items, total and cost line arrive from /ledger/lines.html, and
/around's crawl rows from /around/snapshot.html. Both islands are the
same for every visitor, so each sits behind cachedRender at the
page's old 5-minute edge TTL.

Measured against production and the built Worker:
- /ledger: 5,539 B today; shell 4,502 B at q11 plus a ~710 B fragment.
  First visit -6%, a return visit pays only the fragment.
- /around: 5,967 B today; shell 4,022 B plus a ~1,747 B fragment.
  First visit -3%, return visit likewise the fragment alone.

Each placeholder is its renderer's own frame with the live row count:
one unnamed row per crawler the ledger can name (production shows all
16), and one per neighbour (always 20). Contract tests pin both counts
against independently sized reads, and each goes red when its
placeholder loses a row.

The owner's /around?bust=SECRET still works: it re-crawls and
overwrites the colo's cached snapshot before the shell goes out.

Also:
- /around said "Refreshed: every 30 min by cron"; the crawl has been
  daily at 05:41 UTC since 2026-08-14. It says once a day now.
- Accept: text/markdown on /around now gets the hand twin.
- "/around*" folds three exact run_worker_first rows, and dev gets the
  "/ledger/*" glob production already had, which clears the build's
  standing allowlist-mismatch warning.
@github-actions github-actions Bot added area: content Authored documents and prose area: docs Runbooks and repository prose area: infra Declared Cloudflare state, wrangler config, pins area: tools Developer tooling and the contract suite area: worker The site Worker: routing, rendering, bindings release-path Touches what decides which commit reaches production type: feat New capability or surface labels Sep 25, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
aadhar-sh 05bcffc Sep 25 2026, 07:05 PM

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Wire-size diff vs merge base

d16775168e817bc96940fc8726eb4decc499de05 → ca973056890e50f083feb5d06ca8c6299ff09451

Brotli q11, the quality the edge serves. Every number here is deterministic:
an unchanged file produces no row. Advisory — this check fails on nothing.

Client assets

No change. 27 files, 91.60 KiB Brotli total.

Pages

Total Brotli: 613.89 KiB → 622.22 KiB (+8.33 KiB, +1.4%) across 63 files, 2 changed.

file base head Δ
ledger.html — 4.41 KiB new
around.html — 3.92 KiB new

Worker bundle

258.02 KiB → 258.75 KiB gzip (+0.73 KiB, +0.3%)

Largest module deltas (raw bytes in bundle):

file base head Δ
.build/src/worker/ledger.ts 11.84 KiB 12.87 KiB +1.03 KiB (+8.7%)
.build/src/worker/around.ts 15.20 KiB 15.91 KiB +0.70 KiB (+4.6%)
.build/src/worker/index.ts 13.17 KiB 13.77 KiB +0.60 KiB (+4.6%)
.build/src/worker/lib/csp-hashes.ts 7.01 KiB 7.22 KiB +0.21 KiB (+3.0%)

Dictionary transport

Family dictionary acquisition: no change, 14.41 KiB q11.

pd/: 173 → 175 deltas, 510.99 KiB → 515.64 KiB (+4.65 KiB).

cachedRender stores the fragment inside waitUntil after hashing a weak
ETag. The test waited one setTimeout(0) and then counted puts, which
passed under bun and lost the race on CI's node, symlink leg. It now
collects the waitUntil promises and awaits them.
@oddharsh
oddharsh enabled auto-merge (squash) September 25, 2026 18:56
…islands

# Conflicts:
#	CLAUDE.md
#	tools/build.ts
@oddharsh
oddharsh merged commit 5801a81 into main Sep 25, 2026
14 checks passed
@oddharsh
oddharsh deleted the claude/ledger-around-islands branch September 25, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: content Authored documents and prose area: docs Runbooks and repository prose area: infra Declared Cloudflare state, wrangler config, pins area: tools Developer tooling and the contract suite area: worker The site Worker: routing, rendering, bindings release-path Touches what decides which commit reaches production type: feat New capability or surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant