diff --git a/README.md b/README.md index 6c78a29..d241694 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,26 @@ Agent Cards v1.1.0 is intentionally flat: - current cards bind **directly** to `commandlayer.org` mirror URLs - current v1.1.0 uses **no `_shared`** +## Validation and release trust + +The default reviewer path is the current canonical line. + +```bash +npm install +npm run validate +``` + +Validation commands are intentionally split by authority surface: + +- `npm run validate` — release-facing validation for the current canonical line; runs current discovery validation, current v1.1.0 card validation, checksum verification, and typecheck +- `npm run validate:current` — current discovery + current v1.1.0 cards only +- `npm run validate:legacy` — archival/compatibility validation for `agents/v1.0.0/` +- `npm run validate:checksums` — verifies `checksums.txt` against tracked release artifacts +- `npm run validate:release` — explicit alias for the full current release flow used by CI +- `npm run validate:cards` — runs both current and legacy card schema/path validation when a maintainer wants a full repository sweep + +`npm run validate` is the command reviewers should trust most because it centers the current release line instead of the compatibility archive. + ## How card bindings work For `v1.1.0`: diff --git a/agents/v1.0.0/commercial/verifyagent.eth.json b/agents/v1.0.0/commercial/verifyagent.eth.json index 4815c8c..ee557bb 100644 --- a/agents/v1.0.0/commercial/verifyagent.eth.json +++ b/agents/v1.0.0/commercial/verifyagent.eth.json @@ -42,6 +42,9 @@ "payments", "settlement", "protocol-reference" + ], + "notes": [ + "Legacy commercial v1.0.0 cards are preserved without mirror URLs because no canonical IPFS mirror binding was recorded for this line." ] }, "networks": [ diff --git a/scripts/validate-cards.mjs b/scripts/validate-cards.mjs index b582080..a859b5a 100644 --- a/scripts/validate-cards.mjs +++ b/scripts/validate-cards.mjs @@ -16,6 +16,12 @@ const expectedV11 = { commercial: commercialVerbs.map((verb) => `${verb}agent.eth.json`) }; +const VALIDATION_MODES = new Set(["current", "legacy", "all"]); +const CURRENT_DESCRIPTOR_PATHS = [ + ".well-known/agent.json", + ".well-known/agent-cards-v1.1.0.json" +]; + const ajv = new Ajv2020({ strict: true, allErrors: true }); addFormats(ajv); @@ -110,15 +116,21 @@ function validateCard(fullPath) { const primaryVerb = card.implements[0]; const expectedId = `https://commandlayer.org/agent-cards/${relativePath}`; const expectedEntry = `x402://${card.ens}/${primaryVerb}/v${card.version}`; - const semverFolder = folderVersion.replace(/^v/, ""); + const expectedSchema = "https://commandlayer.org/agent-cards/schemas/v1.1.0/agent.card.schema.json"; + const rawCommons = /^https:\/\/raw\.githubusercontent\.com\/commandlayer\/protocol-commons\/refs\/tags\/v1\.1\.0\/schemas\/v1\.1\.0\/commons\/([^/]+)\/\1\.(request|receipt)\.schema\.json$/; + const rawCommercial = /^https:\/\/raw\.githubusercontent\.com\/commandlayer\/protocol-commercial\/refs\/tags\/v1\.1\.0\/schemas\/v1\.1\.0\/commercial\/([^/]+)\/\1\.(request|receipt)\.schema\.json$/; + const mirrorCommons = /^https:\/\/commandlayer\.org\/schemas\/v1\.1\.0\/commons\/([^/]+)\/\1\.(request|receipt)\.schema\.json$/; + const mirrorCommercial = /^https:\/\/commandlayer\.org\/schemas\/v1\.1\.0\/commercial\/([^/]+)\/\1\.(request|receipt)\.schema\.json$/; - if (card.version !== semverFolder) fail(`${relativePath}: version mismatch.`); + if (card.version !== folderVersion.replace(/^v/, "")) fail(`${relativePath}: version mismatch.`); if (card.$id !== expectedId) fail(`${relativePath}: $id mismatch.`); + if (card.$schema !== expectedSchema) fail(`${relativePath}: stale or invalid $schema.`); if (card.entry !== expectedEntry) fail(`${relativePath}: entry mismatch.`); if (card.id !== card.ens) fail(`${relativePath}: id must equal ens.`); if (card.class !== tier) fail(`${relativePath}: class mismatch.`); if (fileName !== `${card.ens}.json`) fail(`${relativePath}: filename mismatch.`); if (new Date(card.updated_at).getTime() < new Date(card.created_at).getTime()) fail(`${relativePath}: updated_at must be >= created_at.`); + if (JSON.stringify(card).includes("_shared")) fail(`${relativePath}: current v1.1.0 card must not reference _shared.`); if (folderVersion === CURRENT_LINE) { const expectedSchema = `https://commandlayer.org/agent-cards/schemas/${CURRENT_LINE}/agent.card.schema.json`;