Public: anyone can read this; the crew can change it.

The claim page says when its link stops working and how to get another, in plain words #11349

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The claim page now says when its link stops working, and a dead link now answers 410 with a plain line on why and who can send another. The new test file and the existing claim tests pass (37 tests) and `tsc` is clean. I haven't run the rest of the suite (the forge runs the full check on push). `design-lint` printed its OK line; `scripts/gate.ts` printed nothing, so I don't know it ran.

shipped production is serving it deploy eb2664b

opened this

The claim page now says when its link stops working, and a dead link now answers 410 with a plain line on why and who can send another. The new test file and the existing claim tests pass (37 tests) and tsc is clean. I haven't run the rest of the suite (the forge runs the full check on push). design-lint printed its OK line; scripts/gate.ts printed nothing, so I don't know it ran.

What changed

  • Valid link: GET /claim (src/web/pages/claim.ts) shows "This link works until <time> Nov 12, 14:30 UTC (in 23 hours). After that, ask alice to send you a new one." The server renders it, and the date and the relative time both come from the stored expiry.
  • Dead link: an expired or used link now answers 410 on both GET /claim and POST /claim. The text says why and names the spawner, for example "It stopped working on Nov 10, 09:00 UTC. Ask alice to send you a new one." The revoked link also names the spawner.
  • src/spawn/claim.ts: linkOf now also returns expiresAt.
  • Tests: test/claim-page-words.test.ts covers valid, expired and used links. The expired and used cases assert the plain wording and that the page has no "token" or 4xx number.

Decisions

  • The GET can't see which code a visitor holds, because the code lives in the # fragment. So it answers from the forge's single current link: open shows the expiry, and expired, used or revoked shows the 410. That says nothing about any particular code.
  • A made-up code on POST /claim still gets the same 404 "not a forge link" as before, so nobody can learn whether a code ever existed.
  • A forge that already has an admin and no recorded link still shows the form on GET. I left that alone because claim-timing.test.ts relies on it.

Try it: spawn a forge and open /claim. To see the 410, set the stored link's e to a past time and reload.

I added an empty Try: commit because the pre-push hook asked for one.

🤖 Generated with Claude Code

Closes #11339

commented

The claim page now says when its link stops working, and a dead link now answers 410 with a plain line on why and who can send another. The new test file and the existing claim tests pass (37 tests) and tsc is clean. I haven't run the rest of the suite (the forge runs the full check on push). design-lint printed its OK line; scripts/gate.ts printed nothing, so I don't know it ran.

What changed

  • Valid link: GET /claim (src/web/pages/claim.ts) shows "This link works until <time> Nov 12, 14:30 UTC (in 23 hours). After that, ask alice to send you a new one." The server renders it, and the date and the relative time both come from the stored expiry.
  • Dead link: an expired or used link now answers 410 on both GET /claim and POST /claim. The text says why and names the spawner, for example "It stopped working on Nov 10, 09:00 UTC. Ask alice to send you a new one." The revoked link also names the spawner.
  • src/spawn/claim.ts: linkOf now also returns expiresAt.
  • Tests: test/claim-page-words.test.ts covers valid, expired and used links. The expired and used cases assert the plain wording and that the page has no "token" or 4xx number.

Decisions

  • The GET can't see which code a visitor holds, because the code lives in the # fragment. So it answers from the forge's single current link: open shows the expiry, and expired, used or revoked shows the 410. That says nothing about any particular code.
  • A made-up code on POST /claim still gets the same 404 "not a forge link" as before, so nobody can learn whether a code ever existed.
  • A forge that already has an admin and no recorded link still shows the form on GET. I left that alone because claim-timing.test.ts relies on it.

Try it: spawn a forge and open /claim. To see the 410, set the stored link's e to a past time and reload.

I added an empty Try: commit because the pre-push hook asked for one.

🤖 Generated with Claude Code

commented

The two failing tests didn't come from the claim change, and they pass here after I merged current main into the branch and pushed. I haven't seen the forge's check re-run on this push yet.

  • job-slices: it failed because src/main.ts runs an entitlements job under during that the test hadn't listed. That job isn't part of my change, and the test passes now that main is merged in.
  • past its deep budget (in test/crawl-budget.test.ts): it passes here; I only ran it after the merge, so I didn't reproduce the failure. I didn't change anything it covers, so I'm treating it as a one-off from the shard run.

After the merge, the claim tests (test/claim-page-words.test.ts and test/claim-link.test.ts) pass, and tsc is clean. The claim page change itself is unchanged: it shows the expiry on a valid link and gives a plain 410 for an expired or used one.

Approved

Approved. The valid page shows a date and a relative time in a <time> element, server-rendered and fine without JavaScript. Expired and used links answer 410 on GET and POST in plain words and name who to ask. The made-up-code 404 is kept, so nobody learns whether a code ever existed. The new tests cover valid, expired and used links. The preview forge is not a spawned one, so I could not see the expiry line live and judged it from the source and tests. Two things to do next: (1) add a test for the revoked wording and for the 422 re-render, which now also passes expiresAt and is untested; (2) the tests share one mutable stored link and depend on running in order, so reset it per test.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs