# Backend Cloudflare Worker Implementation Plan

> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build a new Cloudflare Worker backend in `apps/backend` that preserves the current `apps/api` contract and deploys to `api.xtablo.com` and `api-staging.xtablo.com`.

**Architecture:** Create a new Worker-native Hono app with request-scoped services for Supabase, Stripe, mail, storage, and billing sync. Port the current router contract in slices, keeping `apps/api` in the repo as the reference implementation while replacing Node-only concerns with Cloudflare-compatible adapters.

**Tech Stack:** Cloudflare Workers, Hono, TypeScript, Vitest, Wrangler, Supabase JS, Stripe SDK, pnpm workspaces, Turborepo.

**Spec:** `docs/superpowers/specs/2026-04-16-backend-cloudflare-worker-design.md`

---

## File Structure

### New files

**App scaffold**
- `apps/backend/package.json` — Worker package scripts and dependencies
- `apps/backend/tsconfig.json` — TypeScript config for the backend app
- `apps/backend/turbo.json` — task definitions for build, dev, test, lint, typecheck
- `apps/backend/wrangler.toml` — Worker config, routes, bindings, environments
- `apps/backend/worker/index.ts` — Worker entrypoint exporting `fetch`
- `apps/backend/worker-configuration.d.ts` — generated Worker binding types
- `apps/backend/vitest.config.ts` — backend test runner config

**Core app**
- `apps/backend/src/create-app.ts` — Hono app factory
- `apps/backend/src/env.ts` — typed Worker bindings and runtime env helpers
- `apps/backend/src/config/index.ts` — config normalization and validation
- `apps/backend/src/types/app.types.ts` — backend Hono env/context types
- `apps/backend/src/middlewares/context.ts` — request-scoped service injection
- `apps/backend/src/middlewares/auth.ts` — auth, maybe-auth, and access guards

**Services**
- `apps/backend/src/services/supabase.ts`
- `apps/backend/src/services/stripe.ts`
- `apps/backend/src/services/billing-sync.ts`
- `apps/backend/src/services/mail.ts`
- `apps/backend/src/services/storage.ts`
- `apps/backend/src/services/org-icons.ts`

**Libraries and routes**
- `apps/backend/src/lib/auth.ts`
- `apps/backend/src/lib/billing.ts`
- `apps/backend/src/lib/helpers.ts`
- `apps/backend/src/lib/org-icons.ts`
- `apps/backend/src/lib/slots.ts`
- `apps/backend/src/lib/token.ts`
- `apps/backend/src/routers/index.ts`
- `apps/backend/src/routers/public.ts`
- `apps/backend/src/routers/invite.ts`
- `apps/backend/src/routers/user.ts`
- `apps/backend/src/routers/tablo.ts`
- `apps/backend/src/routers/tablo_data.ts`
- `apps/backend/src/routers/notes.ts`
- `apps/backend/src/routers/tasks.ts`
- `apps/backend/src/routers/clientInvites.ts`
- `apps/backend/src/routers/stripe.ts`
- `apps/backend/src/routers/authRouter.ts`
- `apps/backend/src/routers/maybeAuthRouter.ts`

**Tests**
- `apps/backend/src/__tests__/create-app.test.ts`
- `apps/backend/src/__tests__/config/config.test.ts`
- `apps/backend/src/__tests__/auth/auth.test.ts`
- `apps/backend/src/__tests__/middlewares/middlewares.test.ts`
- `apps/backend/src/__tests__/routes/public.test.ts`
- `apps/backend/src/__tests__/routes/user.test.ts`
- `apps/backend/src/__tests__/routes/tablo.test.ts`
- `apps/backend/src/__tests__/routes/tablo_data.test.ts`
- `apps/backend/src/__tests__/routes/clientInvites.test.ts`
- `apps/backend/src/__tests__/routes/stripe.test.ts`
- `apps/backend/src/__tests__/helpers/*.test.ts`

### Modified files

- `package.json` — add `dev:backend`, `test:backend`, and deploy scripts
- `pnpm-lock.yaml` — workspace dependency lock updates
- `docs/superpowers/specs/2026-04-16-backend-cloudflare-worker-design.md` — only if implementation reveals required spec corrections

### Existing files used as source material

- `apps/api/src/config.ts`
- `apps/api/src/secrets.ts`
- `apps/api/src/middlewares/middleware.ts`
- `apps/api/src/helpers/*`
- `apps/api/src/routers/*`
- `apps/api/src/__tests__/*`

---

## Chunk 1: Scaffold The Worker App

### Task 1: Create `apps/backend` package, Worker config, and smoke test

**Files:**
- Create: `apps/backend/package.json`
- Create: `apps/backend/tsconfig.json`
- Create: `apps/backend/turbo.json`
- Create: `apps/backend/wrangler.toml`
- Create: `apps/backend/worker/index.ts`
- Create: `apps/backend/vitest.config.ts`
- Create: `apps/backend/src/create-app.ts`
- Create: `apps/backend/src/__tests__/create-app.test.ts`
- Modify: `package.json`

- [ ] **Step 1: Write the failing smoke test**

Create `apps/backend/src/__tests__/create-app.test.ts` with a minimal contract check:

```ts
import { describe, expect, it } from "vitest";
import { createApp } from "../create-app";

describe("createApp", () => {
  it("mounts the API under /api/v1", async () => {
    const app = createApp({} as never);
    const res = await app.request("http://example.com/api/v1/unknown");
    expect(res.status).toBe(404);
  });
});
```

- [ ] **Step 2: Run the test to verify it fails**

Run: `pnpm --filter @xtablo/backend test -- --run src/__tests__/create-app.test.ts`

Expected: FAIL with missing package or missing `createApp` export.

- [ ] **Step 3: Add the backend package scaffold**

Create the package and scripts using the same repo conventions as `apps/chat-worker` and `apps/main`.

Minimum scripts:

```json
{
  "dev": "wrangler dev",
  "deploy": "wrangler deploy",
  "typecheck": "tsc --noEmit",
  "test": "vitest run",
  "cf-typegen": "wrangler types"
}
```

Root script additions:

```json
{
  "dev:backend": "turbo dev --filter=@xtablo/backend",
  "test:backend": "turbo test --filter=@xtablo/backend"
}
```

- [ ] **Step 4: Implement the minimal app factory and Worker entrypoint**

Start with:

```ts
import { Hono } from "hono";

export function createApp(_env: unknown) {
  const app = new Hono();
  app.basePath("/api/v1");
  return app;
}
```

And in `worker/index.ts`:

```ts
import { createApp } from "../src/create-app";

export default {
  fetch(request: Request, env: unknown, ctx: ExecutionContext) {
    return createApp(env).fetch(request, env, ctx);
  },
};
```

- [ ] **Step 5: Run the smoke test, typecheck, and generate Worker types**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/create-app.test.ts`
- `pnpm --filter @xtablo/backend typecheck`
- `pnpm --filter @xtablo/backend cf-typegen`

Expected: PASS for the smoke test, no type errors, generated `worker-configuration.d.ts`.

- [ ] **Step 6: Commit**

```bash
git add package.json apps/backend
git commit -m "feat(backend): scaffold cloudflare worker app"
```

### Task 2: Add env/config validation and request-scoped service context

**Files:**
- Create: `apps/backend/src/env.ts`
- Create: `apps/backend/src/config/index.ts`
- Create: `apps/backend/src/types/app.types.ts`
- Create: `apps/backend/src/middlewares/context.ts`
- Create: `apps/backend/src/__tests__/config/config.test.ts`
- Modify: `apps/backend/src/create-app.ts`

- [ ] **Step 1: Write failing config tests**

Create `apps/backend/src/__tests__/config/config.test.ts` with two checks:

```ts
it("throws when SUPABASE_URL is missing", () => {
  expect(() => createConfig({ SUPABASE_URL: "" } as never)).toThrow(/SUPABASE_URL/);
});

it("normalizes staging config", () => {
  const config = createConfig({
    APP_ENV: "staging",
    SUPABASE_URL: "https://example.supabase.co",
    SUPABASE_SERVICE_ROLE_KEY: "srk",
  } as never);

  expect(config.APP_ENV).toBe("staging");
});
```

- [ ] **Step 2: Run the config tests to verify they fail**

Run: `pnpm --filter @xtablo/backend test -- --run src/__tests__/config/config.test.ts`

Expected: FAIL because `createConfig` does not exist yet.

- [ ] **Step 3: Implement typed bindings and config normalization**

Define a Worker env type that includes:
- `APP_ENV`
- `SUPABASE_URL`
- `SUPABASE_SERVICE_ROLE_KEY`
- `STRIPE_SECRET_KEY`
- `STRIPE_WEBHOOK_SECRET`
- `STRIPE_SOLO_PRICE_ID`
- `STRIPE_TEAM_PRICE_ID`
- `STRIPE_FOUNDER_PRICE_ID`
- `XTABLO_URL`
- `CLIENTS_URL`
- `TASKS_SECRET`
- email provider credentials
- storage configuration

And validate them in `createConfig()`.

- [ ] **Step 4: Add request context middleware**

Create a context middleware that:
- builds config from env
- constructs service handles lazily
- stores them on Hono context

Keep the surface small:

```ts
type AppServices = {
  config: AppConfig;
  supabase: SupabaseClient;
};
```

- [ ] **Step 5: Re-run config tests and app smoke test**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/config/config.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/create-app.test.ts`

Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add apps/backend/src/env.ts apps/backend/src/config apps/backend/src/types apps/backend/src/middlewares/context.ts apps/backend/src/__tests__/config/config.test.ts apps/backend/src/create-app.ts
git commit -m "feat(backend): add env validation and request context"
```

---

## Chunk 2: Port The HTTP Surface

### Task 3: Port pure libraries and auth middleware before moving routers

**Files:**
- Create: `apps/backend/src/lib/auth.ts`
- Create: `apps/backend/src/lib/token.ts`
- Create: `apps/backend/src/lib/slots.ts`
- Create: `apps/backend/src/lib/billing.ts`
- Create: `apps/backend/src/lib/helpers.ts`
- Create: `apps/backend/src/middlewares/auth.ts`
- Create: `apps/backend/src/__tests__/auth/auth.test.ts`
- Create: `apps/backend/src/__tests__/middlewares/middlewares.test.ts`
- Create: `apps/backend/src/__tests__/helpers/slots.test.ts`
- Use as source: `apps/api/src/helpers/auth.ts`, `apps/api/src/helpers/token.ts`, `apps/api/src/helpers/slots.ts`, `apps/api/src/helpers/billing.ts`, `apps/api/src/helpers/helpers.ts`, `apps/api/src/middlewares/middleware.ts`

- [ ] **Step 1: Copy the pure helpers and write failing auth tests first**

Start with `auth.test.ts` assertions that verify:
- Bearer token auth succeeds for valid Supabase user
- missing bearer token returns 401
- maybe-auth leaves `user` as `null`

Use a stubbed Supabase client, not the real network.

- [ ] **Step 2: Run the targeted auth and helper tests**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/auth/auth.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/helpers/slots.test.ts`

Expected: FAIL.

- [ ] **Step 3: Port the pure helpers with minimal changes**

Keep the signatures stable. Only remove direct `process.env` reads where required by the Worker app and pass config in explicitly instead.

- [ ] **Step 4: Implement Worker auth middleware**

Split the old `MiddlewareManager` behavior into focused middleware:
- `withSupabase`
- `requireAuth`
- `withMaybeAuth`
- `requireRegularUser`
- `requireBillingCheckoutAccess`
- `requireActivePlanAccess`
- `requireTaskBasicAuth`

- [ ] **Step 5: Re-run targeted tests**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/auth/auth.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/middlewares/middlewares.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/helpers/slots.test.ts`

Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add apps/backend/src/lib apps/backend/src/middlewares/auth.ts apps/backend/src/__tests__/auth apps/backend/src/__tests__/middlewares apps/backend/src/__tests__/helpers
git commit -m "feat(backend): port shared helpers and auth middleware"
```

### Task 4: Port public and maybe-auth routers

**Files:**
- Create: `apps/backend/src/routers/public.ts`
- Create: `apps/backend/src/routers/invite.ts`
- Create: `apps/backend/src/routers/maybeAuthRouter.ts`
- Create: `apps/backend/src/routers/index.ts`
- Create: `apps/backend/src/__tests__/routes/public.test.ts`
- Use as source: `apps/api/src/routers/public.ts`, `apps/api/src/routers/invite.ts`, `apps/api/src/routers/maybeAuthRouter.ts`, `apps/api/src/routers/index.ts`

- [ ] **Step 1: Write failing route tests for public slots, org icon fetch, and booking**

At minimum, cover:
- `GET /api/v1/public/slots/:shortUserId/:standardName`
- `GET /api/v1/public/org-icons/:orgId/:filename`
- `POST /api/v1/book/...` route currently mounted under maybe-auth

- [ ] **Step 2: Run the targeted route tests**

Run: `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/public.test.ts`

Expected: FAIL.

- [ ] **Step 3: Port the routers with context-based dependencies**

Replace direct middleware singleton calls with imported middleware functions and service access from Hono context.

- [ ] **Step 4: Wire the routers into `createApp()`**

Mount `/api/v1` exactly as in `apps/api`, preserving route order:
- public routes
- tasks/basic-auth routes
- Stripe webhook
- maybe-auth routes
- authenticated routes

- [ ] **Step 5: Re-run the public route tests**

Run: `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/public.test.ts`

Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add apps/backend/src/routers/public.ts apps/backend/src/routers/invite.ts apps/backend/src/routers/maybeAuthRouter.ts apps/backend/src/routers/index.ts apps/backend/src/__tests__/routes/public.test.ts apps/backend/src/create-app.ts
git commit -m "feat(backend): port public and maybe-auth routes"
```

### Task 5: Port authenticated route families in contract-preserving slices

**Files:**
- Create: `apps/backend/src/routers/authRouter.ts`
- Create: `apps/backend/src/routers/user.ts`
- Create: `apps/backend/src/routers/tablo.ts`
- Create: `apps/backend/src/routers/tablo_data.ts`
- Create: `apps/backend/src/routers/notes.ts`
- Create: `apps/backend/src/routers/tasks.ts`
- Create: `apps/backend/src/routers/clientInvites.ts`
- Create: `apps/backend/src/__tests__/routes/user.test.ts`
- Create: `apps/backend/src/__tests__/routes/tablo.test.ts`
- Create: `apps/backend/src/__tests__/routes/tablo_data.test.ts`
- Create: `apps/backend/src/__tests__/routes/clientInvites.test.ts`
- Use as source: `apps/api/src/routers/*.ts`, `apps/api/src/__tests__/routes/*.test.ts`

- [ ] **Step 1: Copy the highest-value existing route tests before porting code**

Port these test files first, adapting imports only:
- `user.test.ts`
- `tablo.test.ts`
- `tablo_data.test.ts`
- `clientInvites.test.ts`

Prefer the current expectations verbatim so the new app is measured against the old contract.

- [ ] **Step 2: Run the route tests to capture failures**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/user.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/tablo.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/tablo_data.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/clientInvites.test.ts`

Expected: FAIL across missing routers and missing services.

- [ ] **Step 3: Port one router family at a time**

Order:
1. `user.ts`
2. `clientInvites.ts`
3. `notes.ts`
4. `tasks.ts`
5. `tablo.ts`
6. `tablo_data.ts`

Keep each port minimal:
- preserve handler shape
- move direct env access to config
- replace direct transport/storage access with service calls

- [ ] **Step 4: Re-run targeted tests after each router family**

Do not batch all routers before checking. After each family:

```bash
pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/<file>.test.ts
```

- [ ] **Step 5: Run the combined authenticated route suite**

Run:

```bash
pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/user.test.ts src/__tests__/routes/tablo.test.ts src/__tests__/routes/tablo_data.test.ts src/__tests__/routes/clientInvites.test.ts
```

Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add apps/backend/src/routers apps/backend/src/__tests__/routes
git commit -m "feat(backend): port authenticated route families"
```

---

## Chunk 3: Replace Node-Only Runtime Dependencies

### Task 6: Implement storage, org icon, and mail adapters

**Files:**
- Create: `apps/backend/src/services/storage.ts`
- Create: `apps/backend/src/services/org-icons.ts`
- Create: `apps/backend/src/services/mail.ts`
- Create: `apps/backend/src/lib/org-icons.ts`
- Modify: `apps/backend/src/routers/public.ts`
- Modify: `apps/backend/src/routers/user.ts`
- Modify: `apps/backend/src/routers/tablo_data.ts`
- Create: `apps/backend/src/__tests__/routes/org-icons.test.ts`

- [ ] **Step 1: Write failing tests around storage-backed routes**

Add tests that verify:
- org icon fetch returns the same content-type and fallback behavior
- avatar upload/delete preserves status codes
- tablo file get/post/delete preserves route behavior

- [ ] **Step 2: Run the targeted tests**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/org-icons.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/tablo_data.test.ts`

Expected: FAIL.

- [ ] **Step 3: Implement the storage service interface**

Start with:

```ts
export interface StorageService {
  get(key: string, bucket: "web-assets" | "tablo-data"): Promise<StoredObject | null>;
  put(input: PutStoredObjectInput): Promise<void>;
  head(key: string, bucket: "web-assets" | "tablo-data"): Promise<StoredObjectMeta | null>;
  delete(key: string, bucket: "web-assets" | "tablo-data"): Promise<void>;
  list(prefix: string, bucket: "web-assets" | "tablo-data"): Promise<string[]>;
}
```

- [ ] **Step 4: Implement Worker-compatible org icon and mail services**

Requirements:
- org icon service owns validation, resizing, upload, and fetch
- mail service exposes `sendMail({ to, subject, text, html })`
- routers do not import AWS SDK, `sharp`, or `nodemailer` directly

- [ ] **Step 5: Re-run storage and org-icon tests**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/org-icons.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/user.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/tablo_data.test.ts`

Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add apps/backend/src/services/storage.ts apps/backend/src/services/org-icons.ts apps/backend/src/services/mail.ts apps/backend/src/lib/org-icons.ts apps/backend/src/routers/public.ts apps/backend/src/routers/user.ts apps/backend/src/routers/tablo_data.ts apps/backend/src/__tests__/routes/org-icons.test.ts
git commit -m "feat(backend): replace storage, mail, and icon adapters"
```

### Task 7: Replace Stripe sync engine with Worker-side billing sync

**Files:**
- Create: `apps/backend/src/services/stripe.ts`
- Create: `apps/backend/src/services/billing-sync.ts`
- Modify: `apps/backend/src/routers/stripe.ts`
- Create: `apps/backend/src/__tests__/routes/stripe.test.ts`
- Use as source: `apps/api/src/routers/stripe.ts`, `apps/api/src/middlewares/stripeSync.ts`

- [ ] **Step 1: Port the current Stripe tests first**

Copy and adapt `apps/api/src/__tests__/routes/stripe.test.ts` into the backend test tree.

- [ ] **Step 2: Run the Stripe route tests**

Run: `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/stripe.test.ts`

Expected: FAIL.

- [ ] **Step 3: Implement the Stripe service and billing sync service**

The Stripe service should own:
- SDK construction
- webhook signature verification
- checkout-session helpers

The billing sync service should own:
- event parsing after verification
- Supabase writes required by current billing reads
- idempotent handling for repeated webhook delivery

- [ ] **Step 4: Update the Stripe router to use those services**

Preserve:
- route paths
- request body expectations
- status codes
- checkout behavior visible to clients

- [ ] **Step 5: Re-run Stripe tests and a broader route sweep**

Run:
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/stripe.test.ts`
- `pnpm --filter @xtablo/backend test -- --run src/__tests__/routes/user.test.ts src/__tests__/routes/tablo.test.ts`

Expected: PASS.

- [ ] **Step 6: Commit**

```bash
git add apps/backend/src/services/stripe.ts apps/backend/src/services/billing-sync.ts apps/backend/src/routers/stripe.ts apps/backend/src/__tests__/routes/stripe.test.ts
git commit -m "feat(backend): replace stripe sync engine with worker billing sync"
```

---

## Chunk 4: Verify Parity And Deployment Readiness

### Task 8: Add deploy scripts, environment routes, and full verification suite

**Files:**
- Modify: `apps/backend/wrangler.toml`
- Modify: `package.json`
- Modify: `apps/backend/package.json`
- Create: `docs/superpowers/reports/2026-04-16-backend-cloudflare-worker-verification.md`

- [ ] **Step 1: Finalize Wrangler environments and root scripts**

Ensure `wrangler.toml` has:
- base Worker name
- `env.staging` route for `api-staging.xtablo.com`
- `env.production` route for `api.xtablo.com`
- required vars placeholders

And root scripts for:
- `dev:backend`
- `test:backend`
- `deploy:backend:staging`
- `deploy:backend:prod`

- [ ] **Step 2: Run the backend test suite**

Run:

```bash
pnpm --filter @xtablo/backend test
pnpm --filter @xtablo/backend typecheck
turbo lint --filter=@xtablo/backend
```

Expected: PASS.

- [ ] **Step 3: Run focused regression checks against the old API contract**

Run the legacy API tests for the same route families where practical and compare expectations:

```bash
pnpm --filter @xtablo/api test -- --run src/__tests__/routes/public.test.ts
pnpm --filter @xtablo/api test -- --run src/__tests__/routes/user.test.ts
pnpm --filter @xtablo/api test -- --run src/__tests__/routes/stripe.test.ts
```

Expected: the backend assertions match the practical old contract.

- [ ] **Step 4: Write the manual parity checklist report**

Record manual checks for:
- auth/login-dependent flows
- public slots and booking
- tablo read/write flows
- file upload/download/delete
- avatar upload/delete
- client invites
- checkout session creation
- Stripe webhook handling in staging

- [ ] **Step 5: Commit**

```bash
git add package.json apps/backend/wrangler.toml apps/backend/package.json docs/superpowers/reports/2026-04-16-backend-cloudflare-worker-verification.md
git commit -m "chore(backend): finalize deploy config and verification"
```

---

## Notes For The Implementer

- Keep `apps/api` untouched unless a shared helper absolutely must move.
- Prefer copying code into `apps/backend` first, then simplifying behind service interfaces once tests are in place.
- Do not port `apps/api/src/index.ts` or `apps/api/src/secrets.ts` directly. Those files are reference material only.
- Avoid direct `process.env` reads in route files. Resolve config once from Worker env and pass it through context.
- Avoid direct imports of `nodemailer`, `sharp`, or `@supabase/stripe-sync-engine` anywhere in `apps/backend`.
- Preserve route ordering from `apps/api/src/routers/index.ts`; some behavior depends on middleware order.
- Keep commits small and scoped to one task whenever possible.

## Completion Criteria

- `apps/backend` exists as a deployable Cloudflare Worker app.
- `apps/backend` serves the current `/api/v1` contract.
- the backend test suite passes.
- staging and production routes are configured.
- a manual parity report exists for web app validation against `api-staging.xtablo.com` and `api.xtablo.com`.

Plan complete and saved to `docs/superpowers/plans/2026-04-16-backend-cloudflare-worker.md`. Ready to execute?
