Skip to content

[Agentic Web] ard-schema not updated for ARD v0.91 #17251

Description

@frmoretto

Submission Type

  • Feedback / Bug Report on Existing Audit

Audit Name / Affected Audit

ard-schema (Agent Resource Discovery / ARD)

Description & Rationale

Summary. The ARD audit in this repository follows ARD as it stood in June 2026:
its bundled conformance validator is a faithful port of the upstream conformance
test at that date, and its gatherer looks for the well-known path ARD specified at
that date.

ARD v0.91, published in August, changed three things: the well-known path a
consumer must fetch, the schema a manifest is validated against, and the severity
of a root collections array.

The pinned upstream commit was then advanced past v0.91 twice, both times inside a
routine "deps: upgrade deps" PR (#17206, #17245), and the ported code was never
adapted to the changes the pin had just crossed.

Two consequences, both reproduced below: a site that follows the current specification
is invisible to this audit, and a manifest that ARD considers valid scores 0.

Both confirmed at current HEAD (08c05cdee7e191719480477056bd145abcb09c17,
2026-09-18, 13.5.0) by reading source and reproducing locally.

Defect 1: the audit looks for the wrong discovery path.

Seeing this needs no fixture. The audit's title is "ai-catalog.json schema is
valid"
. Its description reads

"Valid ai-catalog.json manifests are required for autonomous AI agents and
registries to discover and verify your resources."

and that description links to the ARD specification.

Follow the link to §5.1 "Discovery Mechanisms," the paragraph headed
"Consumer resolution (normative)", quoted verbatim and in full from
the published page (confirmed identical to spec/ard.md at the currently
pinned upstream commit):

"A consumer resolving a domain's entries MUST fetch /.well-known/ard.json,
and MUST honour a rel="ard" link. ARD's predecessor specified the path
/.well-known/ai-catalog.json and the link relation ai-catalog; a consumer
MAY additionally consult these, and a consumer that does treats them as
equivalent entry sources. Consulting the predecessor names is a courtesy to
resources published before this revision, not a conformance requirement: a
consumer that resolves only ard.json and rel="ard" is fully conformant."

The audit's own description says the opposite of the document it cites: it calls
the predecessor name required, where the linked specification says a consumer
needs it only as a courtesy.

Confirmed in source at HEAD, core/gather/gatherers/agentic/ard.js:

  • document.querySelector('link[rel~="ai-catalog" i]')
  • parsed.get('rel', 'ai-catalog') (HTTP Link header)
  • new URL('/.well-known/ai-catalog.json', finalDisplayedUrl) (fallback)

Never rel="ard", never /.well-known/ard.json. A publisher who follows the
spec's own MUST and serves only /.well-known/ard.json is invisible to this
audit: it reports notApplicable, not a failure, so nothing in the report says
why.

Defect 2: a manifest ARD considers valid scores 0.

A manifest carrying a collections array at its root fails this audit outright.
Upstream, at the commit this port currently pins, says that manifest is valid:

"Top-level collections were removed in ADR-0003; ARD ignores unrecognized
top-level members, so this does not invalidate the manifest, but hierarchies
should be modeled inside 'entries'."

There are two independent causes, and fixing either one alone still scores 0. All
four states were run on the same fixture: as shipped, severity fixed alone, schema
fixed alone, both fixed. Scores 0, 0, 0, 0.9.

  1. Severity. third-party/ard/ard.js calls add_error for the collections
    check where upstream calls add_warning. core/audits/agentic/ard-schema.js
    sets score = 0 on any Error before it tests for Low severity, so the check
    never reaches the audit's existing Low severity path, which scores 0.9 instead
    of failing (the behaviour specified for this audit's non-fatal issues in
    core(ard): add support for partial scores audit results #17181).
  2. Schema. The bundled validator is compiled from the predecessor schema file,
    which is additionalProperties: false, so any unrecognized root member is a
    hard Error, while the reference conformance test validates against a definition
    that is deliberately open. Reasoning and file names in the collapsed section.

Each cause is sufficient on its own to hold the score at 0, which is why the two
fixes have to ship together.

Target Agents

Not applicable: this is a conformance and scoring defect in an existing audit, not
a new-signal proposal.

Proposed Implementation / Suggested Improvement

  1. core/gather/gatherers/agentic/ard.js: check /.well-known/ard.json (and
    rel="ard", Link: rel="ard") first, falling back to the existing
    ai-catalog paths, matching the spec's own MUST/MAY order in §5.1. The audit's
    title, failureTitle and description strings need the same correction.
  2. third-party/ard/ard.js: change the collections-at-root check from
    add_error to add_warning, so it lands on the existing Low severity path.
  3. build/build-ard-schema.js: compile the validator from
    spec/schemas/ard-entry.schema.json's $defs.ArdManifest, as the reference
    conformance test does, instead of ai-catalog.schema.json.
    core/scripts/update-ard-spec.js fetches the predecessor schema by hardcoded
    path and needs the same change to stay correct.

Items 2 and 3 are one fix. Both were also measured separately, and each on its own
still scores 0: either alone leaves the audit failing a manifest the specification
says is valid.

Example URL

None: Defect 1 is visible in any report that ran this audit, and both defects are
reproduced with a local fixture in the collapsed section below.

Reproduction, schema detail, pin history, and why the weekly sync check did not catch either

Defect 2, cause 2: the schema, in full

build/build-ard-schema.js compiles
third-party/ard/spec/schemas/ai-catalog.schema.json, which is
additionalProperties: false, so any unrecognized root member is a schema
Error. That is precisely what the second half of the upstream sentence quoted
above, "ARD ignores unrecognized top-level members", says must not happen.

Upstream's conformance test does not open that file. It validates the manifest
against spec/schemas/ard-entry.schema.json's $defs.ArdManifest, whose own
description reads "any other top-level members are transport-defined and ignored
by ARD, so additionalProperties is open"
, and which is additionalProperties: true. ai-catalog.schema.json is still present upstream; it is the predecessor
artefact, and the conformance test stopped using it in v0.91. The port reads it
because that is what upstream read in June 2026.

So the effect is broader than collections: any unrecognized root member fails
this audit, and the specification says ARD ignores all of them.

Why the weekly ARD sync check (#17171) did not catch either

core/scripts/update-ard-spec.js has RELEVANT_PREFIXES = ['spec/schemas/', 'conformance/']. Defect 1 lives in spec/ard.md, outside that list by
construction, so no upstream change to the discovery rules can raise a flag at
all.

Defect 2 lives in conformance/bin/conformance-test, which the check does watch,
so upstream's change of 2026-08-26 was visible. But the check's output is a diff
printed for a human to read. Step 2 of this port's own documented update procedure
is:

"Review the printed diff of conformance/bin/conformance-test and adapt
third-party/ard/ard.js to match any updated validation rules"

Nothing enforces that step, and the one file the script updates automatically is
hardcoded: it fetches spec/schemas/ai-catalog.schema.json, the predecessor the
conformance test no longer uses. The mechanism advances the pin and refreshes the
wrong schema, and nothing prompts revisiting ard.js.

Reproduction: source at HEAD, Chrome 153 headless, local, not PageSpeed Insights

Environment: Lighthouse 13.5.0 at commit 08c05cdee7e191719480477056bd145abcb09c17
(2026-09-18), run against Chrome 153.0.8010.53 headless (--headless=new). That
is the environment this reproduction was run in, with nothing implied about any
other runner's version.

git clone https://github.com/GoogleChrome/lighthouse.git
cd lighthouse
git checkout 08c05cdee7e191719480477056bd145abcb09c17   # or main
yarn install --frozen-lockfile
yarn build-report

Config used for every run (ard-only-config.js):

export default {
  extends: 'lighthouse:default',
  settings: {onlyAudits: ['ard-schema']},
};

Fixture manifest, spec-compliant, no collections (manifest-clean.json):

{
  "specVersion": "1.0",
  "entries": [{
    "identifier": "urn:air:example.com:catalog:main-agent",
    "displayName": "Example Agent Catalog",
    "type": "application/ai-catalog+json",
    "url": "https://example.com/agents/main",
    "representativeQueries": ["find agents on example.com", "list the example.com catalog"]
  }]
}

Any minimal static server works, serving that file at exactly one well-known
path per run, plus a bare / page.

Scenario A, spec-compliant, /.well-known/ard.json only (the MUST path):

node cli/index.js http://127.0.0.1:8093/ --config-path=ard-only-config.js \
  --output=json --output-path=result-A.json --chrome-flags="--headless=new" --quiet

Result: {"id": "ard-schema", "score": null, "scoreDisplayMode": "notApplicable"}.
A fully valid manifest, at the path the linked specification says a conformant
consumer MUST fetch, is invisible to the audit.

Scenario B, identical manifest, /.well-known/ai-catalog.json only (the
predecessor path):

Same command against a server serving the byte-identical file at the old path.
Result: {"id": "ard-schema", "score": 1, "scoreDisplayMode": "binary"}. Same
bytes, correctly detected and scored, once served at the path the spec demoted.

Scenario C, collections: [] added at root, served at
/.well-known/ai-catalog.json (so the audit runs). Run in four states:

(a) as shipped, score 0

{"id": "ard-schema", "score": 0, "scoreDisplayMode": "binary",
 "details": {"items": [
   {"element": "Root", "issue": "JSON Schema Validation Failed: must NOT have additional properties at path 'root'", "severity": "Error"},
   {"element": "Root", "issue": "Deprecated field check: Found 'collections' array at root. Top-level collections were REMOVED in ADR-0003. Catalog hierarchies MUST be modeled inside 'entries' using 'type: application/ai-catalog+json'.", "severity": "Error"}
 ]}}

(b) add_error changed to add_warning on the collections check, nothing
else
, still score 0

{"id": "ard-schema", "score": 0, "scoreDisplayMode": "binary",
 "details": {"items": [
   {"element": "Root", "issue": "JSON Schema Validation Failed: must NOT have additional properties at path 'root'", "severity": "Error"},
   {"element": "Root", "issue": "Deprecated field check: Found 'collections' array at root. ...", "severity": "Low"}
 ]}}

core/audits/agentic/ard-schema.js sets score = 0 on any Error before testing
for Low severity, so the schema Error alone holds the score at 0.

(c) validator compiled from ard-entry.schema.json's $defs.ArdManifest, the
collections check left as add_error
, score 0

{"id": "ard-schema", "score": 0, "scoreDisplayMode": "binary",
 "details": {"items": [
   {"element": "Root", "issue": "Deprecated field check: Found 'collections' array at root. Top-level collections were REMOVED in ADR-0003. Catalog hierarchies MUST be modeled inside 'entries' using 'type: application/ai-catalog+json'.", "severity": "Error"}
 ]}}

The schema Error is gone and the remaining Error is the collections check itself,
which is enough to keep the score at 0. Together with (b) this is why items 2 and 3
of the proposal are one fix: neither is sufficient alone.

(d) both, (b) plus (c), score 0.9

{"id": "ard-schema", "score": 0.9, "scoreDisplayMode": "binary",
 "details": {"items": [
   {"element": "Root", "issue": "Deprecated field check: Found 'collections' array at root. ...", "severity": "Low"}
 ]}}

For the schema change in (c) and (d), build/build-ard-schema.js was pointed at
ard-entry.schema.json with
schema['$ref'] = '#/$defs/ArdManifest', the same variant construction upstream's
as_def("ArdManifest") performs, then node build/build-ard-schema.js. One
incidental adjustment was needed: the build script strips
require("ajv-formats/dist/formats") out of the standalone output by matching the
literal variable names formats0 and formats10, and Ajv numbers those per
schema, so the rewrite has to be generalised to formats\d+ or the generated
module throws require is not defined on import.

Regression check. Scenario B (clean manifest, no collections) re-run in state
(d): {"score": 1, "scoreDisplayMode": "binary"}, zero detail items. The schema
change does not affect the clean case.

node core/test/scripts/run-mocha-tests.js third-party/ard/ard-test.js in state
(d): 12 passing, 1 failing, "fails with Error when root contains deprecated
collections field (ADR-0003)"
. That test asserts the current behaviour and would
move from tester.errors to tester.warnings as part of the fix.

All local edits were then reverted; git status clean at
08c05cdee7e191719480477056bd145abcb09c17.

Upstream pin history

third-party/ard/README.md, against the upstream content at each pinned commit:

pinned commit upstream date collections manifest validated against ard.json in spec
(none, #17168 added the audit) n/a n/a n/a n/a
47042e5c (#17171) 2026-06-26 add_error ai-catalog.schema.json no
aa3e598 (#17206) 2026-08-26, "Publish ARD v0.91" add_warning ard-entry.schema.json yes
b76f235a (#17245) 2026-09-12 add_warning ard-entry.schema.json yes

The port matches the first populated row in every column.

Existing issues checked

Searched open and closed issues for ard, ai-catalog, agentic,
agent-discoverability, well-known, "ard.json", "rel=ard",
"well-known/ard". No open or closed issue reports either defect. Two closed
issues are adjacent and were read in full before concluding this: #17082 (notes
the Chromium-146-vs-Chrome-150 mismatch, but the confirmed root cause was a
2-second fetch timeout, unrelated to either defect here) and #17194 (a false
negative on llms.txt caused by server-side cloaking, also unrelated to
ard-schema). Neither addresses the ARD discovery path or the collections
severity.

The audit's own history, for context

The ard-schema audit was first proposed in #17158 (opened 2026-08-06), which was
closed without merging. The port that is live today merged in #17168 (2026-08-31),
carrying no upstream pin at all; the pin was introduced minutes later by #17171 and
has moved twice since.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions