Repository navigation
Mentions show up when they arrive: mentions and responses change domains (migration 0032) - #79
Merged
Merged
Conversation
Webmentions verify in seconds, but their views did not follow. Two revision counters on change_state, with triggers on mentions_in and mentions_out in 0024's remote-safe shape: - mentions: any outbound change, and inbound changes to rows the Studio lists (verified or gone). The Studio's mention lists and source cards now read through the revision envelope instead of re-reading both lists and every source card on each 15 s tick. - responses: inbound changes to rows the public responses list shows (verified, not hidden). It joins the public HTML cache key, so a new response or a hide/show reaches the item page at once. Pending claims and their failure move neither counter, so spam cannot churn the edge cache. pruneFailedInbound counts deleted rows via RETURNING: D1's meta.changes now includes the trigger's state-row write. The mentions view drops its 'about a minute' edge-cache note. Oracle families, field branches and a status-transition receiving witness cover both domains; an e2e test writes a verified mention through a fixture-only side channel and sees it in the open view within one poll, and after a resume. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Mentions show up when they arrive:
mentionsandresponseschange domains (migration 0032)Diagnosis
On the live blyg, mentions are delivered and verified quickly. Every recent inbound mention went from
first_seentoverified_atin 1–4 s, and outbound sends succeed on the first attempt. Delivery is not the problem. What lags is the views.PUBLIC_DOMAINSinsrc/html-cache.tshad no mentions domain ("Inbound mentions have no domain yet and still wait out the 60-second window"). The edge entry ismax-age=60, stale-while-revalidate=300, so a newly verified response, or a hide or show, could take up to about 6 minutes to appear on the item's public page. The Studio even said so: "The public page can take about a minute to reflect a change here".main(b6b2cd5), that is not the case.MentionsPagealready callsusePollon both lists. A new e2e test, written first and run againstmain, passed there: a verified mention showed up within one 15 s tick, and a resumed tab checked at once. But becauseinbound,outboundandmentionSourceswere not keyed on/api/changes, every tick re-read both lists in full plus one source card per row, even when nothing had changed.Fix
Migration
0032_mentions_change_domain.sqladds two counters tochange_stateand six triggers, written in 0024's remote-safe shape (SELECT RAISE … WHERE, and everyCASEclosed inside parentheses):mentionsmentions_outinsert, update or delete; amentions_inchange to a row whose status is, or was,verified/gone(the rowsGET /mentionslists)reading) each mention's source cardresponsesmentions_inchange to a row that is, or was,verifiedand not hidden (exactly whatlistPublicResponsesshows)There are two counters instead of one because the public condition has to be narrow. Anyone can POST a claim. If pending rows rolled the public key, a stranger could empty the edge cache at the receiver's rate cap. A pending claim, its failure and the daily prune of failed claims move neither counter. They still pay the one state-row write every tracked table pays, and they are still refused if the state row is missing.
src/change-state.ts:CHANGE_DOMAINSgainsmentionsandresponses.openapi.jsonand the SDK were regenerated:ChangeState.domainsderives from the list.src/html-cache.ts:responsesjoinsPUBLIC_DOMAINS, with a comment explaining the trade-off.src/ui/data.ts:inboundandoutboundread throughcachedQuery(…, 'mentions').mentionSourcesuses['mentions', 'reading'], because a source card joins imported items and subscriptions, and both advancereading.readIfChangednow accepts several domains, labelled with their sum: counters only increase within an epoch, so the sum moves exactly when one of them does.Pollingalready stops while hidden and refreshes immediately onvisibilitychangeback to visible and onfocus, for every domain. The new e2e test asserts that no reads happen while hidden and that a check runs at once on resume.src/mentions/store.ts:pruneFailedInboundcounts deleted rows withRETURNING id. D1'smeta.changesnow includes the trigger's state-row write (the oracle already documents that only its zero/nonzero meaning holds), which made the count double.src/ui/catalog.tsx: the "about a minute … cached at the edge" note is removed.scripts/reset-change-epoch.sqlresets the new counters.docs/d1-polling-cache-operations.mddocuments the domains, the deploy order (0032 before the Worker) and the restore steps.Tests
test/polling-cache.oracle.test.ts,polling-cache-trigger-cases.ts):mentions_inandmentions_outare trigger families, which gives each one the insert/update/delete effect, missing-state rejection and every-column-in-guard checks. There are also 28 one-field branches, plus a new receiving witness that walks a claim's whole life (claim, fail, re-claim, verify, hide, re-claim while hidden, gone, show while pending, verify, prune). Each step's expected domains are checked against observed differences inGET /api/mentionsand in the public page's responses section, not against the trigger text.test/html-cache.test.ts: the public key moves on verify, hide and show. It does not move on a pending claim, its failure, a re-verification of a hidden row, or outbound queue work. The item page renders a new response and drops a hidden one.test/migration-remote-shape.test.ts: 0032 passes.test-ui): mention lists and source cards re-read only when their revision moves. A two-domain query reloads on either domain and on no other.e2e/polling-cache.spec.ts): with the Studio open on the mentions view, a fixture-only side channel inscripts/e2e-server.tswrites a verified mention (the Worker ships no such route). It appears in the open view within one poll. After that, a hidden tab makes no reads over 60 s, and an owner hide made in the meantime shows as soon as visibility returns.Results (local, on the commit):
npm run typecheck: cleannpm test: 142 files, 1785 passed, 5 skippednpm run test:ui: 12 files, 99 passednpx playwright test(desktop + mobile): 337 passed, 1 skippednpm run test:polling-cache:mutations: all 9 controls rejected at their checkpointsnpm run test:mutations(oracle): every control caughtmigrations/changed)Follow-ups (not in this PR)
🤖 Generated with Claude Code