diff --git a/CLAUDE.md b/CLAUDE.md index df7cba88..fa4166c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,6 +73,7 @@ These mirror `CONTRIBUTING.md`. Enforce them in your own writing and when review | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `src/content/spec//.md` | Spec content. Edit here. | | `src/content.config.ts` | Content collection schema. Edit if adding a field. | +| `src/content/considered/.md` | Hand-curated register of standards deliberately **not** specced, with the reason and what would reverse it. Rendered at `/considered/`. Not derived. | | `src/lib/site.ts` | Site metadata + the canonical category list. | | `src/layouts/BaseLayout.astro` | HTML shell, head, dialog for ⌘K search, Plausible (PROD only). | | `src/layouts/SpecLayout.astro` | Spec page wrapper; emits TechArticle + BreadcrumbList JSON-LD; advertises Markdown alt via `markdownUrl`. | @@ -153,7 +154,32 @@ The same PR must do **one** of: Also update the [api-catalog Linkset](public/.well-known/api-catalog) if the capability adds a discoverable resource, and the global `Link` header in [`public/_headers`](public/_headers) if it has a registered IANA `rel`. Both should match what the new spec page describes. -The reverse holds too: **do not promote a convention to spec status without us shipping a working implementation first.** The site is the proof-of-feasibility. If the underlying convention turns out non-existent or defunct (see the `/.well-known/ai.txt` deletion history), remove the asset _and_ the spec page in the same PR — don't leave the page documenting something we no longer ship. +If the underlying convention turns out non-existent or defunct (see the `/.well-known/ai.txt` deletion history), remove the asset _and_ the spec page in the same PR — don't leave the page documenting something we no longer ship. + +### Speccing and shipping are separate decisions + +**We do not have to ship something to spec it.** The site is a worked example wherever that is useful, not a gate. Plenty of good advice does not apply to a static, cookieless, six-page-of-plumbing site: a page can be right for the sites reading it and irrelevant here. When that is the case, write the page and skip the implementation — but **say so on the page**, in one line, with the reason. "This site does not ship it: we are static, so no response ever means _this changed_" is useful content. Silence is not. + +So there are three honest shapes for a page, and all three are fine: + +1. **Specced and shipped** — the common case. Carry the "this site ships it; see [X]" callout. +2. **Specced, deliberately not shipped** — the topic does not apply to us. Carry a one-line note saying that, and why. +3. **Not specced** — see below. + +### What we don't spec, and where that gets recorded + +**Do not add a page for a convention nothing is using yet.** A standard being final is not sufficient; something has to implement it, or the page recommends a header no cache reads and an element no browser honours. Being final also is not necessary — a widely-deployed convention can earn a page before its RFC lands. + +**This particular call belongs to the maintainer, not to an agent.** If a topic is real and well-sourced but adoption looks thin, do not add the page and do not silently drop it either — surface it (Slack, for the daily scan; the PR description otherwise) and let Joost decide. + +When a topic is turned down, record it in **`src/content/considered/`** — the hand-curated collection rendered at [`/considered/`](src/pages/considered.astro). One file per topic: + +- `title`, `date` (the decision), `reason` (`too-early` | `out-of-scope` | `too-narrow`), `sources`, and `revisit` — the last being _what would change our mind_, which is what keeps the register from becoming a graveyard. +- A two-or-three-paragraph body: what the thing is, why it did not land, and — where the reasoning generalises — what it is the reference case for. + +Being in `/considered/` is not a rejection forever. When the reason expires (something ships it; adoption broadens), delete the entry in the same PR that adds the spec page. + +Like the changelog, this collection is **not derived** — nothing generates it. It is also **not** a to-do list: entries are decisions already taken, not a queue. ## When changing a status diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19ca8030..b8631d8d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,12 @@ Just open a PR. You can use the "Edit this page on GitHub" link on any spec page 3. Run `npm run dev` locally on port 31337 and verify the page renders. 4. Open a PR. CI will type-check, build, and verify the schema. +A topic has to be **used**, not merely standardised. A final RFC with a permanent IANA registration still does not earn a page if nothing implements it — the page would recommend a header no cache reads. The reverse also holds: a widely-deployed convention can earn a page before its RFC lands. If adoption looks thin, say so in the issue and let the maintainer weigh it. + +Topics that get turned down are recorded publicly at [`/considered/`](https://specification.website/considered/), with the reason and with what would reverse the decision. That page is worth reading before you open an issue — your topic may already be there, in which case the useful contribution is evidence that its `revisit` condition has now been met. + +Note that we do **not** require the site to implement something before specifying it. Plenty of good advice does not apply to a small static site; such a page simply says so in one line, and explains why. + ### New category These are rare. Open an issue first — categories are slow-changing. diff --git a/ops/routines/daily-standards-scan.md b/ops/routines/daily-standards-scan.md index dc2fc83a..f676f574 100644 --- a/ops/routines/daily-standards-scan.md +++ b/ops/routines/daily-standards-scan.md @@ -8,8 +8,8 @@ You maintain **specification.website** — a platform-agnostic spec of what a good website does, generated entirely from Markdown under `src/content/spec//.md`. Read `CLAUDE.md` and `CONTRIBUTING.md` first; they are binding (status discipline, -cardinal content rules, the "ship it before you spec it" rule, the add-a-page workflow, -the SKILL.md digest step). +cardinal content rules, the separation of speccing from shipping, the add-a-page +workflow, the SKILL.md digest step). ## Goal @@ -121,10 +121,24 @@ context. `recommended`/`optional`; `avoid` for outdated/harmful. Default to `recommended`. - Primary sources only (WHATWG / W3C / IETF / IANA / WCAG / schema.org first; MDN / web.dev for context). -- **Ship it before you spec it**: a brand-new _convention_ that would require the site to - implement a new capability does NOT get a PR — flag it in Slack as "needs us to ship X - first" so the maintainer can decide. PRs are only for documenting standards we can - honestly describe (and, where applicable, that the site already satisfies). +- **Speccing does not require shipping.** A page whose topic does not apply to this site + is still a good page — write it, and add a one-line note saying we do not ship it and + why. Do not withhold a PR on the grounds that we would have to implement something. +- **Adoption, not finality, is the bar.** Do NOT open a PR for a convention nothing is + using yet, however final the RFC and however permanent the IANA registration. A page + recommending a header no cache reads is worse than no page. Conversely a widely-deployed + convention can earn a page before its RFC lands. +- **Thin adoption is the maintainer's call, not yours.** When a topic is real and + well-sourced but you cannot find implementations, do not open the PR and do not silently + drop it. Put it in Slack with what you checked (MDN/BCD, Chrome Platform Status, the + relevant CDN or server docs) and let Joost decide. If he says add it, add it. +- **Record every turn-down.** Anything you skip on adoption or scope grounds gets an entry + in `src/content/considered/` — `title`, `date`, `reason` (`too-early` | `out-of-scope` | + `too-narrow`), `sources`, `revisit` (what would change our mind), and a short body. That + register at `/considered/` is public and is the reason the Slack "skipped, and why" + section exists: the two should agree. Adding an entry there is a normal PR, and it is the + right output for a scan that found something real but premature. When the reason later + expires, delete the entry in the same PR that adds the spec page. ## Dedup (critical for a daily job) @@ -160,6 +174,8 @@ DM the maintainer with: - New topics found → PR links (or "flagged, needs implementation decision"). - Status changes → page + what moved + source + PR link. - Stale/dead citations → page + broken source + fix PR link. -- Anything deliberately skipped, and why. +- Anything deliberately skipped, and why — plus whether it earned a `/considered/` entry + (and the PR link if so). Anything you skipped for thin adoption goes here as an explicit + question for Joost, not as a closed decision. Keep it scannable: grouped, one line each, links inline. diff --git a/public/.well-known/agent-skills/index.json b/public/.well-known/agent-skills/index.json index 79feefc2..7798f311 100644 --- a/public/.well-known/agent-skills/index.json +++ b/public/.well-known/agent-skills/index.json @@ -6,7 +6,7 @@ "type": "skill-md", "description": "Query and apply The Website Specification — a platform-agnostic specification of what a good website does. Use when the user asks what their site should have, whether something is required, how to audit a URL, what's missing for agent readiness, or anything else where you'd otherwise be guessing at web best practice. Backs answers with primary sources and ships an MCP server with search, list, fetch, checklist, and audit tools.", "url": "/.well-known/agent-skills/specification-website/SKILL.md", - "digest": "sha256:487b0ea2610fb2e4887e8dc7b682cd6de270934d6a9513409a3c202fd62b9a7c" + "digest": "sha256:71188d9a6b563ac302db37f26e319e246ccba55d03ead299385d9828075a680d" } ] } diff --git a/public/.well-known/agent-skills/specification-website/SKILL.md b/public/.well-known/agent-skills/specification-website/SKILL.md index f675885d..bda1d32f 100644 --- a/public/.well-known/agent-skills/specification-website/SKILL.md +++ b/public/.well-known/agent-skills/specification-website/SKILL.md @@ -138,6 +138,18 @@ status promotion (e.g. a topic moving to `required`) can change a previously passing site into a failing one, so a delta check is worth doing even when no new pages were added. +## When a topic is absent + +Absence is not always an oversight. `https://specification.website/considered/` +lists standards that were evaluated and deliberately left out, each with a reason +— `too-early` (final, but nothing implements it), `out-of-scope` (a way of +building a site, not something a site does), `too-narrow` (real, but applies to +too few sites) — and with what would reverse the decision. + +Check it before concluding the spec has a gap, and before recommending that a +topic be added. If the topic is listed, the useful contribution is evidence that +its stated revisit condition has now been met, not a fresh proposal. + ## Sources and licence Code MIT. Content CC BY 4.0. Site source: . diff --git a/public/og/considered.png b/public/og/considered.png new file mode 100644 index 00000000..82bd865e Binary files /dev/null and b/public/og/considered.png differ diff --git a/scripts/generate-assets.mjs b/scripts/generate-assets.mjs index 3aaae2cb..22b3ea93 100644 --- a/scripts/generate-assets.mjs +++ b/scripts/generate-assets.mjs @@ -588,6 +588,13 @@ const marketingPages = [ subtitle: "New topics, status changes, and honest removals. Newest first, with sources.", }, + { + slug: "considered", + eyebrow: "CONSIDERED", + title: "What we left out.", + subtitle: + "Standards we read, weighed, and did not adopt — with the reason, and what would change it.", + }, { slug: "privacy", eyebrow: "PRIVACY", diff --git a/src/components/SiteFooter.astro b/src/components/SiteFooter.astro index fd20a80e..fe81e917 100644 --- a/src/components/SiteFooter.astro +++ b/src/components/SiteFooter.astro @@ -79,6 +79,11 @@ import { site, categories } from "~/lib/site"; >Changelog +
  • + Considered +
  • PrivacyChangelog
  • +
  • + Considered +
  • `, subscribers register with that hub, and the hub delivers new content as it appears instead of every reader re-fetching the feed on a timer. It is a W3C Recommendation, re-published on 2 June 2026 with added cross-site-scripting mitigations for the subscription verification step. + +It clears the bar on every count but one. The convention is real, it is implemented, and it is auditable — `rel="hub"` is right there in the feed. But outside the fediverse and the IndieWeb, very few sites publish a hub, and fewer still would benefit: for a site with a handful of new items a week, polling costs almost nothing and adds no moving parts. Recommending a hub to every site that ships a feed would be recommending infrastructure most of them should not run. + +The existing pages on [feed discovery](/spec/foundations/feed-discovery/) and [feed hygiene](/spec/foundations/feed-hygiene/) cover what nearly every site actually needs from a feed. diff --git a/src/lib/site.ts b/src/lib/site.ts index 5185f419..b1b9d587 100644 --- a/src/lib/site.ts +++ b/src/lib/site.ts @@ -120,3 +120,31 @@ export const changeTypeColor: Record = { status: "bg-amber-50 text-amber-800 border-amber-200", removed: "bg-red-50 text-red-800 border-red-200", }; + +export const consideredReasonLabel: Record = { + "too-early": "Too early", + "out-of-scope": "Out of scope", + "too-narrow": "Too narrow", +}; + +export const consideredReasonColor: Record = { + "too-early": "bg-blue-50 text-blue-800 border-blue-200", + "out-of-scope": "bg-ink-100 text-ink-700 border-ink-200", + "too-narrow": "bg-amber-50 text-amber-800 border-amber-200", +}; + +// Shown as the intro under each group heading on /considered/. +export const consideredReasonBlurb: Record = { + "too-early": + "The standard is real and final, but nothing implements it yet. Speccing it would mean recommending something no visitor, crawler, or cache would notice.", + "out-of-scope": + "Real and widely used, but not a property of a website you could check from the outside. These are ways of building a site, not things a good site does.", + "too-narrow": + "Real, implemented, and auditable — but it applies to too few sites to belong in a general specification.", +}; + +export const consideredReasonOrder = [ + "too-early", + "out-of-scope", + "too-narrow", +] as const; diff --git a/src/pages/considered.astro b/src/pages/considered.astro new file mode 100644 index 00000000..ab9dddf4 --- /dev/null +++ b/src/pages/considered.astro @@ -0,0 +1,128 @@ +--- +import BaseLayout from "~/layouts/BaseLayout.astro"; +import Breadcrumbs from "~/components/Breadcrumbs.astro"; +import { + consideredReasonBlurb, + consideredReasonColor, + consideredReasonLabel, + consideredReasonOrder, +} from "~/lib/site"; +import { getCollection, render } from "astro:content"; + +const entries = await getCollection("considered", ({ data }) => !data.draft); +entries.sort((a, b) => b.data.date.localeCompare(a.data.date)); + +const rendered = await Promise.all( + entries.map(async (entry) => ({ + entry, + Content: (await render(entry)).Content, + })), +); + +const dayFmt = new Intl.DateTimeFormat("en-GB", { + day: "numeric", + month: "short", + year: "numeric", +}); + +// Group by reason, in the fixed order from site.ts rather than by date, so the +// page reads as a register of decisions rather than a timeline. +const groups = consideredReasonOrder + .map((reason) => ({ + reason, + items: rendered.filter(({ entry }) => entry.data.reason === reason), + })) + .filter((g) => g.items.length > 0); +--- + + +
    + +
    +

    + Considered, not adopted +

    +

    + A specification is defined as much by what it leaves out as by what it + contains. These are standards we have read, weighed, and decided not to + cover — each with the reason, and with what would change our mind. The + changelog records what went in; this records what did not. +

    +
    + +
    + { + groups.map((group) => ( +
    +

    + + {consideredReasonLabel[group.reason]} + +

    +

    + {consideredReasonBlurb[group.reason]} +

    + +
      + {group.items.map(({ entry, Content }) => ( +
    1. +
    2. + ))} +
    +
    + )) + } +
    +
    + diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts index 5c0b1779..0a8dcc19 100644 --- a/src/pages/llms.txt.ts +++ b/src/pages/llms.txt.ts @@ -99,6 +99,9 @@ export const GET: APIRoute = async () => { lines.push( `- [Checklist](${site.url}/checklist/): The whole spec as a flat checklist. Also as a copy-and-paste [Markdown task list](${site.url}/checklist.md).`, ); + lines.push( + `- [Considered, not adopted](${site.url}/considered/): Standards deliberately left out of the spec, with the reason for each and what would change it.`, + ); lines.push( `- [MCP server](${site.url}/mcp/): How to connect an MCP client to the spec.`, );