fix: treat published prerendered HTML as first-class (co-equal priority + publish awaits it)#5517
Conversation
For a published realm the rendered HTML is the deliverable served to visitors — a realm that is indexed but not prerendered serves a blank shell — so user-initiated prerendered HTML is as first-class as the search index, not an off-critical-path follow-on. Raise userInitiatedPrerenderHtml from 9 to 10, co-equal with user indexing; system-initiated prerender-html stays background (0). With prerender-html at 10 the high-priority worker pool (which floors at that tier) and the user-index pool both floor at 10 and serve all user-initiated work — indexing and rendering alike. What keeps each moving is worker capacity, not a dedicated lane; the split across the two count knobs is now immaterial. Comments on both the worker-manager tiers and the matrix harness updated to the co-equal model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A published realm's rendered HTML is its deliverable, but prerender-html runs on a separate fire-and-forget channel after the index pass, so a realm can report ready (indexed) while still serving a shell without card markup — the source of the host-mode / head-tags publish flakes. Make publishing await the HTML. `_readiness-check` gains an opt-in `awaitPrerenderHtml` param that, after the index await, also waits for the realm's published HTML to be live for its current generation (`awaitPublishedHtmlReady`: polls `prerendered_html` for an instance row with isolated_html at >= the realm's current generation — the artifact, gated on generation, so it can't settle on a prior publish's stale rows or race the fire-and-forget enqueue). The publish handler points its 202 `Location` at the flagged readiness URL, and the matrix `publishRealm` helper waits on it, so a publish is not "done" until the realm is both indexed and rendered. Default readiness stays index-only, so createRealm / boot readiness are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: addba793f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…when HTML never lands Two gaps in the prior commit: 1. Only the 202 Location carried awaitPrerenderHtml, so the shared waitForReady operation — used by the host Publish UI and boxel-cli — still polled the index-only readiness URL and reported a publish complete once the index settled, while the render was still queued. waitForReady gains an opt-in awaitPrerenderHtml flag; the two publish callers (host waitForRealmReady, boxel-cli realm publish) set it, so "publish awaits HTML" reaches production, not just the matrix test. Generic readiness (createRealm) leaves it off. 2. readinessCheck ignored awaitPublishedHtmlReady's result, so a render failure or a wait past its budget still returned 200. Return 503 (Retry-After) when the HTML isn't live for the current generation, so pollers keep waiting and a single-shot caller sees the failure. The matrix publishRealm helper now polls the readiness URL until 200 instead of a single blocking GET. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…a successful isolated_html row awaitPublishedHtmlReady required an instance row with non-null isolated_html at the current generation, which never appears for a realm whose renders error (e.g. the boxel-cli integration harness's noop prerenderer, where every instance becomes an error document) — so readiness hung and publish timed out. Gate instead on any prerendered_html row at >= the current generation: batch.done() swaps a generation's rows in atomically, so a row at the current generation means that generation's render batch has landed, whether the renders succeeded (isolated HTML present) or errored (error rows). Either way the publish's async render work is done; a genuine render failure surfaces downstream as missing markup rather than hanging readiness. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 202 Location now points at the readiness check gated on the published HTML being rendered (?awaitPrerenderHtml=true), so a publish poll only reports ready once the realm is both indexed and viewable. Update the expected Location. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR makes published prerendered HTML a first-class artifact in the publish flow by (1) elevating user-initiated prerender_html work to the same priority tier as user indexing and (2) extending readiness/publish waiting so “publish complete” can be gated on rendered HTML being available for the current realm generation.
Changes:
- Raise user-initiated
prerender_htmlqueue priority to 10 (co-equal with user indexing) and update worker-tier documentation accordingly. - Add an opt-in readiness gate (
awaitPrerenderHtml=true) and plumb it through the sharedwaitForReadyoperation; update publish callers (host UI + boxel-cli) to use it. - Update publish endpoint Location header and relevant tests/helpers to poll readiness rather than racing the served published URL.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/runtime-common/realm.ts | Adds awaitPrerenderHtml query-param gating to _readiness-check, returning 503 while published HTML isn’t ready. |
| packages/runtime-common/realm-operations.ts | Extends waitForReady with awaitPrerenderHtml to append the query param when polling readiness. |
| packages/runtime-common/queue.ts | Makes user-initiated prerender-html priority co-equal with user jobs (10). |
| packages/runtime-common/jobs/prerender-html.ts | Updates priority commentary and introduces awaitPublishedHtmlReady polling helper. |
| packages/realm-server/worker-manager.ts | Updates tier semantics commentary for worker pools given priority changes. |
| packages/realm-server/handlers/handle-publish-realm.ts | Updates publish 202 Location URL to include awaitPrerenderHtml=true. |
| packages/realm-server/tests/publish-unpublish-realm-test.ts | Updates expectation for Location header and clarifies that readiness is gated on rendered HTML. |
| packages/matrix/tests/host-mode.spec.ts | Waits for published realm readiness (indexed + rendered) via readiness-check instead of racing the served URL. |
| packages/matrix/support/isolated-realm-server.ts | Updates worker-tier explanation for the matrix isolated stack. |
| packages/host/app/services/realm-server.ts | Publish UI now waits for readiness gated on prerendered HTML. |
| packages/boxel-cli/src/commands/realm/publish.ts | CLI publish now waits for readiness gated on prerendered HTML. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Readiness callers already re-poll at ~1s (Retry-After: 1) and HTML rendering takes seconds, so the 250ms internal interval only multiplied DB queries (~240 per blocking request) under concurrent publish polls without improving latency. Default to 1000ms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Responsiveness wins over the modest query volume here: the probe is a single indexed `SELECT 1 … LIMIT 1`, and readiness should return promptly once the render batch lands rather than tracking the caller's coarser ~1s re-poll. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the fixed-interval poll with an event-driven wait: pg-queue emits NOTIFY jobs_finished when a job's finalize transaction commits, and the prerender-html batch's generation swap is durable by then, so re-checking on that signal catches the current generation landing near-instantly — with far fewer DB queries than a 250ms poll under concurrent publish polls. The LISTEN is feature-detected (PgAdapter exposes subscribe; SQLite has no pub/sub) and fire-and-forget: a coarse periodic poll remains as the guarantee, covering a missed notification, a failed/slow LISTEN, and adapters without pub/sub, so readiness never hangs on the subscription. Resolves as soon as the render channel catches up, false on timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Background
Stacked on #5511 (dedicated user-index worker tier — fixes
createRealm/registration provisioning flakes). This PR fixes the remaining publish-path flake class:host-mode.spec.ts,head-tags.spec.ts, and thepublish-realmrepublish (CS-11043) test.The issue is semantic.
prerender_htmlwas split out of the indexing job into its own priority-9 queue job — one notch below user indexing (10), treated as an off-critical-path follow-on. But for a published realm the rendered HTML is the deliverable: a realm that is indexed (searchable) but not prerendered serves a blank shell. Andserve-index.tsreads a card's isolated HTML only from theprerendered_htmltable with no on-demand fallback, while realm readiness gates on the index pass only — so under load the priority-9 render job sat ~40s behind bigger sweeps and the published URL kept returning a markerless shell past the test's 45s poll.Changes
1. Prerendered HTML is co-equal with the index (priority 10). User-initiated
prerender_htmlis raised from 9 to 10 — as first-class as the search index. System-initiated prerender-html stays background (0). With prerender-html at 10, the high-tier worker pool serves indexing and rendering as one user-work tier; capacity (not a dedicated lane) keeps both moving.2. Publishing awaits the HTML (all publish paths).
_readiness-checkgains an opt-inawaitPrerenderHtmlparam that, after the index await, waits for the realm's published HTML to be live for its current generation —awaitPublishedHtmlReadypolls theprerendered_htmlartifact for an instance row withisolated_htmlat ≥ the realm's current generation (gated on generation, so it can't settle on a prior publish's stale rows or race the fire-and-forget enqueue). If the HTML never lands within budget, readiness returns 503 rather than a false 200.The opt-in is wired through the shared
waitForReadyoperation, so all publish callers await it — the host Publish UI (waitForRealmReady, which backs the "Publishing…" state) and boxel-clirealm publish— not just the matrix helper. Generic readiness (e.g. createRealm, boot) leaves it off, so it stays index-only and fast.Verification
Stacked on #5511, so CI runs with both. The matrix shards should be reliably green (not a lucky re-run), and the prerender-html queue-wait tail should drop well below the ~45s seen before.
createRealmsettle-time diagnostics from #5511 should stay low (index not regressed by sharing the tier).Note
Stacked PR — base is #5511's branch. Retarget to
mainonce #5511 merges. Touchesboxel-cli(publish now awaits HTML), hence thefix:title prefix for the version bump.