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
42 changes: 19 additions & 23 deletions .agents/shortcuts/cut-minor-ga.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,31 +197,27 @@ marker to get unblocked. Without it, `npm run version` refuses to generate

16. **[H]** Redeploy the worker (`npm run deploy:cdn-artifacts-worker`) if
`main` changed it after step 9.
17. **[H]** Refresh the root `/schemas/index.json` and `/schemas/latest.json`.
No script path refreshes them today:
- release uploads use `--skip-latest`;
- deploys use `--latest-only`, which excludes the root indexes;
- `backfill-cdn-artifacts.sh` refuses `--version` without `--skip-latest`.

Pick one of two ways:
- land a reviewed tooling PR that adds a root-index-only mode before GA
day; or
- get explicit maintainer authorization for a tracked one-off under the
publication-authority rules in `RELEASING.md`. It uploads exactly these
two mutable files from the `v3.2.1` tag, with the headers the script
uses.

Never touch semver paths this way. The one-off, run with R2 credentials:
17. **[A]** The root `/schemas/index.json` and `/schemas/latest.json` refresh
themselves on the next successful `main` deploy after the release
artifacts land. `backfill-cdn-artifacts.sh --latest-only` rebuilds them
through `scripts/build-schema-discovery-pointers.cjs` from the releases
the bucket actually serves, and `deploy.yml` fails if the public documents
are not the ones it just uploaded (#8318). No one-off upload here.

Confirm. If an alias still trails, read the deploy log for the
`Schema versions missing from the CDN` warning — that names a release
whose `schemas/<version>/index.json` never reached R2, which is a release
recovery, not a pointer problem. To inspect without deploying:

```bash
git checkout v3.2.1
for file in index.json latest.json; do
aws s3 cp "dist/schemas/$file" "s3://$ADCP_ARTIFACT_R2_BUCKET/schemas/$file" \
--endpoint-url "$R2_ENDPOINT" --no-guess-mime-type \
--cache-control 'public, no-cache, must-revalidate' \
--content-type 'application/json; charset=utf-8'
done
curl -s https://adcontextprotocol.org/schemas/index.json | jq .latest_stable # "3.2.1"
npm run backfill:cdn-artifacts -- --bucket "$ADCP_ARTIFACT_R2_BUCKET" \
--build-latest --latest-only --aws-dry-run
```

```bash
curl -s https://adcontextprotocol.org/schemas/index.json \
| jq '{latest_stable, v3: .aliases.v3, "v3.2": .aliases["v3.2"]}'
# {"latest_stable": "3.2.1", "v3": "3.2.1", "v3.2": "3.2.1"}
```

## Phase 6: docs snapshot
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,19 @@ jobs:
echo "::error::Missing R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, or R2_SECRET_ACCESS_KEY secret."
exit 1
fi
npm run backfill:cdn-artifacts -- --bucket "${ADCP_ARTIFACT_R2_BUCKET}" --build-latest --latest-only --quiet
mkdir -p "${RUNNER_TEMP}/schema-pointers"
npm run backfill:cdn-artifacts -- --bucket "${ADCP_ARTIFACT_R2_BUCKET}" --build-latest --latest-only --quiet \
--pointer-output-dir "${RUNNER_TEMP}/schema-pointers"

# Uploading is not serving: R2 publication is progressive and per-object.
# #8318 went unnoticed for weeks because nothing compared the public
# discovery document against the release set it is supposed to name.
- name: Verify published schema discovery pointers
run: |
set -euo pipefail
node scripts/verify-schema-discovery.mjs \
--expected-index "${RUNNER_TEMP}/schema-pointers/index.json" \
--expected-latest "${RUNNER_TEMP}/schema-pointers/latest.json"

# On any failure (hard or fallback-with-degraded-gates), snapshot Fly
# state so the next-turn investigator doesn't have to fetch it
Expand Down
64 changes: 58 additions & 6 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,64 @@ requalify each dependent PR on that main, including combined tests and human rev
historical backfill or incident recovery.

Routine app deployment is authorized to rebuild and upload only
`schemas/latest/**`, `compliance/latest/**`, and `protocol/latest.tgz*`.
It uses `backfill-cdn-artifacts.sh --latest-only`; root version indexes and
all semver paths are excluded. The app itself serves committed `dist`, so a
committed package version without a published GitHub Release and its four
assets also blocks app deployment. This can delay app-only fixes while a
release needs recovery.
`schemas/latest/**`, `compliance/latest/**`, `protocol/latest.tgz*`, and the
root discovery pointers `schemas/index.json` and `schemas/latest.json`. It
uses `backfill-cdn-artifacts.sh --latest-only`; all semver paths are excluded.
The app itself serves committed `dist`, so a committed package version without
a published GitHub Release and its four assets also blocks app deployment.
This can delay app-only fixes while a release needs recovery.

The root pointers name versions, so routine deployment must not be able to
advertise one that release authority has not published.
`scripts/build-schema-discovery-pointers.cjs` enforces that mechanically:

- It starts from the regenerated `dist/schemas/index.json` and refuses a
source document whose aliases disagree with its own version list, whose
`$id`/`$schema`/`channel` are not the expected literals, or that carries a
malformed entry.
- `versions[]` keeps only the releases with a `schemas/<version>/` prefix in
one delimited bucket listing. A truncated listing is refused.
- An alias (`latest_stable`, `v3`, `v3.2`, …) may only resolve to a release
whose `schemas/<version>/index.json` object was confirmed with a HEAD. A
prefix appears as soon as release publication writes its first object, and
that publication is progressive and per-object, so prefix existence alone
would let a half-uploaded release win an alias under an immutable cache
policy. The deploy walks each line newest-first and stops at the first
confirmation — typically one HEAD per release line.
- Every URL in the published document is derived from a validated version
string; nothing but two prose fields is copied from the source.
- It refuses to move any pointer backwards relative to the copy currently
served. The one exemption is a release the source marks `withdrawn` or
`unpublished`, which is a deliberate editorial decision. Any other
regression means the CDN no longer serves a release it advertises: recover
that release, do not override the guard.

A release whose version directory never reached the CDN therefore keeps its
line pinned to the newest patch that is actually fetchable, and the deploy log
carries a `Schema versions missing from the CDN` warning naming the gap.

`scripts/verify-schema-discovery.mjs` then fetches the canonical public URLs
and fails the deploy unless they serve byte-for-byte what this run uploaded —
uploading is not serving. Note the scope: it proves the public document is
this run's, not that the document names the newest release of every line. A
line pinned one release back by a CDN gap passes, and the warning above is the
signal for that.

`npm run backfill:cdn-artifacts -- --bucket "$ADCP_ARTIFACT_R2_BUCKET"
--build-latest --latest-only --aws-dry-run` computes and prints both documents
against the live bucket without uploading anything, which is how to preview
pointer state during an incident.

Pointer currency follows the deploy, not the release: `release.yml` uploads
`schemas/<version>/` with `--version V --skip-latest` and still never touches
the pointers, so a new release is advertised by the next successful `main`
deploy that sees its prefix. Releases cut on a maintenance branch wait for the
forward merge into `main`.

Before [#8318](https://github.com/adcontextprotocol/adcp/issues/8318) no
workflow published these two objects at all: `--latest-only` excluded them and
`--version V --skip-latest` still does, so they froze at whatever the last
manual bulk backfill wrote while four releases shipped past them.

Only the release workflow publishes a new immutable version. It verifies the
current tested branch, the original merged release PR's final-head maintainer
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"deploy:cdn-artifacts-cutover": "wrangler deploy --config workers/artifact-cdn/wrangler.cutover.toml",
"deploy:cdn-artifacts-cutover:dry-run": "wrangler deploy --config workers/artifact-cdn/wrangler.cutover.toml --dry-run",
"verify:cdn-artifacts-cutover": "node scripts/verify-cdn-artifacts-cutover.mjs",
"verify:schema-discovery": "node scripts/verify-schema-discovery.mjs",
"typecheck": "tsc --project server/tsconfig.json --noEmit && npm run typecheck:fixed-trace-rollout-tests",
"typecheck:fixed-trace-rollout-tests": "node scripts/typecheck-fixed-trace-rollout-tests.mjs",
"test:schemas": "node tests/schema-validation.test.cjs && node --test tests/sync-platform-id.test.cjs tests/assignment-landing-page.test.cjs tests/assignment-trackers.test.cjs tests/outcome-target.test.cjs tests/trusted-match-offer-creative-data.test.cjs tests/accessibility-violation-details.test.cjs tests/portfolio-routing-scope.test.cjs tests/catalog-item-availability-updates.test.cjs tests/catalog-ingestion-capabilities.test.cjs tests/sdk-implementation-metadata.test.cjs tests/seller-optimized-package-controls.test.cjs tests/compact-product-lifecycle-storyboards.test.cjs tests/timezone-resolution-storyboards.test.cjs tests/total-budget-redistribution-currency.test.cjs tests/dooh-allocation.test.cjs tests/identity-absence-coherence.test.cjs tests/execution-requirements.test.cjs tests/schema-deprecation-metadata.test.cjs tests/products-only-brief-compatibility.test.cjs tests/async-identity-convergence.test.cjs tests/creative-rotation.test.cjs tests/canonical-forecast-point-parity.test.cjs tests/creative-revisions.test.cjs tests/creative-delivery-contracts.test.cjs tests/tracker-execution-contracts.test.cjs tests/tracker-execution-package-integration.test.cjs tests/metric-identity-coherence.test.cjs tests/print-metrics.test.cjs tests/daypart-time-of-day.test.cjs tests/audience-size-range.test.cjs tests/viewable-rate-optimization-goal.test.cjs tests/sort-contract-delivery-reporting.test.cjs tests/time-based-views-contract.test.cjs tests/metric-qualifier-parity.test.cjs tests/requested-metrics-contract.test.cjs tests/auto-breakdown-negotiation-contract.test.cjs tests/format-delivery-reporting-contract.test.cjs tests/inventory-delivery-reporting-contract.test.cjs tests/property-delivery-conformance.test.cjs tests/lint-schema-enum-drift.test.cjs tests/format-asset-type-parity.test.cjs tests/canonical-manifest-constructibility.test.cjs tests/synthetic-depiction.test.cjs tests/creative-rendering-authority.test.cjs tests/buyer-reason.test.cjs tests/error-recovery-vectors.test.cjs tests/reporting-status-contract.test.cjs tests/reporting-reconciliation-fixture.test.cjs tests/reporting-core-fixture.test.cjs tests/reporting-native-version-ref-bounds.test.cjs tests/definition-pin-vectors.test.cjs tests/trust-json-v1.test.cjs tests/refine-proposals-expected-confirmation-at.test.cjs tests/hold-policy.test.cjs tests/hold-policy-vectors.test.cjs tests/collection-catalog.test.cjs tests/collection-copies-vectors.test.cjs && npm run test:premium-display-formats && npm run test:geo-region-targeting",
Expand Down Expand Up @@ -129,7 +130,7 @@
"test:build-schemas-hoist-by-identity": "node --test --test-force-exit --test-timeout=30000 tests/build-schemas-hoist-by-identity.test.cjs",
"test:build-schemas-async-response-refs": "node --test --test-force-exit --test-timeout=30000 tests/build-schemas-async-response-refs.test.cjs",
"test:build-schemas-vendor-metric-uniqueness": "node --test --test-force-exit --test-timeout=30000 tests/build-schemas-vendor-metric-uniqueness.test.cjs",
"test:release-workflow": "node tests/release-workflow-immutability.test.cjs && node --test tests/release-publication-ordering.test.cjs tests/maintenance-release-publication.test.cjs tests/changesets-fence.test.cjs tests/release-supersession.test.cjs tests/skip-withdrawn-release.test.cjs && node tests/promote-release-candidate.test.cjs && node tests/published-compliance-versions.test.cjs && node --test --test-timeout=30000 tests/cdn-artifacts.test.cjs tests/artifact-cdn-worker.test.cjs",
"test:release-workflow": "node tests/release-workflow-immutability.test.cjs && node --test tests/release-publication-ordering.test.cjs tests/maintenance-release-publication.test.cjs tests/changesets-fence.test.cjs tests/release-supersession.test.cjs tests/skip-withdrawn-release.test.cjs && node tests/promote-release-candidate.test.cjs && node tests/published-compliance-versions.test.cjs && node --test --test-timeout=30000 tests/cdn-artifacts.test.cjs tests/artifact-cdn-worker.test.cjs tests/schema-discovery-pointers.test.cjs",
"promote:rc": "node scripts/promote-release-candidate.mjs",
"test:immutable-release-artifacts": "node tests/immutable-release-artifacts.test.cjs",
"test:runtime-artifacts": "node --test --test-force-exit --test-timeout=30000 tests/runtime-artifact-pruning.test.cjs",
Expand Down
Loading
Loading