Skip to content

feat(providers): registries + Cloudflare + onboarding tooling (+ Stripe Projects snapshots)#4

Merged
snowmead merged 2 commits into
mainfrom
feat/stripe-projects-snapshots
Jun 16, 2026
Merged

feat(providers): registries + Cloudflare + onboarding tooling (+ Stripe Projects snapshots)#4
snowmead merged 2 commits into
mainfrom
feat/stripe-projects-snapshots

Conversation

@snowmead

@snowmead snowmead commented Jun 16, 2026

Copy link
Copy Markdown
Owner

This branch began as the Stripe Projects plugin snapshot/pinning framework and grew into a provider-extensibility refactor that makes adding a provider a one-registration-site change — proven by adding Cloudflare — plus the tooling to make the next provider frictionless.

72 files changed; the two parts are independent and each green on their own.


Part 1 — Provider extensibility, Cloudflare, and onboarding tooling

Foundation — core never names a provider

  • Replaced the Host enum with a binary-owned substrate registry (stackless/src/substrates.rs); stackless-core takes the known-substrate list as input and names no provider.
  • Folded spend_line / fetch_logs into the Substrate trait — commands.rs no longer special-cases providers, and this fixes Vercel inheriting bogus local-file logs.
  • Moved RENDER_* / VERCEL_* error codes out of core into their provider crates, with a workspace-wide code-uniqueness test.
  • Registry-driven integration dispatch via a ProviderOps object — no more match provider / is_clerk_resource.
  • New stackless-cloud crate: shared credential resolution + operator prepare (de-duplicates the verbatim render/vercel copies).

Generalized resolution + Cloudflare

  • Generic CatalogResource trait + shared provision_outputs, handling Stripe's dual {RESOURCE}_X / {PROVIDER}_X env-var naming. Clerk's single-JSON-blob path stays the documented exception.
  • Cloudflare catalog integrations: R2, KV, D1, Queues, Hyperdrive, Workers, Workers AI, Browser Run — output envelopes pinned by live provisioning. Excluded by design: containers (paid / "pricing unavailable" = unknown cost), registrar:domain (one-time non-refundable purchase), workers:free|paid (plans). The Workers deploy substrate (--on cloudflare) is a separate future build.

Onboarding tooling + tests

  • New xtask crate → mise run catalog | discover | new-integration:
    • catalog <provider> — offline schema/pricing explorer.
    • discover <reference> — live: provisions into a throwaway env, dumps the real credential output env vars (the envelope the catalog doesn't describe), tears down.
    • new-integration <reference> — scaffolds a provider module from the catalog schema.
  • Shared test_support::provision_script harness (collapses each provision test to ~5 lines) + a registry-completeness test.
  • One shared fixtures/smoke/run.sh (always tears down, unique per-run instance names) replacing three duplicated tasks — fixes the latent vercel/render down-skip-on-failure bug.
  • Docs: docs/ADDING-A-PROVIDER.md (workflow + hard-won gotchas) and a root CLAUDE.md indexing the project docs.

Net: adding a provider is now catalog → new-integration → discover → paste fields + one PROVIDERS/SUBSTRATES row.


Part 2 — Stripe Projects plugin snapshots

The stripe projects plugin is versioned independently of the Stripe CLI (v0.19.0), ships no per-version changelog, and every release is 0.MINOR.0 (breaking-eligible). Three committed snapshots under crates/stackless-stripe-projects/tests/fixtures/ whose git diff is the changelog Stripe doesn't publish:

  • plugin-version.txt — the pinned version of record (CI installs exactly this).
  • command-surface.txt — every stripe projects subcommand's --help; added/removed/renamed commands & flags show as line diffs.
  • catalog.json — re-blessed/canonicalized; server-side last_updated normalized so it only diffs on real service/schema changes.

One bless path generates all three — refresh_blesses_snapshots (gated on STRIPE_PROJECTS_REFRESH=1), reusing StripeProjects/TokioRunner/drift_report(). It refuses to write if the live catalog has wire-format the typed model can't cover.

Enforcement — no manual ritual:

Layer Where What
Hermetic mise run test (every PR) + prek hooks fixtures_are_coherent: header==pin, blocks==code's TRAVERSAL, every banner command captured
Live pinned gate smoke.yml (nightly) installs pinned plugin, re-blesses, fails on surface/version drift or unmodeled catalog
Watcher → auto-PR stripe-projects-watch.yml (nightly) installs latest, opens an upgrade PR with the diff when upstream changes

No STRIPE_API_KEY needed for snapshots (--version, --help, catalog --json are unauthenticated).


Verification

mise run check ✅ · workspace tests 200 passed ✅ · pre-commit + pre-push gates green on push ✅ · Cloudflare R2/KV/D1/Workers/workers-ai/browser-run live-pinned end-to-end ✅ · snapshot bless idempotent (byte-identical re-bless) ✅

Setup needed before merge

  • Add a GH_PAT repo secret (repo scope) so the snapshot watcher's auto-PR re-triggers ci.yml (PRs from the default GITHUB_TOKEN don't). Falls back to GITHUB_TOKEN if absent.

🤖 Generated with Claude Code

The `stripe projects` plugin is versioned independently of the Stripe CLI
and ships no changelog. Pin it and snapshot its surface so upgrades are
reproducible and their git diff is the changelog:

- plugin-version.txt, command-surface.txt, catalog.json — committed
  snapshots regenerated by one bless path (refresh_blesses_snapshots,
  gated on STRIPE_PROJECTS_REFRESH=1), reusing the typed driver +
  drift_report(); refuses to write on unmodeled catalog drift.
- Enforced automatically, no manual ritual: fixtures_are_coherent runs
  offline on every PR (in `mise run test`); prek pre-commit/pre-push
  hooks auto-wired by `mise install`; smoke.yml gates the pinned surface
  against the live plugin; stripe-projects-watch.yml opens an upgrade PR
  nightly when upstream changes.

clap was rejected for the refresh (mise + a bless-test fits the repo's
conventions); both existing shell scripts stay shell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@snowmead snowmead enabled auto-merge June 16, 2026 14:35
…ling

Make adding a Stripe-Projects-backed provider a one-registration-site change,
prove it by adding Cloudflare, and build tooling so the next provider is
frictionless.

Foundation (core never names a provider):
- Replace the `Host` enum with a binary-owned substrate registry
  (`stackless/src/substrates.rs`); core takes the known-substrate list as input.
- Fold `spend_line`/`fetch_logs` into the `Substrate` trait (fixes Vercel
  inheriting bogus local-file logs); move `RENDER_*`/`VERCEL_*` error codes into
  the provider crates with a workspace-wide uniqueness test.
- Registry-driven integration dispatch via a `ProviderOps` object (no more
  `match provider` / `is_clerk_resource`).
- New `stackless-cloud` crate: shared credential resolution + operator prepare
  (de-duplicates the verbatim render/vercel copies).

Generalized resolution + Cloudflare:
- Generic `CatalogResource` trait + shared `provision_outputs` (handles Stripe's
  dual `{RESOURCE}_X` / `{PROVIDER}_X` env-var naming); Clerk's single-blob path
  stays the documented exception.
- Cloudflare catalog integrations: r2, kv, d1, queues, hyperdrive, workers,
  workers-ai, browser-run (live-pinned envelopes; containers/registrar excluded
  as paid/unknown-cost / non-disposable).

Onboarding tooling + tests:
- New `xtask` crate: `catalog` (offline schema explorer), `discover` (live
  output-envelope pinning), `new-integration` (scaffold from catalog schema).
- Shared `test_support::provision_script` harness + a registry-completeness test.
- One shared `fixtures/smoke/run.sh` (always tears down, unique per-run names),
  fixing the latent vercel/render down-skip-on-failure bug.
- Docs: `docs/ADDING-A-PROVIDER.md` (workflow + gotchas) and a root `CLAUDE.md`
  indexing the project docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@snowmead snowmead changed the title feat(stripe-projects): pin plugin + auto-detect upgrades via snapshots feat(providers): registries + Cloudflare + onboarding tooling (+ Stripe Projects snapshots) Jun 16, 2026
@snowmead snowmead merged commit aec93b6 into main Jun 16, 2026
11 checks passed
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