From 5be999944dbfe93b7fe77cce389f89a95b697f0f Mon Sep 17 00:00:00 2001 From: Sean <5307302+yuxiaoli@users.noreply.github.com> Date: Sun, 11 Oct 2026 13:32:29 +0800 Subject: [PATCH 1/2] fix(api): use redirect handling supported by Workers --- docs/REGISTRY_API.md | 2 +- src/api/api.test.ts | 5 +++-- src/api/worker.ts | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/REGISTRY_API.md b/docs/REGISTRY_API.md index c701c7d2..ed02b5fc 100644 --- a/docs/REGISTRY_API.md +++ b/docs/REGISTRY_API.md @@ -31,7 +31,7 @@ The first release requires a manual “Deploy Skill Catalog” run with `bootstrap_feed=true`. The existing directory becomes the baseline, with no historical notification flood. Future runs read `feed-state.json` from the last successful Pages publication and compare its source revision with the latest -successful `github-pages` deployment. A later missing/corrupt state cannot be +completed successful production publication workflow. A later missing/corrupt state cannot be bootstrapped again: recover the last published artifact instead. Production publishes are serialized through verification of the new public diff --git a/src/api/api.test.ts b/src/api/api.test.ts index 14334e32..4470a131 100644 --- a/src/api/api.test.ts +++ b/src/api/api.test.ts @@ -237,6 +237,7 @@ describe("catalog API", () => { `https://raw.githubusercontent.com/alpha/skills/${"a".repeat(40)}/SKILL.md`, ); expect(fetcher.mock.calls[1][1]?.headers).toBeUndefined(); + expect(fetcher.mock.calls[1][1]?.redirect).toBe("manual"); expect( ( await worker.fetch( @@ -250,7 +251,7 @@ describe("catalog API", () => { }); it("reports legacy provenance, unavailable upstream and mismatched hash", async () => { - for (const scenario of ["legacy", "missing", "hash", "timeout"]) { + for (const scenario of ["legacy", "missing", "redirect", "hash", "timeout"]) { const s = await fixtureSnapshot(); if (scenario === "legacy") delete s.skills[0].sourceCommit; const fetcher: typeof fetch = async (input) => { @@ -259,7 +260,7 @@ describe("catalog API", () => { if (scenario === "timeout") throw new DOMException("timeout", "TimeoutError"); return new Response("wrong", { - status: scenario === "missing" ? 404 : 200, + status: scenario === "missing" ? 404 : scenario === "redirect" ? 302 : 200, }); }; const response = await createWorker(fetcher).fetch( diff --git a/src/api/worker.ts b/src/api/worker.ts index 2e368396..23292ec3 100644 --- a/src/api/worker.ts +++ b/src/api/worker.ts @@ -115,7 +115,9 @@ export function createWorker( try { const response = await fetcher(sourceUrl, { signal: AbortSignal.timeout(10_000), - redirect: "error", + // workerd supports manual/follow only. Reject redirect status codes + // below to preserve the commit-pinned origin contract. + redirect: "manual", }); if (!response.ok || !response.body) throw new Error("Source unavailable"); const reader = response.body.getReader(); From c836f0870b43f5ae4e71f01b2ba115cc9efe63e7 Mon Sep 17 00:00:00 2001 From: Sean <5307302+yuxiaoli@users.noreply.github.com> Date: Sun, 11 Oct 2026 13:33:55 +0800 Subject: [PATCH 2/2] docs(api): record live acceptance and Free plan limitation --- docs/REGISTRY_API.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/REGISTRY_API.md b/docs/REGISTRY_API.md index ed02b5fc..770e71af 100644 --- a/docs/REGISTRY_API.md +++ b/docs/REGISTRY_API.md @@ -27,6 +27,27 @@ to GitHub. No workflow enables or purchases a Workers plan. ## Publish the first feed +### Current Free-plan deployment + +Live acceptance on 2026-10-11 verified 6,842 skills from 81 repositories, +all selectors and pagination, and a 292,366-character body reconstructed from +10 hash-verified segments. Cloudflare measured API CPU P50 of 1.43 ms and P99 +of 560 ms, and isolate memory P99 of approximately 83 MiB. These are sampled +metrics from the acceptance requests, including cold loads. + +The account is on Workers Free. Cold loads and snapshot refreshes exceed its +10 ms CPU allowance; repeated refresh testing returned resource-limit 503s. +The maintainer explicitly deferred a paid upgrade. The current deployment is +therefore available for evaluation but cannot guarantee successful cold loads +or refreshes. No subscription was changed. + +For a future approved Workers Paid deployment, configure +`"limits": { "cpu_ms": 1000 }` in `wrangler.jsonc`, deploy with Wrangler and +repeat cold-load/refresh acceptance. The Paid subscription starts at US$5/month; +see [Cloudflare pricing](https://developers.cloudflare.com/workers/platform/pricing/). +Free accounts reject this CPU-limit configuration, so it is intentionally absent +from the current config. + The first release requires a manual “Deploy Skill Catalog” run with `bootstrap_feed=true`. The existing directory becomes the baseline, with no historical notification flood. Future runs read `feed-state.json` from the last