Skip to content

fix: align starter with the real Throttle platform contract - #1

Merged
kalwiggins merged 1 commit into
mainfrom
fix/platform-contract-alignment
Jul 21, 2026
Merged

fix: align starter with the real Throttle platform contract#1
kalwiggins merged 1 commit into
mainfrom
fix/platform-contract-alignment

Conversation

@kalwiggins

Copy link
Copy Markdown
Contributor

What & why

An end-to-end readiness evaluation (2026-07-20) against the live platform found four defects that each break a real integration the moment a builder moves past the mocked quickstart. Companion platform PR: Epic-Design-Labs/app-throttle#765 (fixes the extension.ping envelope + publishes the starter docs page).

The four fixes

  1. Webhook envelope — every real delivery was rejected. Real deliveries send the canonical flat envelope with a required version: "1" field (see buildOutboundRequest in @platform/webhooks, and the envelope example on the events docs page). The strict throttleEventSchema didn't allow version, so every real webhook failed validation → 4xx → platform retries → endpoint auto-disable. Added version to the schema, fixture, and all test event literals + a required-field regression test.

  2. Dashboard origin — real iframe handshake always failed. dashboard.usethrottle.dev does not resolve (the dashboard is app.usethrottle.dev). Exact-origin matching meant the shipped default could never complete the bridge handshake in a real install. Fixed in wrangler.jsonc, README, local-development.md, and the setup script's next-steps output.

  3. pnpm setuppnpm run setup. Bare pnpm setup invokes pnpm's built-in setup command — it appends PNPM_HOME config to the developer's shell profile and never runs scripts/setup.mjs (reproduced locally). All docs updated; the documentation contract test now rejects the bare form.

  4. Scope placeholders weren't real scope ids. connector:read/connector:write aren't in the Throttle scope catalog (closest real ids are the plural connectors:*, which are payment-connector management scopes anyway) — an identity JWT can never carry them, so the Worker 403'd every authenticated request. Defaults are now orders:read + fulfillments:write (real, extension-grantable, fit the demo order-sync connector); the mock bridge stays in sync and the README config table links the scope catalog.

Deliberately NOT changed

  • <this-repository-url> in the README quickstart stays a placeholder — verify-release intentionally warns downstream publishers to set their own repo URL, and that guardrail (plus its tests) depends on the placeholder. The published clone command lives on the starter-repository docs page.
  • Test fixtures that use dashboard.usethrottle.dev as an arbitrary origin value are self-consistent and untouched.

Verification

pnpm check passes end-to-end: format, lint, typecheck, all 9 package suites + root e2e/workspace-boundary/documentation tests (137/137 root), and the wrangler dry-run build.

🤖 Generated with Claude Code

Four fixes from an end-to-end readiness evaluation against the live platform
(2026-07-20). Each one broke a real integration point when the starter was
used past the mocked quickstart:

1. Webhook envelope: real deliveries carry a required `version` field
   ("1") in the canonical flat envelope; the strict throttleEventSchema
   rejected it, so EVERY real delivery would have failed validation. Added
   `version` to the schema, the fixture, and every test event literal, plus
   a regression test that the field is required.

2. Dashboard origin: `dashboard.usethrottle.dev` does not resolve — the
   real dashboard is `app.usethrottle.dev`. Since the bridge and CORS/CSP
   enforce an exact-origin match, the shipped default broke the real iframe
   handshake. Fixed in wrangler.jsonc, README, local-development.md, and
   the setup script's next-steps output. (Test fixtures that use the old
   host as an arbitrary origin are self-consistent and unchanged.)

3. `pnpm setup` -> `pnpm run setup` in all docs: the bare form invokes
   pnpm's own built-in `setup` command, which modifies the developer's
   shell profile instead of running scripts/setup.mjs. The documentation
   contract test now rejects the bare form.

4. Scope placeholders: `connector:read`/`connector:write` are not ids in
   the Throttle scope catalog (and the JWT would never carry them), so the
   Worker would 403 every request. Defaults are now `orders:read` +
   `fulfillments:write` (real, extension-grantable ids that fit the demo
   order-sync connector), with the mock bridge kept in sync and a README
   note pointing at the scope catalog.

Verified: pnpm check passes end-to-end (format, lint, typecheck, all
package suites + root e2e/boundary/documentation tests, wrangler dry-run
build).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kalwiggins
kalwiggins merged commit a0571cc into main Jul 21, 2026
0 of 2 checks passed
@kalwiggins
kalwiggins deleted the fix/platform-contract-alignment branch July 21, 2026 15:20
kalwiggins added a commit that referenced this pull request Jul 21, 2026
… behavior (#2)

Found by a live end-to-end dry run against production (register → install →
iframe launch → bootstrap → ping → real order.created). Three bugs meant the
starter could not complete a real integration out of the box:

1. Identity verification rejected every real launch token: requiredClaims
   demanded `nbf`, but platform tokens carry iss/sub/aud/iat/exp only (and
   the docs never promise nbf). Dropped nbf from requiredClaims — jose still
   validates it when present. The "missing nbf" test now asserts acceptance.

2. Worker CORS only allowed the dashboard origin, but the iframe UI calls
   the Worker from the UI's OWN origin, so every UI request failed
   preflight in the documented split-origin deployment. Added an optional
   EXTENSION_UI_ORIGIN var (exact HTTPS origin, validated at boot) that is
   allowed alongside the dashboard origin; documented in wrangler.jsonc,
   README, cloudflare-deployment.md, and local-development.md.

3. The demo connector read `event.data.orderId`, but real order.created
   deliveries carry the full order under `data.order` — the demo terminal-
   failed on its first real event while its invented fixture kept tests
   green. Now reads data.order.id; the fixture and every event literal in
   tests model the real payload shape.

Also repairs two breaks that slipped into main via #1 (its final check ran
stale): an unused-var lint error in contracts.test.ts and three webhook
verification tests whose signed rawBody fixture predated the required
`version` field (digest recomputed).

Verified: pnpm check (format, lint, typecheck, 137 root + all package
tests, wrangler dry-run build) exit 0.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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