fix(cards): one catalog, one host, one primary key - #129
Merged
Merged
Conversation
`faf cards --target catalog` wrote {specVersion, entries} — AI Catalog
Level 1 "minimal". The pack API's projectAiCatalog, from the same .fafa,
named the host and reached Level 2 "discoverable". One projector, two
levels: the CLI's own catalog validated a level below its API's.
Both paths now derive the host once, in pack.ts. `displayName` is the
field that does the lifting — the validator takes an empty one as
*invalid*, not as minimal — so a .fafa that names nobody gets no host at
all: minimal and valid beats discoverable and broken. `identifier` rides
along when the .fafa says where it lives.
On a catalog faf shares, `host` is the one key it may add, and only to a
catalog that names none: a host already there is the site's own and is
left byte for byte, in the file's own layout.
Receipts (ai-catalog-cli v0.2.2, sha256-checked release binary):
fresh catalog → discoverable, valid
existing minimal one → minimal → discoverable, foreign row untouched
foreign host present → untouched, still theirs
second run → byte-identical
4 of the 7 new tests fail without this change; the other 3 guard what it
must not do. 2045/2045 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnHgQTeSEV6uATCGumM1nz
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Pre-launch, so the right identifiers matter more than the old ones.
The CLI keyed its rows off the homepage host and the raw `agent.name`;
the pack projector keys them off the domain the .fafa *declares*
(agent.id's urn:air, metadata.cards.domain, else the homepage) and the
handle. Same .fafa, two primary keys — and once the host was named, the
file contradicted itself: host `weather.acme.example`, rows
`urn:air:acme.example:…`.
The raw name was the worse half. `agent.name: Weather Bot` published
`urn:air:acme.example:a2a:Weather Bot` — a space inside a URN. The
validator passes it (it does not parse URN syntax), so it would have
shipped broken-but-valid, and an identifier is a catalog's primary key:
cheap to change now, breaking for every consumer later.
AI Catalog spec, Catalog Entry: `urn:air:{publisher}:{namespace}:{name}`
— publisher is "the domain name of the organization publishing the
artifact", name is "the stable, unique name". Host Info: identifier is
"a DID or domain name", so the bare host the CLI emits is the right
form.
A .fafa that names no domain is now refused in one line (exit 2) rather
than published as `urn:air:local:…` — the same stance as "will not
invent a door". The A2A card's passport URL comes off the same domain,
so a card and a catalog never disagree about where the .fafa lives.
Unchanged for any .fafa whose homepage host is its declared domain and
whose name is already a handle — faf.one's own included.
2047/2047 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnHgQTeSEV6uATCGumM1nz
`faf cards --target ard` writes `.well-known/ard.json`: the catalog rows,
carrying the search hints ARD reads from the .fafa
(`metadata.cards.keywords` -> tags, `metadata.cards.examples` ->
representativeQueries).
On the path: the spec prose and its own tool disagree, and the tool is
newer. ARD v0.9 §4.1/§6.1 name /.well-known/ai-catalog.json; the
conformance CLI v0.9.1 says "A consumer MUST fetch /.well-known/ard.json.
Consulting the predecessor /.well-known/ai-catalog.json is permitted
(MAY) but not required." Build ahead: write ard.json, and `--target
catalog,ard` writes both, as faf.one already serves both.
The document keeps specVersion + host. ARD ignores them ("top-level
members ignored by ARD (transport-defined)") but ai-catalog requires
specVersion and reads host, so one shape serves both specs — which is
what ARD §4 means by building on ai-catalog.
A manifest with no representativeQueries is valid and unfindable:
registries build their semantic index from that term. faf now says so in
one line and points at the .fafa key, rather than writing a card nobody
can find. The spec asks for 2-5.
Receipts (ard-spec conformance CLI v0.9.1, `manifest`):
hints in the .fafa -> PASS, 0 errors, 0 warnings
no hints -> PASS, 2 warnings, and faf printed the note first
And the receipt that settles the identifier change one commit back: the
identifier the CLI used to emit, `urn:air:acme.example:a2a:Weather Bot`,
fails ARD outright — three errors, "does not match RFC 8141 URN pattern".
The ai-catalog validator passed it. ARD does not.
2050/2050 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnHgQTeSEV6uATCGumM1nz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The CLI's
faf cards --target catalogand the pack API'sprojectAiCatalogprojected different catalogs from the same.fafa. Pre-launch, with nothing published yet, this fixes the projection rather than preserving it.1. The catalog names its host
faf cardswrote{specVersion, entries}— AI Catalog Level 1 "minimal". The pack API named the host and reached Level 2 "discoverable". Both paths now derive the host once.displayNameis the field that does the lifting. The validator takes an empty one as invalid, not as minimal, so a.fafathat names nobody gets no host at all — minimal and valid beats discoverable and broken. That also closes a latent hole on the pack path, which would have emitteddisplayName: ""for a vendorless.fafa.Spec, Host Info:
displayNameMUST;identifieris "a DID or domain name" — so the bare host the CLI emits is the right form.2. One primary key
With the host named, the file contradicted itself. Same
.fafa, before:weather.acme.exampleurn:air:acme.example:a2a:Weather Botweather.acme.exampleurn:air:weather.acme.example:a2a:weather-botThe CLI keyed rows off the homepage host and the raw
agent.name; the pack projector keys them off the domain the.fafadeclares and the handle. The raw name is the worse half — a space inside a URN. The validator passes it (it doesn't parse URN syntax), so it would have shipped broken-but-valid.Spec, Catalog Entry:
urn:air:{publisher}:{namespace}:{name}— publisher is "the domain name of the organization publishing the artifact", name is "the stable, unique name". An identifier is a catalog's primary key: cheap to change now, breaking for every consumer later.A
.fafathat names no domain is now refused in one line (exit 2) rather than published asurn:air:local:…— the same stance as "will not invent a door". The A2A card's passport URL comes off the same domain, so a card and a catalog never disagree about where the.fafalives.Unchanged for any
.fafawhose homepage host is its declared domain and whose name is already a handle — faf.one's own included.Receipts
ai-catalog-cliv0.2.2 (sha256-checked release binary), against files the CLI actually wrote:discoverable, validdiscoverable; a foreign row kept byte for byteunchanged.fafa(declared domain ≠ homepage, name with a space).fafawith no domainThe added
hostfollows the file's own layout — proven on a hand-written four-space catalog.Tests
2047/2047green · lint 0 errors. The load-bearing new test asserts the two projectors produce identical identifiers and URLs from one.fafa.🤖 Generated with Claude Code
https://claude.ai/code/session_01KnHgQTeSEV6uATCGumM1nz