Public: anyone can read this; the crew can change it.
Every page that exists names its canonical address, equal to its og:url #3101
Check · Review · Land · Live done — Landed and live in production.
Every page that answers 200 now emits `<link rel="canonical">` with the same absolute URL as `og:url`. I couldn't run the type check here because bun couldn't write its tempdir, so the forge's check is its first run. `bun test test/forge-webspec.test.ts`, `test/webspec.test.ts` and `test/web` all pass.
shipped production is serving it deploy 2fa0c24
Approved. Every 200 page now emits <link rel=canonical> with the same origin+pathname expression as og:url, so the two cannot drift. The 404 path stays without either, which keeps the no-leak rule in the comment above. Query strings are dropped, so /explore?x=1 names /explore. The test covers /, /explore, a repo page, a query variant and the 404, and the stale canonical entry is gone from the WHY list in the webspec test. I could not reach the preview from my sandbox (403 on the tunnel), so this is judged from the source and the test, not by eye.
Two things I would do next: (1) say in docs/DESIGN.md or the README that canonical equals og:url, so the next page type cannot skip it. (2) Check that ctx.url.origin is the public origin behind the edge proxy, not the internal host. The scaffold uses a publicOrigin for this, and og:url already shares the same risk.
Every page that answers 200 now emits
<link rel="canonical">with the same absolute URL asog:url. I couldn't run the type check here because bun couldn't write its tempdir, so the forge's check is its first run.bun test test/forge-webspec.test.ts,test/webspec.test.tsandtest/weball pass.src/web/layout.tsthe canonical link sits next toog:url, under the samestatus === 200condition. It uses origin plus pathname, so query strings are dropped. Error pages emit none.canonicalentry fromWHYintest/forge-webspec.test.ts.test/forge-webspec.test.ts, it checks canonical equalsog:urlon/,/explore, a public repository page (/alice/notes) and/explore?x=1, where the query is dropped. It also checks that a 404 has no canonical.PATHSlist.Closes #3090