Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions .github/workflows/spec-drift-watch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: spec drift watch

# Once a week, checks every standard's spec against the state last read for
# it (the pins in spec-pins.json, generated from `spec_sources` in
# content/standards). For each standard whose spec changed, it opens or
# refreshes one issue that links what changed; it closes the issue once the
# pins are moved. It also publishes a small status file to the
# `spec-drift-status` branch, which the standard pages read to show "The spec
# changed after this check."
#
# The check itself is scripts/spec-drift.mjs, shared byte for byte with Ora's
# watcher (eralabs-ai/ora). It needs no npm install. Upstream content never
# reaches a shell: the script runs git and gh with argument arrays, and only
# hashes, revision names and digests reach the issues.

on:
schedule:
- cron: "17 5 * * 1"
workflow_dispatch:

# Two jobs, so the job that reads upstream repositories and pages holds no
# write token: `check` runs read-only and hands its report to `report`,
# which alone can write issues and the status branch.
permissions:
contents: read

concurrency:
group: spec-drift-watch
cancel-in-progress: false

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
outcome: ${{ steps.check.outcome }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc

- name: Compare every spec with its pins
id: check
continue-on-error: true
run: node scripts/spec-drift.mjs check spec-pins.json --out "$RUNNER_TEMP/report.json"

- uses: actions/upload-artifact@v4
with:
name: spec-drift-report
path: ${{ runner.temp }}/report.json
retention-days: 7

report:
needs: check
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write # push the status file to the spec-drift-status branch only
issues: write
env:
GH_TOKEN: ${{ github.token }}
STATUS_BRANCH: spec-drift-status
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- uses: actions/download-artifact@v4
with:
name: spec-drift-report
path: ${{ runner.temp }}

- name: Read the last published status
run: |
# The branch is missing until the first run publishes it.
if git ls-remote --exit-code --heads origin "$STATUS_BRANCH" >/dev/null; then
git fetch --depth 1 origin "+refs/heads/$STATUS_BRANCH:refs/remotes/origin/$STATUS_BRANCH"
git show "origin/$STATUS_BRANCH:spec-drift.json" > "$RUNNER_TEMP/status-in.json"
fi

- name: Open, refresh or close the drift issues
run: |
node scripts/spec-drift.mjs issues "$RUNNER_TEMP/report.json" --repo "$GITHUB_REPOSITORY" \
--status-in "$RUNNER_TEMP/status-in.json" --status-out "$RUNNER_TEMP/status-out.json"

- name: Publish the status for the site
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
dir="$RUNNER_TEMP/status-branch"
if git rev-parse --verify --quiet "origin/$STATUS_BRANCH" >/dev/null; then
git worktree add --detach "$dir" "origin/$STATUS_BRANCH"
else
git worktree add --orphan -b "$STATUS_BRANCH" "$dir"
fi
cp "$RUNNER_TEMP/status-out.json" "$dir/spec-drift.json"
git -C "$dir" add spec-drift.json
if git -C "$dir" diff --cached --quiet; then
echo "status unchanged"
exit 0
fi
git -C "$dir" commit --quiet -m "chore: update spec drift status"
git -C "$dir" push origin "HEAD:refs/heads/$STATUS_BRANCH"

- name: Fail when a spec could not be read
if: needs.check.outputs.outcome == 'failure'
run: |
echo "At least one spec source could not be read; see the check step. Its issue was left as it was."
exit 1
16 changes: 14 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ By taking part you agree to follow the [code of conduct](CODE_OF_CONDUCT.md).
| You want to | Do this |
| --- | --- |
| Fix a wrong sentence or a stale status | Press **Edit on GitHub** at the foot of the standard's page, change the line, link your source, open a pull request. |
| Re-check a page | Check every claim against its sources, fix what changed, update `last_verified`. |
| Re-check a page | Check every claim against its sources, fix what changed, update `last_verified`. If the spec itself changed, move its pins too (see [Spec sources](#spec-sources)). |
| Add a standard | Copy [`content/standards/_template.md`](content/standards/_template.md) to `content/standards/<id>.md` and fill it in. |
| Report a problem without fixing it | [Report an error](https://github.com/ora/standards/issues/new?template=correction.yml). |
| Suggest a standard without writing it | [Suggest a standard](https://github.com/ora/standards/issues/new?template=new-standard.yml). |
Expand Down Expand Up @@ -44,6 +44,18 @@ A partner list is not adoption, and a transaction count is not commerce. If a nu
- Put the spec first in `links`.
- Change `last_verified` only when you have rechecked **every** claim on the page.

## Spec sources

Every standard lists where its spec lives in `spec_sources`, with a `pin`: the exact state of the spec that was read (a Git SHA, a release tag, a draft revision, or a digest of a page's text). Once a week a check ([`scripts/spec-drift.mjs`](scripts/spec-drift.mjs), run by [`spec-drift-watch.yml`](.github/workflows/spec-drift-watch.yml)) compares each spec with its pins. When one changed, it opens an issue that links what changed, and the standard's page says "The spec changed after this check" until the pins move. The template lists the kinds of source.

When you handle one of those issues:

1. Read what changed and recheck the page against it.
2. Set each moved `pin` to the live value the issue lists, even when nothing on the page needed to change: moving a pin is the record that someone read the change.
3. Run `npm run pins`. It regenerates [`spec-pins.json`](spec-pins.json), which the check reads, and the tests fail until it matches the content files.

To see the live state yourself, run `node scripts/spec-drift.mjs check spec-pins.json --only <id>`. It needs Node and Git, no install. [Ora](https://ora.ai) reads `spec-pins.json` too and compares it with the pins behind its own checks, so tell us in the pull request if a change looks like it should change how sites are scored.

## House style

- Plain words and short sentences. Explain a term the first time it appears.
Expand Down Expand Up @@ -75,7 +87,7 @@ npm test
npm run build
```

If you added a standard or changed a name or a `phrase`, run `npm run readme` too: it regenerates the table of standards in the README, and the tests fail until it matches.
If you added a standard or changed a name or a `phrase`, run `npm run readme` too: it regenerates the table of standards in the README, and the tests fail until it matches. If you changed `spec_sources`, run `npm run pins` for the same reason.

## License of contributions

Expand Down
20 changes: 17 additions & 3 deletions app/standards/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { StandardTile } from "@/components/standard-tile";
import { StatusBadge } from "@/components/status-badge";
import { content, editHref, formatDate } from "@/lib/content";
import { renderInline, renderMarkdown } from "@/lib/markdown";
import { getSpecDrift } from "@/lib/spec-drift";
import { daysSince, getJob, getStandard, getStandards } from "@/lib/standards";
import "../standard.css";

type Props = { params: Promise<{ id: string }> };

// Every standard is a file, so every page is made at build time.
export const dynamicParams = false;
// Made again at most hourly (SPEC_DRIFT_REVALIDATE), to pick up the spec
// drift status and keep the "out of date" mark current between deploys.
export const revalidate = 3600;

export function generateStaticParams() {
return getStandards().map((standard) => ({ id: standard.id }));
Expand Down Expand Up @@ -42,6 +46,7 @@ export default async function StandardPage({ params }: Props) {
return target ? [{ ...r, standard: target }] : [];
});
const stale = daysSince(standard.last_verified) > content.staleAfterDays;
const drift = await getSpecDrift(standard.id);
const file = `content/standards/${standard.id}.md`;

return (
Expand Down Expand Up @@ -117,10 +122,19 @@ export default async function StandardPage({ params }: Props) {

<footer className="verified">
<p>
{stale && <strong className="verified__stale">May be out of date. </strong>}
{drift ? (
<strong className="verified__stale">{content.specDrift.changed} </strong>
) : (
stale && <strong className="verified__stale">May be out of date. </strong>
)}
Last checked against its sources on{" "}
<time dateTime={standard.last_verified}>{formatDate(standard.last_verified)}</time>. These standards
move fast; if something changed, please help.
<time dateTime={standard.last_verified}>{formatDate(standard.last_verified)}</time>.{" "}
{drift?.issue && (
<>
<a href={drift.issue}>{content.specDrift.seeChange}</a>.{" "}
</>
)}
These standards move fast; if something changed, please help.
</p>
<p className="verified__actions">
<a href={editHref(file)} className="button">
Expand Down
7 changes: 6 additions & 1 deletion content/site.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@
"repo": {
"url": "https://github.com/ora/standards",
"branch": "main",
"newIssueUrl": "https://github.com/ora/standards/issues/new"
"newIssueUrl": "https://github.com/ora/standards/issues/new",
"specDriftStatusUrl": "https://raw.githubusercontent.com/ora/standards/spec-drift-status/spec-drift.json"
},
"staleAfterDays": 90,
"specDrift": {
"changed": "The spec changed after this check.",
"seeChange": "See what changed"
},
"footer": {
"about": "The Agentic-Web Cheatsheet: an open, community-edited guide. Every page is a file in a public repository, so anyone can fix a sentence or add a standard with a pull request.",
"maintainedBy": {
Expand Down
16 changes: 16 additions & 0 deletions content/standards/_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ related:
note: How the two relate, in one sentence.
# The day you checked every claim on this page against its sources.
last_verified: "2027-01-01"
# Where the spec lives, so a weekly check can tell you when it changes. Each
# pin is the exact state you read: the check opens an issue when the live
# spec no longer matches it. Kinds (see scripts/spec-drift.mjs):
# git-path repo + path: a file or folder in a Git repository
# git-head repo: any change anywhere in a small, spec-only repository
# git-dated-dirs repo + path: a newer dated revision folder, such as spec/2026-04-17
# git-latest-tag repo + match: a newer release tag
# ietf-draft name: a new revision of an IETF draft
# page url: a spec that is only a web page
# Get the pin of a new source by running `node scripts/spec-drift.mjs check
# spec-pins.json --only <id>` with any placeholder pin, and copy the live value.
spec_sources:
- kind: git-path
repo: example/spec
path: spec.md
pin: "0000000000000000000000000000000000000000"
---

## How it works
Expand Down
9 changes: 9 additions & 0 deletions content/standards/acp.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ related:
- id: x402
note: Payment rails like MPP and x402 can sit underneath.
last_verified: "2026-10-19"
spec_sources:
- kind: git-dated-dirs
repo: agentic-commerce-protocol/agentic-commerce-protocol
path: spec
pin: "2026-04-17"
- kind: git-path
repo: agentic-commerce-protocol/agentic-commerce-protocol
path: spec/2026-04-17
pin: "f90eae69b2cb6e93746436ab8c81fbc356e398b6"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/ai-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ related:
- id: nlweb
note: A catalog can list an NLWeb endpoint.
last_verified: "2026-10-19"
spec_sources:
- kind: git-path
repo: Agent-Card/ai-catalog
path: specification
pin: "e020b2db26f8f13e904a95e0e457457dd3da9efd"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/ap2.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ related:
- id: mpp
note: Another way to actually move the money.
last_verified: "2026-10-19"
spec_sources:
- kind: git-path
repo: google-agentic-commerce/AP2
path: docs/ap2/specification.md
pin: "80a6dd354d2062a512281e32df42b0233af27c8f"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/ard.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ related:
- id: ai-catalog
note: Its predecessor, now the publishing format. ARD is the discovery format. Serve the same file at both addresses.
last_verified: "2026-10-19"
spec_sources:
- kind: git-path
repo: ards-project/ard-spec
path: spec
pin: "40151d4a76af2e266723f78d9142f54a89fb5dcf"
---

## How it works
Expand Down
4 changes: 4 additions & 0 deletions content/standards/auth-md.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ related:
- id: web-bot-auth
note: Web Bot Auth proves which bot is asking. auth.md creates the account it acts for.
last_verified: "2026-10-19"
spec_sources:
- kind: git-head
repo: workos/auth.md
pin: "b53c9edfbfeea679b617727ebca9ba436bade794"
---

## How it works
Expand Down
4 changes: 4 additions & 0 deletions content/standards/mpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ related:
- id: ap2
note: AP2 records what a person approved; MPP moves the money.
last_verified: "2026-10-19"
spec_sources:
- kind: ietf-draft
name: draft-httpauth-payment
pin: "01"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/nlweb.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ related:
- id: ai-catalog
note: An AI catalog (or ARD file) can list your NLWeb endpoint.
last_verified: "2026-10-19"
spec_sources:
- kind: git-path
repo: nlweb-ai/website
path: NLWEBSPEC.md
pin: "b62cf132e689a0e7083b87fde8ff083a1d853b8e"
---

## How it works
Expand Down
10 changes: 10 additions & 0 deletions content/standards/poppy.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ related:
- id: ap2
note: Payments are not covered yet. Until they are, payment consent needs another protocol, such as AP2.
last_verified: "2026-10-19"
spec_sources:
- kind: page
url: https://personalagentprotocol.org/docs/spec
pin: "1c12afb072eb6e9b3ef221132248a6d5be8f43ef863614cc4a8663fe25ab09d5"
- kind: page
url: https://personalagentprotocol.org/docs/extensions/operations
pin: "ce2bd8aca3f71832a50539f35f26227c81b4834099f97f1e05eb5838ca38184e"
- kind: page
url: https://personalagentprotocol.org/docs/guides
pin: "8b7ec9262ae7664f146f3945c8a5f165a39c575ed5931532dbc284d475f563b6"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/ucp.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ related:
- id: poppy
note: May overlap with UCP on sign-in.
last_verified: "2026-10-19"
spec_sources:
- kind: git-latest-tag
repo: Universal-Commerce-Protocol/ucp
match: ^v\d{4}-\d{2}-\d{2}$
pin: "v2026-08-25"
---

## How it works
Expand Down
4 changes: 4 additions & 0 deletions content/standards/web-bot-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ related:
- id: x402
note: Cloudflare plans to use them together for pay-per-crawl.
last_verified: "2026-10-19"
spec_sources:
- kind: ietf-draft
name: draft-ietf-webbotauth-httpsig-protocol
pin: "00"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/webmcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ related:
- id: ucp
note: Shopify puts WebMCP tools on every Liquid storefront, next to its UCP support.
last_verified: "2026-10-19"
spec_sources:
- kind: git-path
repo: webmachinelearning/webmcp
path: index.bs
pin: "bd81b6d06c4d22bd15c14849030bbeb2fb14d9cd"
---

## How it works
Expand Down
5 changes: 5 additions & 0 deletions content/standards/x402.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ related:
- id: web-bot-auth
note: Cloudflare plans to use them together for pay-per-crawl.
last_verified: "2026-10-19"
spec_sources:
- kind: git-path
repo: x402-foundation/x402
path: specs
pin: "fd92aa2f26e0b20985479ef43ab54cb28b555cf2"
---

## How it works
Expand Down
Loading
Loading