Skip to content

release: routes-inheritance fix + post-#85 promotion#91

Merged
brettdavies merged 3 commits into
mainfrom
release/2026-05-15-routes-inheritance
May 15, 2026
Merged

release: routes-inheritance fix + post-#85 promotion#91
brettdavies merged 3 commits into
mainfrom
release/2026-05-15-routes-inheritance

Conversation

@brettdavies
Copy link
Copy Markdown
Owner

Summary

Third production release of the day. Bundles all post-PR-#85 dev work that survived today's incident, namely the three PRs that are net additive after the failed split-config experiment (PR #88) and its revert (PR #89) cancel out: PR #86 (explicit Wrangler env target + dual-env pre-push dry-run), PR #87 (corrected cf registry entry pointing at its npm distribution surface), and PR #90 (the actual fix for the 2-week routing-drift bug: explicit routes: [] override on env.staging to break Wrangler's inheritable-keys inheritance, plus prophylactic triggers: { crons: [] } override for the same trap shape).

The headline is PR #90's wrangler.jsonc change. anc.dev is currently bound to the production Worker (after a manual rebind earlier today), and PR #90's staging-deploy verification confirmed the routing fix holds: the Deployed agentnative-site-staging triggers block no longer lists anc.dev (custom domain), and the CF API confirms the binding stays on agentnative-site through staging deploys. Promoting that wrangler.jsonc state to main means the routing fix is in the committed source-of-truth, not just in dev's history.

User-facing effects on anc.dev after this deploy: the corrected /registry-index.json and /score/cf content from PR #87 reach production (today they live on staging only); the env-target explicitness from PR #86 removes the wrangler ambiguity warning from the next production deploy log. The Worker code itself, bindings, and DO migrations are unchanged.

Changelog

Fixed

  • Stop staging deploys from re-attaching anc.dev to the staging Worker on every dev push. The routing-drift bug tracked since 2026-04-30 was caused by env.staging silently inheriting the top-level routes array. Explicit routes: [] override on env.staging breaks the inheritance. anc.dev now stays on the production Worker across staging deploys.
  • Correct the cf entry in registry.yaml. The previous entry claimed repo: cloudflare/workers-sdk, but cf is not in that repo and the npm package itself declares no repository. Replaced with a url: pointing at the npm distribution page. Side effect: the build's reverse-lookup map for cloudflare/workers-sdk now correctly resolves to wrangler instead of cf.

Changed

  • Pass --env="" explicitly on the production wrangler-action deploy command. Removes the "Multiple environments are defined" ambiguity warning from production deploy logs.
  • Pre-push hook now runs wrangler deploy --dry-run for both the production and staging environments instead of one bare invocation. Catches binding mistakes in either environment before push.
  • Added explicit triggers: { crons: [] } override on env.staging as a prophylactic against the same inheritance trap shape on scheduled triggers. Currently no scheduled triggers; the override forces a deliberate decision when adding any.

Type of Change

  • fix: Bug fix (non-breaking change which fixes an issue)

The release is multi-typed (one fix headline plus two ride-along changes) but fix headlines because PR #90's routes-inheritance fix is the durable resolution of the 2-week routing-drift incident.

Related Issues/Stories

Files Modified

Modified:

  • wrangler.jsonc: env.staging block now explicitly overrides routes: [] and triggers: { crons: [] } to break Wrangler's inheritable-keys inheritance. Top-level production config unchanged (routes: [{ pattern: "anc.dev", custom_domain: true }], workers_dev: false).
  • .github/workflows/deploy.yml: production job's wrangler-action command is deploy --env="" (was bare deploy); staging job's command is unchanged (deploy --env staging).
  • scripts/hooks/pre-push: replaces the single bare wrangler deploy --dry-run step with two dry-runs, one per environment (--env="" and --env staging).
  • registry.yaml: cf entry replaces repo: cloudflare/workers-sdk with url: https://www.npmjs.com/package/cf and an inline comment explaining why.

Created:

  • None.

Renamed:

  • None.

Deleted:

  • None.

Testing

  • Unit tests added/updated
  • All tests passing

Test Summary:

  • 315 unit and regression tests pass.
  • bun x wrangler deploy --dry-run --env="": clean, lists the production-side bindings (Sandbox DO, R2 bucket anc-score-cache, rate-limit namespace 1001, ASSETS). Container image pinned at :30f61f1.
  • bun x wrangler deploy --dry-run --env staging: clean, lists the staging-side bindings (R2 anc-score-cache-staging, rate-limit namespace 1002). The Deployed triggers section in dry-run output is suppressed.
  • Pre-push hook (lint, build, both wrangler dry-runs, pack-README, banned-fonts, prose-check) passes end-to-end.
  • prose-check: 0 blocking.

Post-merge verification plan (after the production deploy on this PR's merge):

## Summary

Address the wrangler 4.x "Multiple environments are defined ... but no
target environment was specified" warning that appears in
production-deploy logs and in every pre-push run. The fix is to pass
`--env=""` explicitly on production (which means "use the top-level
config") and to mirror the dual-env pattern in the pre-push hook so both
production and staging configurations are validated on every push.

No behavior change. Wrangler was already using the top-level config on
bare `wrangler deploy`; this just makes the intent explicit and removes
the cosmetic warning.

Surfaced during the post-deploy log review for release PR #85.

## Changelog

## Type of Change

- [x] `chore`: Maintenance tasks (dependencies, config, etc.)

## Related Issues/Stories

- Story: Cleanup item from the post-deploy log audit of release PR #85.
- Issue: None.
- Architecture: None.
- Related PRs: #85.

## Files Modified

**Modified:**

- `.github/workflows/deploy.yml`: production-step `command:` now reads
`deploy --env=""` with a brief inline comment explaining why.
- `scripts/hooks/pre-push`: replaces the single bare `wrangler deploy
--dry-run` with two dry-runs, one per environment (`--env=""` and `--env
staging`). The hook now catches binding mistakes in either environment
before push instead of only validating the top-level config.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Testing

- [x] Manual testing completed
- [x] All tests passing

**Test Summary:**

- Local `bun x wrangler deploy --dry-run --env=""`: clean, binding list
matches the production-side `anc-score-cache` R2 bucket.
- Local `bun x wrangler deploy --dry-run --env staging`: clean, binding
list matches the staging-side `anc-score-cache-staging` R2 bucket.
- Pre-push hook fired both dry-runs in succession when pushing this
branch and passed.
- The next production deploy (via a release PR to `main` later) will
exercise the `deploy.yml` change; the warning should be absent from the
run log.
…r npm URL (#87)

## Summary

Correct the `cf` entry in `registry.yaml`. The previous entry claimed
`repo: cloudflare/workers-sdk`, but `cf` is not in that repo. I
enumerated all 29 packages in `cloudflare/workers-sdk` and none has
`bin: cf`. The npm `cf` package itself (currently v0.0.5) ships
pre-bundled and declares no `repository`, `homepage`, `bugs`, or
`author` fields. Cloudflare's Technical Preview is published to npm
without disclosing the source repository.

The wrong `repo:` had two downstream effects:

1. The build's registry-index emitted a deterministic but incorrect
mapping from `cloudflare/workers-sdk` to `cf` (overwriting wrangler in
YAML order). A user pasting `github.com/cloudflare/workers-sdk` into the
future live-scoring form would land on `/score/cf` instead of
`/score/wrangler`. The build's "duplicate owner/repo" warning was a
symptom of this.
2. The `/score/cf` page linked to the wrong upstream repository.

Replace `repo:` with `url: https://www.npmjs.com/package/cf` (the
canonical distribution surface for `cf` today) and add an inline comment
explaining why there is no GitHub repo. The registry schema treats
`url:` as the fallback when `repo:` is absent, so the scorecard page
still renders.

Surfaced during the post-deploy log review for release PR #85.

## Changelog

### Fixed

- `/score/cf` and the registry-index now reflect `cf`'s actual
distribution surface: no GitHub source repo is publicly declared, so the
project link is the npm package page. The reverse-lookup map for
`cloudflare/workers-sdk` now resolves correctly to `wrangler`.

## Type of Change

- [x] `fix`: Bug fix (non-breaking change which fixes an issue)

## Related Issues/Stories

- Story: Cleanup item from the post-deploy log audit of release PR #85.
- Issue: None.
- Architecture: None.
- Related PRs: #85, #86.

## Files Modified

**Modified:**

- `registry.yaml`: `cf` entry replaces `repo: cloudflare/workers-sdk`
with `url: https://www.npmjs.com/package/cf` and adds an inline comment
explaining the rationale.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Testing

- [x] Manual testing completed
- [x] All tests passing

**Test Summary:**

- `bun run build`: clean output. The previous "duplicate owner/repo
cloudflare/workers-sdk" warning is gone. `cf` now joins `make` and
`nvidia-smi` in the "no parseable owner/repo, owner/repo entry skipped"
bucket (legitimate; these tools have no canonical source repository).
- Build stats unchanged: 8 principles, 112 HTML pages, 112 MD pages, 97
scorecard pages, 96 badges.
- `dist/score/cf.html` and `dist/score/cf.md` still emit. The fallback
from `repo:` to `url:` does not break scorecard rendering.
- Pre-push hook passed both wrangler dry-runs (production and staging
environments).

**Evidence for the upstream claim:**

- Enumerated all 29 packages under `cloudflare/workers-sdk/packages/`:
`chrome-devtools-patches`, `cli`, `codemod`, `containers-shared`,
`create-cloudflare`, `devprod-status-bot`,
`edge-preview-authenticated-proxy`, `format-errors`, `kv-asset-handler`,
`lint-config-shared`, `local-explorer-ui`, `miniflare`,
`mock-npm-registry`, `pages-shared`, `playground-preview-worker`,
`quick-edit-extension`, `quick-edit`, `solarflare-theme`,
`turbo-r2-archive`, `unenv-preset`, `vite-plugin-cloudflare`,
`vitest-pool-workers`, `workers-editor-shared`, `workers-playground`,
`workers-shared`, `workers-tsconfig`, `workers-utils`,
`workflows-shared`, `wrangler`. None has `bin: cf` in package.json. The
package named `cli` is `@cloudflare/cli-shared-helpers` (internal
helpers, no executable).
- npm registry metadata for `cf`: `repository: null`, `homepage: null`,
`bugs: null`, `author: null`. Tarball contents are entirely pre-bundled
JavaScript under `cf-dist/` (bundler output, no source files).
…eritance from top-level (#90)

## Summary

Explicitly override two inheritable keys in `env.staging` so they stop
silently inheriting destructive values from the top-level config:
`routes` (which has been quietly stealing anc.dev's custom-domain
binding away from production on every dev push since the 2026-04-30 v0.1
launch) and `triggers` (prophylactic; no current scheduled triggers, but
the same trap shape).

The "routing-drift bug" we have been chasing for two weeks turns out to
be documented Wrangler behavior. Per the [Inheritable keys
list](https://developers.cloudflare.com/workers/wrangler/configuration/),
`routes` is an inheritable key. The top-level config declares `routes:
[{ pattern: "anc.dev", custom_domain: true }]`. `env.staging` had no
`routes` field, so it silently inherited that array. Every `wrangler
deploy --env staging` ran with `routes: [{anc.dev}]` in scope and
re-attached anc.dev to `agentnative-site-staging`, transferring the
custom-domain binding away from `agentnative-site`. The deployment log
includes a `Deployed agentnative-site-staging triggers ... anc.dev
(custom domain)` line on every staging deploy that nobody had read as
"the prod custom domain just moved".

Explicit empty arrays break the inheritance without changing any other
behavior. Today's filed-then-retracted upstream issue at
[cloudflare/workers-sdk#13925](cloudflare/workers-sdk#13925)
(rewritten to describe this trap honestly) suggests Wrangler add a
deploy-time warning when an env block inherits a `routes` array
containing custom domains.

## Changelog

### Fixed

- Stop staging deploys from re-attaching `anc.dev` to the staging Worker
on every dev push. The "routing-drift bug" tracked since 2026-04-30 was
caused by `env.staging` silently inheriting the top-level `routes`
array. Explicit `routes: []` override on `env.staging` makes the staging
Worker's deployment stop asserting ownership of `anc.dev`.

### Changed

- Added explicit `triggers: { crons: [] }` override on `env.staging` as
a prophylactic against the same inheritance pattern firing on a future
scheduled-trigger addition.

## Type of Change

- [x] `fix`: Bug fix (non-breaking change which fixes an issue)

## Related Issues/Stories

- Story: Closes the chronic routing-drift bug. anc.dev should now stay
on the production Worker across staging deploys.
- Issue:
[cloudflare/workers-sdk#13925](cloudflare/workers-sdk#13925)
(rewritten to document the actual inheritance trap).
- Architecture: None.
- Related PRs: #85 (manual routing-drift fix that this PR addresses
structurally), #88 (asset-sharing fix that was reverted by #89 once the
routes-inheritance explanation surfaced), #89 (the revert of #88).

## Files Modified

**Modified:**

- `wrangler.jsonc`: two explicit overrides added on `env.staging`
(`routes: []` and `triggers: { crons: [] }`) with inline comments
documenting why. No other config changed.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Testing

- [x] Unit tests added/updated
- [x] All tests passing

**Test Summary:**

- 315 unit and regression tests pass (unchanged from pre-PR baseline).
- `bun x wrangler deploy --dry-run --env=""`: clean. Production-side
bindings unchanged.
- `bun x wrangler deploy --dry-run --env staging`: clean. Staging-side
bindings unchanged.
- The deployment log's `Deployed agentnative-site-staging triggers`
section is now empty (dry-run output suppresses the section entirely
when there are no triggers, which matches the expected post-fix
behavior).
- Pre-push gate passes end-to-end.

**Audit of every inheritable key:**

| Key | Top-level | env.staging | Status |
|---|---|---|---|
| `name` | `agentnative-site` | `agentnative-site-staging` | Explicit
override |
| `main` | `src/worker/index.ts` | inherited | Safe (same source by
design) |
| `compatibility_date` | `2026-04-01` | inherited | Safe |
| `compatibility_flags` | `["nodejs_compat"]` | inherited | Safe |
| `account_id` | via env var | via env var | Same account |
| `workers_dev` | `false` | `true` | Explicit override |
| `routes` | `[{anc.dev}]` | `[]` | **Explicit override (this PR)** |
| `triggers` | not set | `{ crons: [] }` | **Explicit override (this PR,
prophylactic)** |
| `observability` | `{enabled: true, ...}` | inherited | Safe (same
intent) |
| `assets` | `{directory: "./dist", ...}` | inherited | Safe (per-Worker
asset stores; the "asset overlay" symptoms were routing drift) |
| `send_metrics` | `false` | inherited | Safe (both opt out) |
| `migrations` | `[{tag: v1, ...}]` | explicitly declared | Already
overridden |
| `preview_urls`, `route` (singular), `tsconfig`, `rules`, `build`,
`no_bundle`, `find_additional_modules`, `base_dir`,
`preserve_file_names`, `minify`, `keep_names`, `logpush`, `limits`,
`placement` | not set | not set | n/a |

If any of the currently-unset inheritable keys gets set at the top level
later, re-audit `env.staging` and add an explicit override if the
inherited value would be destructive for staging.

**Post-merge plan:**

After this PR's staging deployment on dev, verify:

- Staging deploy log's `Deployed agentnative-site-staging triggers`
section is empty (no `anc.dev (custom domain)` line).
- CF API: `anc.dev` custom-domain binding stays on `service:
agentnative-site` after the deployment completes.
- `curl https://anc.dev/` returns 200 from the production Worker (no
`X-Robots-Tag` header, content matches main's deployed assets).
- `curl https://agentnative-site-staging.brettdavies.workers.dev/`
returns 200 with `X-Robots-Tag: noindex`.

If anc.dev binding moves to staging after this deployment, the fix is
wrong and we need a different mechanism.
@brettdavies brettdavies merged commit f512203 into main May 15, 2026
4 checks passed
@brettdavies brettdavies deleted the release/2026-05-15-routes-inheritance branch May 15, 2026 07:20
brettdavies added a commit that referenced this pull request May 15, 2026
)

## Summary

Pass `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` to the two
`deep-check.yml` jobs (Playwright + Lighthouse CI) that spin up
`wrangler dev` as the test web server. Without these env vars, wrangler
4.x exits with `Not logged in` before Playwright can connect, and the
nightly fails 100% of the time.

The regression landed silently in PR #84 (U3-followup) when the sandbox
container image moved off Docker Hub onto the Cloudflare managed
registry. Anonymous pulls work for `docker.io/...`; the CF managed
registry requires auth, and wrangler 4.x authenticates to read the
container image manifest even under `--local`. ci.yml did not catch it
because nothing in the ci.yml pipeline invokes `wrangler dev`. Today's
first deep-check after PR #91 merged surfaced it as the Playwright +
Lighthouse CI jobs failed simultaneously with the same `Not logged in`
error from `wrangler dev`.

The same secrets `deploy.yml` already uses are passed here; no new
provisioning needed. Header comment updated to document the new
dependency.

## Changelog

### Fixed

- `deep-check.yml`'s `e2e` and `lhci` jobs no longer fail 100% with `Not
logged in` from `wrangler dev`. Both jobs now have
`CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` plumbed through,
which wrangler 4.x needs in order to read the container image manifest
from the CF managed registry even under `--local`.

## Type of Change

- [x] `fix`: Bug fix (non-breaking change which fixes an issue)

## Related Issues/Stories

- Story: deep-check has been failing on every scheduled run since PR #84
(U3-followup) landed on dev. The first deep-check that ran on main with
the migrated container image (run `25905730897`, today 07:20 UTC,
scheduled trigger immediately after PR #91 merged to main) surfaced both
jobs failing identically.
- Issue: None.
- Architecture: None.
- Related PRs: #84 (introduced the regression by migrating off Docker
Hub), #91 (the release that promoted the new image pin to main and
triggered the first deep-check failure with main's view of it).

## Files Modified

**Modified:**

- `.github/workflows/deep-check.yml`: header comment under "Secrets" now
documents the `CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID`
dependency and the reason. The `e2e` job's `End-to-end tests` step and
the `lhci` job's `Lighthouse CI` step each gained an `env:` block
setting `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` from the
existing repo secrets.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Testing

- [x] All tests passing locally (no new tests)

**Test Summary:**

- 315 unit and regression tests pass locally.
- Pre-push gate green.
- The actual auth-fix verification path is post-merge: manually dispatch
the deep-check workflow against the merge commit via `gh workflow run
deep-check.yml --ref main` and confirm both jobs reach the test phase
(no longer fail at `Not logged in`). Cannot fully verify pre-merge
because the failure mode only surfaces in the GHA runner environment.
Local `bun run test:e2e` works because the developer's shell already has
`CLOUDFLARE_API_TOKEN` set.
brettdavies added a commit that referenced this pull request May 25, 2026
First production cut of the live-scoring stack. Promotes everything on
dev since PR #91 (the prior release): plan U5-U10 worker code (handler,
sandbox DO, container, R2 cache, rate limits, telemetry, kill switch,
homepage form, shareable result URLs, monitoring runbook), the docker
sandbox image with anc v0.4.0 baked in, deploy split + routing-drift
follow-ups (#92 CI token plumbing), the contributor surface (nav,
footer Source row, intake template, README rewrite), the build SRP
refactor, the SEO JSON-LD @graph fix, the cross-migration rollback
rehearsal evidence and recipe correction, and the env.SCORE handler
guard that converts a mid-rollback CF 1101 into a typed 503
sandbox_unavailable.

Lockstep image bump: both top-level containers[0].image and
env.staging.containers[0].image at :9aed5c3 (anc-cli v0.4.0,
sha256:dae72c56afe2f332e8745c0517f1ed5d21993470de663409dfc9b3973cdfe4c1).
The image cleared staging deploy on dev push 26384622721; soak was
skipped per release-cut decision.

Triple-diff verification clean (134 files changed; no guarded-path
leaks; expected B-diff is the prod-pin bump only).
brettdavies added a commit that referenced this pull request May 25, 2026
## Summary

First production cut of the live-scoring stack. Promotes every dev
change since PR #91 (2026-05-15) and a lockstep sandbox image bump to
anc-cli v0.4.0.

The headline is `/api/score` on anc.dev. Users (and agents) can paste an
install command or a GitHub URL into the homepage form, and the Worker
resolves it through the registry-fast-path, R2 cache, or a live sandbox
run inside a Cloudflare Durable Object + Container. Sandbox runs invoke
`anc check` (v0.4.0, baked into the image at build time) against a fresh
install of the user's tool and return a typed scorecard with the
response triad (`spec_version`, `site_spec_version`, `anc_version`,
`checker_url`). Shareable result URLs at `/live-score/<binary>` serve
from R2 with a 7-day TTL.

Ride-alongs:

- Contributor surface: nav exposes Skill + Contribute, footer carries a
Source row, an intake template lands for external contributions, and the
README is rewritten to reflect the current dev surface.
- SEO: JSON-LD `@graph` now emits an Organization + Person author on
every page.
- Build internals: `build.mjs` SRP-split into numbered pipeline stages;
CI smoke for `/api/score` extracted into `scripts/smoke-api-score.sh`.
- Reliability: handler now returns a typed 503 `sandbox_unavailable`
instead of Cloudflare error 1101 when the `SCORE` DO binding is missing
(mid-rollback safety; surfaced by the rehearsal).
- Operational: `RELEASES.md` includes the cross-migration rollback
rehearsal evidence row and a recipe correction (mandatory `wrangler
containers delete <id>` step between v2-drop-sandbox and
v3-restore-sandbox).

Image lockstep: top-level `containers[0].image` and
`env.staging.containers[0].image` both pinned at `:9aed5c3` (anc-cli
v0.4.0, digest
`sha256:dae72c56afe2f332e8745c0517f1ed5d21993470de663409dfc9b3973cdfe4c1`).
The image cleared the staging deploy on dev push run `26384622721`; soak
was skipped per the release-cut decision documented in the PR thread
that produced #114.

## Changelog

### Added

- Live scoring at `/api/score`: paste an install command or GitHub URL,
get a typed scorecard back. Homepage form + shareable
`/live-score/<binary>` result URLs (R2-backed, 7-day TTL).
- Cloudflare Sandbox DO + Container running anc v0.4.0 against a fresh
install of the user's tool inside a per-request ephemeral environment.
- Cost guardrails: Workers Analytics Engine telemetry, per-session +
per-IP rate limits, operator-flippable `scoring_disabled` kill switch in
KV.
- Contributor surface: Skill + Contribute nav entries, footer Source row
pointing at the four `brettdavies/agentnative-*` repos, intake template
at `.github/ISSUE_TEMPLATE/`, rewritten README.
- Monitoring runbook at `docs/runbooks/live-scoring-analytics.md`.

### Changed

- `wrangler.jsonc` `containers[0].image` (production pin) advances from
`:30f61f1` to `:9aed5c3` lockstep with
`env.staging.containers[0].image`. New image carries anc-cli v0.4.0 (was
v0.3.1) plus the PR #100 Dockerfile pip-version-check suppression baked
in (previously runtime-bridged).
- JSON-LD on every page now emits a `@graph` with Organization + Person
author (was bare `TechArticle`).
- Build pipeline split into numbered SRP stages under `src/build/`;
`build.mjs` becomes the orchestrator.
- `staging` env block in `wrangler.jsonc` carries an explicit `routes:
[]` and `triggers.crons: []` override and the full live-scoring binding
surface (Sandbox DO, Container, R2, SCORE_KV, Analytics Engine, two
rate-limit namespace IDs).

### Fixed

- `/api/score` no longer surfaces Cloudflare error 1101 when the `SCORE`
DurableObject binding is missing (mid-rollback Worker state). Returns a
typed 503 `sandbox_unavailable` with `spec_version` and `checker_url` so
clients can render a useful error.

### Documentation

- `RELEASES.md` cross-migration rollback rehearsal section now documents
the mandatory `wrangler containers delete <id>` step between
v2-drop-sandbox and v3-restore-sandbox (surfaced live during the
2026-05-24 rehearsal).
- Rehearsal evidence row populated with every staging deploy ID,
container app ID, and DO namespace ID from the v1 → v2-drop-sandbox →
v3-restore-sandbox walk.

## Type of Change

- [x] `feat`: New feature (non-breaking change which adds functionality)

This release is multi-typed (feat headline plus several fix and docs
ride-alongs) but `feat` headlines because live scoring is the durable
new product surface anc.dev ships with this cut.

## Related Issues/Stories

- Story: Closes the live-scoring v3 plan (U1-U10). Production has never
run this stack; staging absorbed the full rehearsal (see `RELEASES.md` §
Cross-migration rollback rehearsal evidence row dated 2026-05-24) and
the lockstep image bump via PR #114.
- Issue: n/a
- Architecture:
`docs/solutions/tooling-decisions/cloudflare-sandbox-python-3.12-base-2026-05-19.md`
(dev-only) for the python:3.12 base + version-pin matrix the sandbox
image follows.
- Related PRs: Every dev-side PR that contributed: #82 (prose-check),
#83 (P8 + PRODUCT.md), #93 (`/api/score` U5), #94 (DO stub fetch), #95
(Sandbox DO + two-phase egress U6), #96 (R2 cache + unified scorecard
lookup U7), #97 (R2 lifecycle docs), #98 (U7 red-team tests), #99
(python:3.12 + sdist allowlist), #100 (homepage form + share URLs U8),
#101 (U9 tests + CI smoke), #102 (extract smoke script), #103
(monitoring runbook), #104 (analytics + cost guardrails U10), #105
(contrib docs + prose pass), #106 (nav + footer source), #107 (build SRP
split), #108 (README rewrite), #109 (matrix sync), #110 (docker/score
mode injection), #111 (JSON-LD @graph), #112 (rehearsal evidence +
recipe fix), #113 (env.SCORE handler guard), #114 (anc v0.4.0 image
rebuild).

## Testing

- [x] Unit tests added/updated
- [x] Integration tests added/updated
- [x] Manual testing completed
- [x] All tests passing

**Test Summary:**

- `bun test`: 737 pass / 0 fail across 28 files on the release branch.
- `bun x wrangler deploy --dry-run` against both environments: clean.
Lists `agentnative-site-sandbox` at `:9aed5c3` (production) and
`agentnative-site-staging-sandbox-staging` at `:9aed5c3` (staging).
- Cross-migration rollback rehearsal executed live on staging
2026-05-24: v1 baseline confirmed, v2-drop-sandbox applied cleanly,
container-app cleanup performed, v3-restore-sandbox applied cleanly,
DO-invoking path confirmed running. Evidence in `RELEASES.md`.
- Staging deploy on dev push `26384622721` (post PR #114 merge): green.
Confirmed `:9aed5c3` deployed and `/api/score` responds with
`anc_version: 0.4.0` (cold-start `xplr` request returned typed 504
`timeout` while container provisioned; warm cache hit immediately after
returned full scorecard).
- Pre-push gate (lint, build, test, wrangler dry-run x2, pack-README,
banned-fonts, prose-check): pass.
- Triple-diff verification per `RELEASES.md`: A=134 files clean,
B=`wrangler.jsonc` only (the prod-pin bump), C=mirror of A,
guarded-paths leak=clean.

**Post-merge verification plan** (after the production deploy on this
PR's merge):

- Production deploy log shows `agentnative-site` at `:9aed5c3`.
- `curl https://anc.dev/api/score -H 'Content-Type: application/json' -d
'{"input":"ripgrep","turnstile_token":"<real>"}' ` returns 200 with the
response triad (`spec_version: 0.4.0`, `site_spec_version: 0.4.0`,
`anc_version: 0.4.0`, `checker_url`).
- Homepage form at anc.dev renders the Turnstile widget (real `sitekey`,
not the staging test `sitekey`) and successfully POSTs.
- `bun x wrangler containers list` shows `agentnative-site-sandbox`
advanced from container app `a0329fb0-...` (image `:30f61f1`, 6
instances) to a new container app instance at `:9aed5c3`. Existing prod
instances may take a few minutes to roll over.
- DO migration `v1` (which created the `Sandbox` class) applies to
production for the first time; the migration is irreversible. Rollback
path documented in `RELEASES.md` § Cross-migration rollback rehearsal,
including the `wrangler containers delete <id>` step that the rehearsal
surfaced.
- No production smoke step runs automatically (per `RELEASES.md` §
Post-deploy smoke scope, the smoke step is staging-only). Manual
verification within ~5 minutes of merge is the gate.

## Files Modified

**Modified:**

- 95+ files across `src/worker/`, `src/build/`, `content/`,
`docker/sandbox/`, `docker/score/`, `tests/`, `scripts/`, `styles/`,
`.github/`, `RELEASES.md`, `README.md`, `wrangler.jsonc`. Full list in
the diff.
- `wrangler.jsonc`: lockstep image pin bump (top-level + env.staging
both `:30f61f1` → `:9aed5c3`); full live-scoring binding surface
mirrored under env.staging; migration history aligned with applied state
(v1 top-level; v1 + v2-drop-sandbox + v3-restore-sandbox under
env.staging per the rehearsal).
- `RELEASES.md`: rehearsal recipe correction + evidence row.

**Created:**

- 30+ new source files implementing the live-scoring pipeline:
`src/worker/score/handler.ts`, `src/worker/score/do.ts`,
`src/worker/score/cache.ts`, `src/worker/score/discover-binary.ts`,
`src/worker/score/parse-install.ts`,
`src/worker/score/registry-lookup.ts`,
`src/worker/score/resolve-spec.ts`,
`src/worker/score/response-shape.ts`,
`src/worker/score/sandbox-exec.ts`, `src/worker/score/session.ts`,
`src/worker/score/sdist-allowlist.ts`, `src/worker/score/telemetry.ts`,
`src/worker/score/turnstile.ts`, `src/worker/score/kill-switch.ts`,
`src/worker/score/github-accessibility.ts`, plus their test files. Full
list in the diff.
- `docker/sandbox/Dockerfile` (sandbox image source),
`docker/sandbox/README.md`.
- `docs/runbooks/live-scoring-analytics.md`.
- `scripts/smoke-api-score.sh`, `scripts/staging-cache-smoke.sh`.

**Renamed:**

- None.

**Deleted:**

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant