{"type":"meta","schema":"session-state/v1","slug":"plugin-backend-core","topic":"Plugin-backend platform on Cloudflare Workers, assembled from @platform-modules; translation is first client service","created":"2026-07-03","exec_mode":"run-plan-codex","scheduler":"dag-parallel","base_branch":"main","land_mode":"pr","land_target":"main","codex_impl_model":"gpt-5.4","codex_impl_effort":"high","status_enum":["BLOCKED","PENDING","WORKING","WRITTEN","REVIEWED","COMMITTED"],"truth_rule":"this file is ground truth for INTENT (goals/directions/task list); git is ground truth for DONE-NESS — reconcile statuses against git, never trust a COMMITTED line with no commit","refresh_how":"native Read of THIS file — NEVER ctx_execute/ctx_search","refresh_what":"meta + every goal/methodology/intent/direction record + all WORKING and BLOCKED tasks (skip COMMITTED)","refresh_when":["wave boundary","any task status change","after a compaction system-reminder","before calling advisor"],"update_rule":"on status change, rewrite that task's single line in place; append new tasks/session_memory as new lines","report_rule":"at session end emit brief: done / blocked / deferred / canceled","note":"run-plan-codex reads meta+task; promptbuilder pulls pinned contract from the .md anchor by (wave, positional order within wave) — task order in THIS file per wave MUST match the .md ### Task section order per wave. Launch: node /home/user/.claude/workflows/run-plan-codex.js --slug plugin-backend-core --repo /home/user/Projects/platform --concurrency 4"}
{"type":"goal","text":"Build a greenfield plugin-backend PLATFORM on Cloudflare Workers, assembled maximally from ~/Projects/platform @platform-modules, serving as shared backend (auth / API keys / subscriptions / billing / entitlements / ledger / invoicing) for ALL Press.Zone WordPress plugins. Translation is the FIRST client plugin service (deferred to a later spec)."}
{"type":"methodology","text":"Consume platform MODULES behind their seams; contribute genuine gaps UPSTREAM as new modules/subpaths, never as app-local islands. Contract-level plan (signatures/seams, no bodies). DB-backed tests use real pglite (mirror packages/ledger/src/test-fixture.ts). Each package change ships a changeset + regenerates subpath exports. Execute wave-by-wave via run-plan-codex; land via PR to main."}
{"type":"intent","text":"One robust, reusable backend so every Press.Zone plugin gets auth/billing/entitlements/metering for free — 'our app is a lego piece of cake'. Avoid bespoke re-implementations that rot."}
{"type":"direction","verbatim":"use ~/Projects/platform/ modules as much as possible for this project, do not write anythign from 0 if you dont have to. also, suggest new modules if needed, our app is going to be a lego piece of cake"}
{"type":"direction","verbatim":"when building new modules for platform-modules, you must pass to the agents to laod the platform's CLAUDE.md, skills, memories, etc,"}
{"type":"direction","verbatim":"go with Option A. run-plan-codex.js"}
{"type":"direction","text":"Do NOT template the app off any platform app (mod-cms/mod-storefront/consumer are distros, irrelevant). Build apps/press-zone as its own Hono scaffold per platform coding-standard; consume MODULES only, behind seams.","verbatim":"what does mod-cms have to do with any of it? mod-cms is a distro... nothing to do with our project"}
{"type":"anchor","path":"docs/specs/2026-07-03-plugin-backend-core-design.md","what":"spec"}
{"type":"anchor","path":"docs/plans/2026-07-03-plugin-backend-core.md","what":"plan"}
{"type":"session_memory","note":"Codex plan schema/template = docs/plans/2026-07-03-mcp-agent-bearer.jsonl (+ .md). Tasks use title+acceptance+files_create+files_modify+blocks+blocked_by. NO gate0_cmd (worktrees have no node_modules → gate0 auto-skips; codex implementer installs + runs the acceptance turbo command itself). Waves are 1-based (run-plan-codex does wave||1, collapsing 0→1). Deps strictly lower-wave so blocked_by are COMMITTED before a wave runs."}
{"type":"session_memory","note":"BLOCKER RESOLUTIONS: (1) DB test harness = @electric-sql/pglite + local src/test-fixture.ts createTestDb(), exemplar packages/ledger/src/{test-fixture,debit}.test.ts. (2) Changeset NOT hook-enforced (lefthook pre-push = pnpm gate + block-main-push only; changesets consumed by release.yml on main) — run won't halt, but each pkg task adds a changeset as release hygiene. (3) New subpaths MUST run pnpm gen:subpath-exports else check:subpath-exports (in verify→gate) fails."}
{"type":"session_memory","note":"Confirmed: entitlements + invoicing packages ABSENT → net-new. billing exports .,./stripe,./sumit (paypal/subscriptions new). auth exports .,./engine-custom,./engine-better-auth,./otp-email (api-keys/oauth-provider new). tax exports ./rates-table (taxability new). tenancy seam resolveCapabilities(RbacResolveInput):Promise<string[]>. App scope = @app/press-zone. auth-react + billing-react exist for SPA."}
{"type":"session_memory","note":"RECOMMENDED at launch: run waves 1-2 (module contributions, t1-t9) first, verify green+reviewed, THEN resume app waves — reduces blast radius on live-money code, catches contract errors before app builds on them. run-plan-codex resumes (COMMITTED tasks skipped on rerun). ~25 codex tasks may brush codex 5h/7d limits — resumable."}
{"type":"task","id":"pbc.t1","wave":1,"status":"COMMITTED","title":"entitlements package (NEW @platform-modules/entitlements): can/quota/setTier + drizzle table + typed errors","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/entitlements --concurrency=4 exits 0, zero warnings; pglite tests (mirror ledger test-fixture): can true-live/false-missing/false-expired, quota arithmetic, setTier idempotent, expiry boundary","files_create":["packages/entitlements/package.json","packages/entitlements/tsconfig.json","packages/entitlements/src/index.ts","packages/entitlements/src/types.ts","packages/entitlements/src/errors.ts","packages/entitlements/src/schema.ts","packages/entitlements/src/test-fixture.ts","packages/entitlements/src/index.test.ts",".changeset/pbc-entitlements.md"],"files_modify":[],"blocks":["pbc.t10","pbc.t12","pbc.t17","pbc.t21"],"blocked_by":[]}
{"type":"task","id":"pbc.t2","wave":1,"status":"COMMITTED","title":"tax taxability seam (extend @platform-modules/tax ./taxability): resolveTaxability above rates-table","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/tax --concurrency=4 exits 0, zero warnings; check:subpath-exports green; tests: IL→IL standard, IL→US zero_rated, treatment→rate via rates-table, exhaustiveness (never-check) over standard/zero_rated/exempt — exempt is a reserved v1-unreached treatment, no fabricated business rule for it","files_create":["packages/tax/src/taxability.ts","packages/tax/src/taxability.test.ts",".changeset/pbc-tax-taxability.md"],"files_modify":["packages/tax/package.json"],"blocks":["pbc.t15"],"blocked_by":[]}
{"type":"task","id":"pbc.t3","wave":1,"status":"COMMITTED","title":"invoicing package core (NEW @platform-modules/invoicing): issueInvoice + InvoiceProvider port + DocumentSpec + idempotency + mock provider","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/invoicing --concurrency=4 exits 0, zero warnings; pglite tests: issue via mock returns doc, same idempotencyKey → provider called once + identical result, provider error → ok:false + no record","files_create":["packages/invoicing/package.json","packages/invoicing/tsconfig.json","packages/invoicing/src/index.ts","packages/invoicing/src/types.ts","packages/invoicing/src/errors.ts","packages/invoicing/src/port.ts","packages/invoicing/src/mock-provider.ts","packages/invoicing/src/test-fixture.ts","packages/invoicing/src/index.test.ts",".changeset/pbc-invoicing.md"],"files_modify":[],"blocks":["pbc.t7","pbc.t10","pbc.t15"],"blocked_by":[]}
{"type":"task","id":"pbc.t4","wave":1,"status":"COMMITTED","title":"billing/subscriptions subpath (NEW @platform-modules/billing ./subscriptions): SubscriptionProvider port + ProviderEvent settlement kind + ingestWebhook (posts money-audit via confirmSettlement, no IntentStore)","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/billing --concurrency=4 exits 0, zero warnings; check:subpath-exports green; pglite tests: settlement webhook → event chargeKey=sub:period, double-ingest same charge → single ledger_entries row, createSubscription idempotent, ProviderEvent union typechecks","files_create":["packages/billing/src/subscriptions/index.ts","packages/billing/src/subscriptions/types.ts","packages/billing/src/subscriptions/port.ts","packages/billing/src/subscriptions/ingest.ts","packages/billing/src/subscriptions/test-fixture.ts","packages/billing/src/subscriptions/index.test.ts",".changeset/pbc-billing-subscriptions.md"],"files_modify":["packages/billing/src/index.ts","packages/billing/package.json"],"blocks":["pbc.t8","pbc.t10","pbc.t17","pbc.t18"],"blocked_by":[]}
{"type":"task","id":"pbc.t5","wave":1,"status":"PENDING","title":"auth/api-keys subpath (NEW @platform-modules/auth ./api-keys): issueApiKey/verifyApiKey Bearer prefix.secret, crypto.subtle hash, scopes, drizzle api_keys table","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/auth --concurrency=4 exits 0, zero warnings; check:subpath-exports green; pglite tests: issue→verify roundtrip, wrong secret→null, unknown prefix→null, scopes preserved, stored secret ≠ raw","files_create":["packages/auth/src/api-keys/index.ts","packages/auth/src/api-keys/types.ts","packages/auth/src/api-keys/errors.ts","packages/auth/src/api-keys/schema.ts","packages/auth/src/api-keys/test-fixture.ts","packages/auth/src/api-keys/index.test.ts",".changeset/pbc-auth-api-keys.md"],"files_modify":["packages/auth/package.json"],"blocks":["pbc.t9","pbc.t10","pbc.t11","pbc.t13"],"blocked_by":[]}
{"type":"task","id":"pbc.t6","wave":1,"status":"PENDING","title":"app scaffold (NEW @app/press-zone): Hono worker entry {fetch,scheduled,queue}, envelope http.ts, config, db client, rate-limit mw, health route, wrangler.toml bindings — per platform coding-standard, NOT off any apps/* distro","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: ok/fail envelope shapes, bigint field in ok(data) serializes to decimal string (no throw), /health 200 {data:{ok:true}}, rate-limit allows N blocks N+1 (429 envelope), config throws on missing var","files_create":["apps/press-zone/package.json","apps/press-zone/tsconfig.json","apps/press-zone/wrangler.toml","apps/press-zone/vitest.config.ts","apps/press-zone/src/index.ts","apps/press-zone/src/http.ts","apps/press-zone/src/config.ts","apps/press-zone/src/errors.ts","apps/press-zone/src/db.ts","apps/press-zone/src/mw/rate-limit.ts","apps/press-zone/src/routes/health.ts","apps/press-zone/src/http.test.ts","apps/press-zone/src/config.test.ts"],"files_modify":[],"blocks":["pbc.t10","pbc.t22"],"blocked_by":[]}
{"type":"task","id":"pbc.t7","wave":2,"status":"PENDING","title":"invoicing/morning adapter (@platform-modules/invoicing ./morning): MorningProvider implements InvoiceProvider, Green Invoice token exchange + create-document via fetch, creds {apiUser,apiPass,companyId}","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/invoicing --concurrency=4 exits 0, zero warnings; check:subpath-exports green; tests (fetch mocked): token exchange→bearer on create, success maps response→doc, API error→typed error","files_create":["packages/invoicing/src/morning/index.ts","packages/invoicing/src/morning/client.ts","packages/invoicing/src/morning/types.ts","packages/invoicing/src/morning/index.test.ts",".changeset/pbc-invoicing-morning.md"],"files_modify":["packages/invoicing/package.json"],"blocks":["pbc.t15"],"blocked_by":["pbc.t3"]}
{"type":"task","id":"pbc.t8","wave":2,"status":"PENDING","title":"billing/paypal adapter (@platform-modules/billing ./paypal): PaypalProvider implements PaymentProvider & SubscriptionProvider, parseWebhook sig-verify→ProviderEvent, createSubscription/cancel/get, refund — web-standard fetch/crypto","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/billing --concurrency=4 exits 0, zero warnings; check:subpath-exports green; tests: valid sig→settlement event stable chargeKey, invalid sig→error no event, refund webhook→refund event, createSubscription round-trips","files_create":["packages/billing/src/paypal/index.ts","packages/billing/src/paypal/client.ts","packages/billing/src/paypal/webhook.ts","packages/billing/src/paypal/types.ts","packages/billing/src/paypal/index.test.ts",".changeset/pbc-billing-paypal.md"],"files_modify":["packages/billing/package.json"],"blocks":["pbc.t17","pbc.t18","pbc.t21"],"blocked_by":["pbc.t4"]}
{"type":"task","id":"pbc.t9","wave":2,"status":"PENDING","title":"auth/oauth-provider subpath (@platform-modules/auth ./oauth-provider): authorize/exchangeToken/registerClient/getClient, PKCE S256, module-owned oauth_clients/codes/tokens tables","acceptance":"pnpm turbo typecheck build test --filter=@platform-modules/auth --concurrency=4 exits 0, zero warnings; check:subpath-exports green; pglite tests: authorize→code, exchange valid verifier→token, wrong verifier→error, code single-use, plain rejected, registerClient/getClient roundtrip","files_create":["packages/auth/src/oauth-provider/index.ts","packages/auth/src/oauth-provider/schema.ts","packages/auth/src/oauth-provider/types.ts","packages/auth/src/oauth-provider/pkce.ts","packages/auth/src/oauth-provider/errors.ts","packages/auth/src/oauth-provider/test-fixture.ts","packages/auth/src/oauth-provider/index.test.ts",".changeset/pbc-auth-oauth-provider.md"],"files_modify":["packages/auth/package.json"],"blocks":["pbc.t10","pbc.t16"],"blocked_by":["pbc.t5"]}
{"type":"task","id":"pbc.t10","wave":3,"status":"PENDING","title":"app domain schema + migration (@app/press-zone): compose module-owned tables (entitlements, api_keys, oauth_*, ledger wallet+entries, subscriptions) + app-owned (accounts, plugins, subscription_packages, sites, credentials, seats/members); 0001_init.sql; period-key convention; declare app's @platform-modules/* deps (entitlements,invoicing,billing,auth,tax,ledger,tenancy,audit) as workspace:^ + @electric-sql/pglite in devDependencies (app's first pglite test; t14/t18/t25 inherit) in package.json — single home so Waves 4-5 importers inherit","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; pglite tests: migration applies clean, all module+app tables queryable, wallet-key helper formats <account>:<plugin>:<period>","files_create":["apps/press-zone/src/schema.ts","apps/press-zone/drizzle/0001_init.sql","apps/press-zone/src/schema.test.ts"],"files_modify":["apps/press-zone/package.json"],"blocks":["pbc.t11","pbc.t12","pbc.t13","pbc.t14","pbc.t15","pbc.t20"],"blocked_by":["pbc.t1","pbc.t3","pbc.t4","pbc.t5","pbc.t9","pbc.t6"]}
{"type":"task","id":"pbc.t11","wave":4,"status":"PENDING","title":"mw/auth (@app/press-zone): requireAuth resolves principal from session/api-key (verifyApiKey) + tenancy resolveCapabilities; 401 envelope on failure","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: valid bearer→principal, invalid→401, capabilities attached","files_create":["apps/press-zone/src/mw/auth.ts","apps/press-zone/src/mw/auth.test.ts"],"files_modify":[],"blocks":["pbc.t16","pbc.t17","pbc.t19","pbc.t20"],"blocked_by":["pbc.t10","pbc.t5"]}
{"type":"task","id":"pbc.t12","wave":4,"status":"PENDING","title":"mw/access (@app/press-zone): requirePluginAccess(feature) = entitlements.can false→402 PLUGIN_NOT_ENTITLED ∩ tenancy capability absent→403 FORBIDDEN","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: entitled+capable→next, no entitlement→402, no capability→403","files_create":["apps/press-zone/src/mw/access.ts","apps/press-zone/src/mw/access.test.ts"],"files_modify":[],"blocks":["pbc.t17","pbc.t19","pbc.t20"],"blocked_by":["pbc.t10","pbc.t1"]}
{"type":"task","id":"pbc.t13","wave":4,"status":"PENDING","title":"lib/credential (@app/press-zone): resolveCredential(account,site,provider) over credentials link table, api-key-backed; typed not-found","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: resolve hit, miss→typed error","files_create":["apps/press-zone/src/lib/credential.ts","apps/press-zone/src/lib/credential.test.ts"],"files_modify":[],"blocks":["pbc.t16","pbc.t19","pbc.t20"],"blocked_by":["pbc.t10","pbc.t5"]}
{"type":"task","id":"pbc.t14","wave":4,"status":"PENDING","title":"lib/wallet (@app/press-zone): periodKey <account>:<plugin>:<period>, seedPeriodWallet upsert walletBalances (host seeding, NOT appendEntry), debitCredits guarded UPDATE WHERE balance>=amount (race-safe)","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; pglite tests (mirror ledger debit.test): seed+debit ok, over-balance→false unchanged, concurrent double-debit of 1-credit balance → exactly one succeeds","files_create":["apps/press-zone/src/lib/wallet.ts","apps/press-zone/src/lib/wallet.test.ts"],"files_modify":[],"blocks":["pbc.t17","pbc.t18","pbc.t19","pbc.t21"],"blocked_by":["pbc.t10"]}
{"type":"task","id":"pbc.t15","wave":4,"status":"PENDING","title":"lib/billing-doc (@app/press-zone): issueSettlementInvoice = tax.resolveTaxability → rate via rates-table → DocumentSpec → invoicing.issueInvoice(MorningProvider); idempotent on idempotencyKey","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests (mock Morning): domestic 18% line, export 0%, idempotencyKey dedup (no 2nd document)","files_create":["apps/press-zone/src/lib/billing-doc.ts","apps/press-zone/src/lib/billing-doc.test.ts"],"files_modify":[],"blocks":["pbc.t18"],"blocked_by":["pbc.t10","pbc.t2","pbc.t3","pbc.t7"]}
{"type":"task","id":"pbc.t16","wave":5,"status":"PENDING","title":"routes/oauth (@app/press-zone): authorize/callback/token delegating to auth/oauth-provider, PKCE S256, persist site credential; envelope; does NOT edit src/index.ts","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: authorize returns code, token exchange stores credential, bad PKCE→400 envelope","files_create":["apps/press-zone/src/routes/oauth.ts","apps/press-zone/src/routes/oauth.test.ts"],"files_modify":[],"blocks":["pbc.t22"],"blocked_by":["pbc.t9","pbc.t11","pbc.t13"]}
{"type":"task","id":"pbc.t17","wave":5,"status":"PENDING","title":"routes/subscribe (@app/press-zone): PaypalProvider.createSubscription → entitlements.setTier → seedPeriodWallet(first period allocation); idempotent per account+package","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests (mock PayPal): happy path sets tier + seeds wallet, idempotent repeat, provider error rolls back (no tier/wallet)","files_create":["apps/press-zone/src/routes/subscribe.ts","apps/press-zone/src/routes/subscribe.test.ts"],"files_modify":[],"blocks":["pbc.t22"],"blocked_by":["pbc.t8","pbc.t4","pbc.t1","pbc.t14","pbc.t12","pbc.t11"]}
{"type":"task","id":"pbc.t18","wave":5,"status":"PENDING","title":"routes/webhooks (@app/press-zone): billing.ingestWebhook (module posts money-audit) → dispatch does ONLY credit-reset via NEW period wallet + issueSettlementInvoice; idempotent on chargeKey/period NEVER eventId, never re-posts money; refund→credit-note","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; pglite+mock tests: settlement seeds next-period wallet + one invoice (no money re-post), redeliver same chargeKey → no 2nd seed/invoice/ledger row, refund → one credit-note","files_create":["apps/press-zone/src/routes/webhooks.ts","apps/press-zone/src/routes/webhooks.test.ts"],"files_modify":[],"blocks":["pbc.t22"],"blocked_by":["pbc.t8","pbc.t4","pbc.t14","pbc.t15"]}
{"type":"task","id":"pbc.t19","wave":5,"status":"PENDING","title":"routes/plugin (@app/press-zone): metered gate requireAuth→requirePluginAccess(feature)→debitCredits(currentPeriodKey,cost); debit false→402 INSUFFICIENT_CREDITS; downstream failure after debit→compensating refund","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: happy path debits once, insufficient→402 no debit-of-record, downstream failure refunds","files_create":["apps/press-zone/src/routes/plugin.ts","apps/press-zone/src/routes/plugin.test.ts"],"files_modify":[],"blocks":["pbc.t22"],"blocked_by":["pbc.t12","pbc.t14","pbc.t11","pbc.t13"]}
{"type":"task","id":"pbc.t20","wave":5,"status":"PENDING","title":"routes/account (@app/press-zone): customer dashboard API — subscriptions/sites/seats/members+roles/invoices/credit-balance scoped to authenticated account (tenancy); envelope","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: list scoped to account, cross-account read→403, member role update path","files_create":["apps/press-zone/src/routes/account.ts","apps/press-zone/src/routes/account.test.ts"],"files_modify":[],"blocks":["pbc.t22"],"blocked_by":["pbc.t10","pbc.t11","pbc.t12","pbc.t13"]}
{"type":"task","id":"pbc.t21","wave":5,"status":"PENDING","title":"routes/staff (@app/press-zone): staff-gated ops — adjust credits, refund (PaypalProvider.refund + credit-note), tier override (entitlements.setTier), audit read; every mutation audited","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; tests: non-staff→403, credit adjust audited, refund path","files_create":["apps/press-zone/src/routes/staff.ts","apps/press-zone/src/routes/staff.test.ts"],"files_modify":[],"blocks":["pbc.t22"],"blocked_by":["pbc.t14","pbc.t1","pbc.t8"]}
{"type":"task","id":"pbc.t22","wave":6,"status":"PENDING","title":"server mount + scheduled + queue + wrangler finalize (@app/press-zone): TWO mount groups — public (webhooks, oauth authorize/callback/token — platform IS the authz server, /token is a back-channel call with code+PKCE verifier, NO user session, must NOT sit behind requireAuth, health) NO requireAuth, authenticated (subscribe, plugin, account, staff) requireAuth→requirePluginAccess; rate-limit on all; scheduled()→cron.ts housekeeping; queue()→queue.ts consumer; finalize bindings","acceptance":"pnpm turbo typecheck build test --filter=@app/press-zone --concurrency=4 exits 0, zero warnings; integration test drives health + one guarded route; unauthenticated webhook POST AND unauthenticated /oauth/token POST each reach handler (NOT 401) while authenticated-group route returns 401; unmounted route→404 envelope","files_create":["apps/press-zone/src/cron.ts","apps/press-zone/src/queue.ts","apps/press-zone/src/index.test.ts"],"files_modify":["apps/press-zone/src/index.ts","apps/press-zone/wrangler.toml"],"blocks":["pbc.t23","pbc.t24","pbc.t25"],"blocked_by":["pbc.t6","pbc.t16","pbc.t17","pbc.t18","pbc.t19","pbc.t20","pbc.t21"]}
{"type":"task","id":"pbc.t23","wave":7,"status":"PENDING","title":"customer dashboard SPA shell (@app/press-zone-web, own workspace member — glob is apps/* so nested apps/press-zone/web would NOT be a member): React+Vite consuming auth-react + billing-react; screens login/connect, subscribe/plan, credits+usage, invoices, sites/seats; real envelope-aware API calls; thin shell","acceptance":"pnpm turbo build test --filter=@app/press-zone-web --concurrency=4 exits 0, zero warnings; render tests for login + credits screens with mocked API","files_create":["apps/press-zone-web/package.json","apps/press-zone-web/vite.config.ts","apps/press-zone-web/index.html","apps/press-zone-web/src/main.tsx","apps/press-zone-web/src/api.ts","apps/press-zone-web/src/routes/dashboard.tsx","apps/press-zone-web/src/routes/dashboard.test.tsx"],"files_modify":[],"blocks":[],"blocked_by":["pbc.t22"]}
{"type":"task","id":"pbc.t24","wave":7,"status":"PENDING","title":"staff SPA shell (@app/press-zone-web staff area): accounts search, credit adjust, refunds, audit log view; consumes staff routes; thin shell","acceptance":"pnpm turbo build test --filter=@app/press-zone-web --concurrency=4 exits 0, zero warnings; render test for accounts + credit-adjust with mocked API","files_create":["apps/press-zone-web/src/staff/index.tsx","apps/press-zone-web/src/staff/accounts.tsx","apps/press-zone-web/src/staff/accounts.test.tsx"],"files_modify":[],"blocks":[],"blocked_by":["pbc.t22"]}
{"type":"task","id":"pbc.t25","wave":8,"status":"PENDING","title":"HTTP integration flows (@app/press-zone): connect, subscribe, webhook-idempotency (double-deliver→single effect), metered-debit (+insufficient 402), staff-refund→credit-note; driven via app.request() against mounted Hono app over pglite, mocked PayPal/Morning — NOT a browser tool (SPA render covered by t23-24)","acceptance":"pnpm turbo test --filter=@app/press-zone --concurrency=4 green (integration suite included); five flows pass; webhook double-delivery asserts single wallet seed + single invoice + no extra ledger_entries","files_create":["apps/press-zone/tests/integration/connect.test.ts","apps/press-zone/tests/integration/subscribe.test.ts","apps/press-zone/tests/integration/webhook-idempotency.test.ts","apps/press-zone/tests/integration/metered-debit.test.ts","apps/press-zone/tests/integration/staff-refund.test.ts"],"files_modify":[],"blocks":[],"blocked_by":["pbc.t22"]}
