You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
5-agent review of today's spec ship (manifest schema, error catalog
port, conformance YAMLs, §8.6 provider self-service) produced
findings across two review passes. Pass 1 (10 findings) + pass 2
(4 follow-up regressions) all closed in this commit + the immediate
predecessors. This commit handles pass 3 — the documentation gaps
the pass-2 review surfaced.
## Pass 3 fixes (this commit)
### NEW-EC-1: §8.6 referenced 3 undefined error codes + 1 wrong code
`spec/04-manifest.md §8.6` cited `DNS_VERIFICATION_FAILED`,
`ROTATION_24H_CAP`, `ROTATION_RACE` as error codes — none existed
in the canonical error spec. Also cited `AGENT_NOT_FOUND` where
the correct Provider-side code is `PROVIDER_NOT_FOUND`.
Resolution: added the 3 missing codes to `spec/03-errors.md` as a
new §3.9 "Provider Self-Service Endpoints (Stage 3)". Each carries
HTTP status, retry-safety, `details` shape, and recovery prose
consistent with the rest of the catalog. §8.6 now deep-links to
the canonical §3.9 anchors instead of dangling at
`COVERAGE-PLAN.md`. `AGENT_NOT_FOUND` replaced with
`PROVIDER_NOT_FOUND` throughout §8.6.
### NEW-C-1: conformance YAML cited non-existent error codes
`conformance/v1.0/JECP-PROVIDER-MUST-AUTH-REQUIRED.yaml` description
block listed `INVALID_PROVIDER_KEY`, `MISSING_AUTH`, `UNAUTHORIZED`
as example auth-failure codes. None are in `spec/03-errors.md`.
Replaced with canonical `AUTH_REQUIRED` and `INVALID_API_KEY`.
### NEW-X-1: internal project nomenclature in public spec
`§8.6.2` and `§8.6.3` carried JobDoneBot-internal references like
"S1 / TIER A.1 fix", "Sprint 6 TIER A.4 fix", and the Rust
function name `rotate_provider_api_key_atomic`. Third-party
implementers reading the spec can't look these up. Stripped them;
the MUST/SHOULD prose stands on its own.
### NEW-X-2: stale "JSON Schema below" comment
`spec/04-manifest.md` line 51 still had the YAML comment "JSON
Schema below" — the inline JSON Schema was replaced with a pointer
in pass 1's S-4 fix. Updated the comment to point at the canonical
schema file.
## Coverage plan updated
`spec/error-catalog/COVERAGE-PLAN.md` now lists the 3 new codes
(DNS_VERIFICATION_FAILED, ROTATION_24H_CAP, ROTATION_RACE) plus
PROVIDER_NOT_FOUND for v1.0.3 catalog backfill. The catalog pages
themselves are deferred to v1.0.3 per the existing coverage plan.
## Out of scope (intentional)
`conformance/v1.0/JECP-PROVIDER-MUST-AUTH-REQUIRED.yaml` notes
block (line 68) still mentions "Sprint 6 review" — non-normative
author commentary, not a public-facing claim. Will sweep in a
future commit if the broader policy is "no internal nomenclature
in any spec-repo file."
## Validation
```
YAML: 44 OK / 0 BAD
schema: valid against Draft 2020-12 metaschema
fixture: validates against canonical manifest schema
```
All 44 conformance YAML files parse, the JSON Schema validates as
a Draft 2020-12 document, and the minimal-valid fixture validates
against the canonical schema. No regressions from the prior pass-1
fixes.
When an assertion declares `preconditions.env_required`, the runner MUST check that every named env var is defined and non-empty BEFORE executing the request. If any required env var is missing, the runner reports the assertion as **SKIPPED** (distinct from PASSED and FAILED) in both the JUnit XML output (`<skipped/>` child element) and the Markdown report. A SKIPPED assertion does NOT contribute to the conformance pass-rate calculation.
33
+
34
+
This addition lets us tighten assertions whose validation path is gated by external state (e.g., a valid Provider key) without falling back to permissive `status_in` ranges that pass for the wrong reason.
35
+
26
36
The runner is `scripts/jecp-conformance.sh` (Phase 0 / c10) — a bash + python3 harness that walks this directory, executes each assertion against `${TARGET}` (default `https://jecp.dev`), and emits JUnit XML + Markdown report. The same logic ships as `npx @jecpdev/conformance` and `docker run ghcr.io/jecpdev/conformance:v1.x` in Phase 2.
27
37
28
38
## Status
@@ -49,7 +59,7 @@ NUMBER = zero-padded sequence within (AREA, LEVEL), e.g., 001, 002
Copy file name to clipboardExpand all lines: conformance/v1.1/INDEX.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ Each assertion is a single YAML file at `conformance/v1.1/<id>.yaml` with a self
8
8
9
9
Fixture references inside YAMLs:
10
10
-`${fixtures.x402.<name>}` — placeholder consumed by the conformance harness, resolved from the JSON files in this directory.
11
-
-`conformance/v1.1/fixtures/<file>` — direct relative path (used only by the rc3 keeper assertions for `idempotency-pull-event.json` and `facilitator-fleet-eoas.txt`). These fixture files are not yet committed; they will land alongside the keeper-rc3 fixture batch.
11
+
-`conformance/v1.1/fixtures/<file>` — direct relative path (used by the rc3 keeper assertions for `idempotency-pull-event.json` and `facilitator-fleet-eoas.txt`). Both fixtures are committed as PLACEHOLDER (2026-05-16) — `idempotency-pull-event.json` uses a deterministic `tx_hash` and a `${TEST_AGENT_EOA}` substitution placeholder for `payer`; `facilitator-fleet-eoas.txt` lists known operators behind `#`-prefixed comment lines pending BaseScan verification (see `feedback_verify_facilitator_on_chain.md` in the JobDoneBot release-prep notes). The harness MAY run the YAMLs that reference them, but the keeper batch is only declared "runnable for GA gate" once the comment-only EOA lines are uncommented after on-chain verification.
12
12
13
13
## Assertion list
14
14
@@ -63,10 +63,10 @@ JSON fixtures resolved via `${fixtures.x402.<name>}`:
-`conformance/v1.1/fixtures/idempotency-pull-event.json` — deterministic pull-event fixture for replay test
69
-
-`conformance/v1.1/fixtures/facilitator-fleet-eoas.txt` — observed facilitator EOA list (Coinbase x402 Facilitator 1-8, Canza, Daydreams, X402rs, plus newly-observed entries)
68
+
-`conformance/v1.1/fixtures/idempotency-pull-event.json` — deterministic pull-event fixture for replay test (PLACEHOLDER `payer` uses `${TEST_AGENT_EOA}` substitution)
69
+
-`conformance/v1.1/fixtures/facilitator-fleet-eoas.txt` — observed facilitator EOA list (Coinbase x402 Facilitator 1-8, Canza, Daydreams, X402rs, plus newly-observed entries). All entries currently `#`-commented; uncomment after BaseScan verification per `feedback_verify_facilitator_on_chain.md`.
"_comment_status": "PLACEHOLDER fixture. The chain_id targets Base Sepolia (84532) for safety. The tx_hash + payer + amount values are deterministic stand-ins so the conformance harness can produce reproducible runs. Replace `payer` with the actual test agent's EOA at runtime, OR rely on the harness to fill it from `${TEST_AGENT_EOA}`. Authored 2026-05-16 for the rc3 keeper batch (ADR-0003 Am-7).",
4
+
"_comment_usage": "Referenced by conformance/v1.1/X402_RECORD_SETTLEMENT_IDEMPOTENT.yaml via the `x402_pull_fixture: idempotency-pull-event` directive. The harness loads this file, substitutes ${TEST_AGENT_EOA} into `payer`, and submits the resulting event TWICE to assert the keeper rejects the duplicate on (chain_id, tx_hash, log_index).",
Copy file name to clipboardExpand all lines: schemas/v1/manifest.schema.json
+14-4Lines changed: 14 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -225,8 +225,8 @@
225
225
},
226
226
"action": {
227
227
"type": "string",
228
-
"pattern": "^[a-z][a-z0-9-]*$",
229
-
"description": "Action id within the target capability."
228
+
"pattern": "^[a-z][a-z0-9-]{2,63}$",
229
+
"description": "Action id within the target capability. MUST match the Action.id pattern (3-64 chars, [a-z][a-z0-9-])."
230
230
},
231
231
"input": {
232
232
"type": "object",
@@ -279,12 +279,13 @@
279
279
280
280
"Authentication": {
281
281
"type": "object",
282
+
"description": "Auth scheme for inbound Hub-forwarded calls. v1.0 supports `api_key` only (HMAC-signed header). v1.1+ will add `mtls` and `oauth2` with per-type subschemas.",
282
283
"required": ["type"],
283
284
"additionalProperties": false,
284
285
"properties": {
285
286
"type": {
286
-
"enum": ["api_key", "mtls", "oauth2"],
287
-
"description": "Auth scheme the Provider expects on inbound Hub-forwarded calls. v1.0 reference Hub implements only api_key (HMAC-signed header)."
287
+
"enum": ["api_key"],
288
+
"description": "Auth scheme the Provider expects on inbound Hub-forwarded calls. v1.0 supports `api_key` only. `mtls` and `oauth2` are reserved for v1.1+ where per-type subschemas will land."
288
289
},
289
290
"header_name": {
290
291
"type": "string",
@@ -379,6 +380,15 @@
379
380
"pattern": "^\\d+\\.\\d+\\.\\d+$",
380
381
"description": "Semver of the replacement version. Surfaced as details.successor in CAPABILITY_DEPRECATED responses."
Copy file name to clipboardExpand all lines: spec/03-errors.md
+29Lines changed: 29 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -498,6 +498,35 @@ The `details.documentation_url` field, when present, is a deep-link of the form
498
498
499
499
A Hub MUST NOT emit `details.subcause` until after it has authenticated the agent (via `X-API-Key` or equivalent). This prevents the subcause registry from acting as an enumeration oracle for unauthenticated callers — same rule as `PROVENANCE_MISMATCH` (§3.1).
500
500
501
+
### 3.9 Provider Self-Service Endpoints (Stage 3)
502
+
503
+
These error codes fire only on the Provider-admin endpoints defined in 04-manifest.md §8.6 (`POST /v1/providers/verify-dns`, `POST /v1/providers/me/rotate-key`). They are never emitted on agent-facing wire calls (`POST /v1/invoke`, etc.). Hubs that do not yet implement Stage 3 (third-party Provider acceptance) never emit these codes.
504
+
505
+
#### `DNS_VERIFICATION_FAILED`
506
+
507
+
-**HTTP**: 422
508
+
-**Cause**: The Provider invoked `POST /v1/providers/verify-dns` (04-manifest.md §8.6.2) but the Hub could not find a matching `_jecp.<domain>` TXT record carrying `jecp-verify=<token>` against the domain extracted from `provider.endpoint_url`. Either the record is absent, the token mismatches, or DNS propagation has not completed.
509
+
-**Retry-safe**: Yes (after the Provider publishes / corrects the TXT record and DNS propagates)
-**Recovery**: Publish the TXT record per 04-manifest.md §8.2, wait for propagation (typically < 5 minutes for low-TTL zones), then re-call `POST /v1/providers/verify-dns`.
512
+
513
+
#### `ROTATION_24H_CAP`
514
+
515
+
-**HTTP**: 429
516
+
-**Cause**: The Provider invoked `POST /v1/providers/me/rotate-key` (04-manifest.md §8.6.3) but has already rotated the maximum number of times allowed in a sliding 24-hour window (default 3). The request has NO effect on the existing key.
517
+
-**Retry-safe**: Yes (after the oldest rotation in the window ages out)
518
+
-**Headers (SHOULD)**: `Retry-After: <integer-seconds>` indicating when the next rotation slot opens.
-**Recovery**: Wait until `next_slot_at`. Operators MAY adjust the cap via Hub configuration; agents/Providers cannot.
521
+
522
+
#### `ROTATION_RACE`
523
+
524
+
-**HTTP**: 409
525
+
-**Cause**: The Provider invoked `POST /v1/providers/me/rotate-key` but the Provider record was modified or deleted by a concurrent administrative action mid-transaction (e.g., Provider deleted, or another rotation racing with row-level lock contention). No new key is issued and the existing key is unchanged.
526
+
-**Retry-safe**: Yes (re-issue the call once the contending operation completes)
-**Recovery**: Retry the call after a brief backoff (100-500 ms). If `provider_disappeared`, the Provider record has been removed and the Provider must re-register.
529
+
501
530
## 4. `next_action` Object
502
531
503
532
Errors that have a clear recovery path SHOULD include a `next_action` object with machine-readable guidance.
0 commit comments