---
phase: 23-rewards-removal-backend-frontend-docs
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  # New migration
  - packages/server/src/db/migrations/005_drop_rewards.ts
  - packages/server/src/db/migrations/index.ts
  # Server — partial edits (whole-file deletions also touched via `git rm`)
  - packages/server/src/server.ts
  - packages/server/src/middleware/auth.ts
  - packages/server/src/db/index.ts
  - packages/server/src/db/types.ts
  - packages/server/src/db/facilitators.ts
  - packages/server/src/routes/admin.ts
  - packages/server/src/routes/internal-webhooks.ts
  - packages/server/src/utils/evm-verify.ts
  - packages/server/.env.example
  # Dashboard — partial edits
  - apps/dashboard/src/lib/api.ts
  - apps/dashboard/src/lib/evm/verification.ts
  - apps/dashboard/src/lib/solana/verification.ts
  - apps/dashboard/src/app/dashboard/[id]/page.tsx
  - apps/dashboard/src/app/dashboard/page.tsx
  - apps/dashboard/src/app/page.tsx
  - apps/dashboard/src/components/navbar.tsx
  - apps/dashboard/src/components/user-menu.tsx
  - apps/dashboard/src/components/archive/wallet-dropdown.tsx
  - apps/dashboard/src/components/auth/auth-provider.tsx
  - apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts
  - apps/dashboard/src/components/dashboard/FeaturesSpotlight/index.tsx
  # Docs / planning
  - .planning/REQUIREMENTS.md
  - .planning/PROJECT.md
  - skills/openfacilitator/references/sdk-api.md
autonomous: true
requirements:
  - REWARDS-01
  - REWARDS-02
  - REWARDS-03
  - REWARDS-04
  - REWARDS-05
  - REWARDS-06
  - REWARDS-07
  - REWARDS-08
  - REWARDS-09
  - REWARDS-10

must_haves:
  truths:
    - "D-03: D-14: Grep gate: `grep -riE 'reward|campaign|\\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' packages/ apps/ skills/openfacilitator/ --include='*.ts' --include='*.tsx' --include='*.md'` yields zero matches outside the migration file (005_drop_rewards.ts + migrations/index.ts entry incl. the `m002` legacy registration of the campaign-audit-creating migration, and the migration filename `002_campaign_audit_table.ts` itself) and the preserved-refunds files (db/claims.ts, services/claims.ts, routes/public.ts /api/claims*, components/resource-owner/claims-list.tsx). Satisfies ROADMAP SC#1, REWARDS-01, REWARDS-03, REWARDS-04, REWARDS-05, REWARDS-07."
    - "D-01: D-02: Migration 005_drop_rewards runs cleanly on the existing dev DB — drops volume_snapshots, reward_claims, campaign_audit, campaigns, reward_addresses in FK-safe order — and a second run is a no-op (idempotent — DROP TABLE IF EXISTS guards + migrations table tracks completion). Satisfies ROADMAP SC#2, REWARDS-02."
    - "`pnpm --filter @openfacilitator/server build` and `pnpm --filter @openfacilitator/dashboard build` both exit 0 after all deletions — single atomic commit per D-15 means the build is never half-broken. Satisfies REWARDS-01, REWARDS-03, REWARDS-06."
    - "Volume calculation produces identical results for facilitator owners and regular users — D-08 deletes `db/volume-aggregation.ts` entirely (the only place the 2x multiplier lived); `routes/stats.ts` computes its own volume independently and is unchanged. No FACILITATOR_MULTIPLIER, no `* 2`, no `multiplier_facilitator` reference remains in the server source tree. Satisfies ROADMAP SC#3, REWARDS-05."
    - "D-05: All admin campaign CRUD routes, handlers, and audit log entries are deleted (server: `routes/rewards.ts` whole-file delete; client: `lib/api.ts` Campaign types + 10 campaign methods deleted; dashboard pages `app/rewards/admin/page.tsx` deleted; components `components/campaigns/` directory deleted) and the surviving admin surface compiles. The `requireAdmin` middleware and `ADMIN_USER_IDS` env var are also gone (D-07). Satisfies ROADMAP SC#4, REWARDS-06."
    - "SPL transfer helpers (`services/reward-transfer.ts`) and reward-claim signature verification (`utils/solana-verify.ts`, `utils/evm-verify.ts`, `services/reward-claims.ts`) are deleted, along with the dashboard-side signing helpers (`apps/dashboard/src/lib/evm/verification.ts`, `apps/dashboard/src/lib/solana/verification.ts`); grep confirms zero remaining references in `packages/server/src/` or `apps/dashboard/src/`. Satisfies ROADMAP SC#5, REWARDS-07."
    - "Dashboard `Tab` union in `dashboard/[id]/page.tsx` no longer contains `'rewards'`; the rewards tab button, the rewards render branch, the `FacilitatorRewardsSection` import, the `RewardsInfoBanner` import + render in `app/dashboard/page.tsx`, the `EnrollmentModal` import + render in `FeaturesSpotlight/index.tsx`, and the entire `components/rewards/` and `components/campaigns/` directories are gone. Satisfies ROADMAP SC#6, REWARDS-03."
    - "Landing page `apps/dashboard/src/app/page.tsx` contains zero `$OPEN` and zero `reward` references — the two `<li>` rewards bullets (Free tier and Starter tier, identified by `<Link href=\"/rewards\">` content) are deleted outright per D-09; Free drops 5→4 bullets, Starter drops 5→4 bullets. Satisfies ROADMAP SC#7, REWARDS-04."
    - "D-12: `packages/server/.env.example` no longer contains a `# Rewards Program` section, `ADMIN_USER_IDS`, `REWARDS_WALLET_PRIVATE_KEY`, or `OPEN_TOKEN_MINT`. The server starts and runs with none of those env vars set (verified by `pnpm --filter @openfacilitator/server build` exit 0, since the only consumers — `routes/rewards.ts`, `services/reward-transfer.ts`, `utils/admin.ts`, `middleware/auth.ts requireAdmin` — are all deleted). Satisfies ROADMAP SC#8, REWARDS-08."
    - "D-11: `skills/openfacilitator/references/sdk-api.md` contains zero rewards/campaign/`$OPEN` content; the existing `## Claims Functions` section (refund claims SDK surface — `withRefundProtection`, `createRefundMiddleware`, `honoRefundMiddleware`, `reportFailure`, `getClaimable`, `executeClaim`, etc.) is preserved unchanged. Satisfies ROADMAP SC#9, REWARDS-09."
    - "`.planning/PROJECT.md ## Core Value` matches the D-10 locked text exactly (verified by `grep -F` substring match on the locked sentence); `## What This Is` no longer contains rewards framing; `Validated` requirements list has rewards entries removed. Satisfies ROADMAP SC#10, REWARDS-10."
  artifacts:
    - path: "packages/server/src/db/migrations/005_drop_rewards.ts"
      provides: "Idempotent migration dropping 5 reward tables in FK-safe order"
      contains: "DROP TABLE IF EXISTS volume_snapshots"
    - path: "packages/server/src/db/migrations/index.ts"
      provides: "Migration registration including m005"
      contains: "m005"
    - path: "packages/server/src/db/index.ts"
      provides: "DB bootstrap with no reward CREATE TABLE blocks, no reward_addresses chain_type migration block, no rewards re-exports"
    - path: "packages/server/src/server.ts"
      provides: "Express server with no rewardsRouter mount"
    - path: "packages/server/src/middleware/auth.ts"
      provides: "Auth middleware with no requireAdmin export and no isAdmin import"
    - path: "packages/server/.env.example"
      provides: "Env example with no Rewards Program section"
    - path: "apps/dashboard/src/lib/api.ts"
      provides: "API client with no rewards/campaign types or methods"
    - path: "apps/dashboard/src/app/dashboard/[id]/page.tsx"
      provides: "Facilitator detail with no rewards tab member, button, render branch, or FacilitatorRewardsSection import"
    - path: "apps/dashboard/src/components/auth/auth-provider.tsx"
      provides: "Auth context with no rewards/claim state and no rewards API calls"
    - path: "apps/dashboard/src/components/navbar.tsx"
      provides: "Navbar with no $OPEN banner, no $OPEN link, no isAdmin badge, no Trophy import"
    - path: ".planning/PROJECT.md"
      provides: "Project doc with new Core Value (facilitator + subscriptions) per D-10"
      contains: "A multi-tenant x402 payment facilitator with multi-chain subscription management"
    - path: ".planning/REQUIREMENTS.md"
      provides: "Requirements with REWARDS-02 table list corrected to the 5-table set per D-04"
    - path: "skills/openfacilitator/references/sdk-api.md"
      provides: "SDK reference with no rewards content; Claims Functions section preserved"
  deleted_artifacts:
    # Server (D-13)
    - "packages/server/src/routes/rewards.ts"
    - "packages/server/src/services/reward-transfer.ts"
    - "packages/server/src/services/reward-claims.ts"
    - "packages/server/src/db/reward-addresses.ts"
    - "packages/server/src/db/reward-claims.ts"
    - "packages/server/src/db/campaigns.ts"
    - "packages/server/src/db/campaign-audit.ts"
    - "packages/server/src/db/volume-snapshots.ts"
    - "packages/server/src/db/volume-aggregation.ts"
    - "packages/server/src/utils/admin.ts"
    - "packages/server/src/utils/solana-verify.ts"
    - "packages/server/src/utils/evm-verify.ts"
    # Dashboard (D-13)
    - "apps/dashboard/src/components/rewards/ (entire directory — 15 files)"
    - "apps/dashboard/src/components/campaigns/ (entire directory — 4 files)"
    - "apps/dashboard/src/components/facilitator-rewards-section.tsx"
    - "apps/dashboard/src/components/rewards-info-banner.tsx"
    - "apps/dashboard/src/lib/evm/verification.ts"
    - "apps/dashboard/src/lib/solana/verification.ts"
    - "apps/dashboard/src/lib/evm/ (directory empty after verification.ts delete — remove directory)"
    - "apps/dashboard/src/lib/solana/ (directory empty after verification.ts delete — remove directory)"
    - "apps/dashboard/src/app/rewards/page.tsx"
    - "apps/dashboard/src/app/rewards/admin/page.tsx"
    - "apps/dashboard/src/app/rewards/admin/ (directory empty after page.tsx delete)"
    - "apps/dashboard/src/app/rewards/ (directory empty after page.tsx + admin/ delete)"
  key_links:
    - from: "packages/server/src/db/migrations/index.ts"
      to: "packages/server/src/db/migrations/005_drop_rewards.ts"
      via: "imported as m005 and pushed into migrations array"
      pattern: "m005"
    - from: "server startup (db/index.ts → runMigrations)"
      to: "DROP TABLE IF EXISTS reward_addresses"
      via: "auto-run migrations on boot"
      pattern: "DROP TABLE IF EXISTS reward_addresses"
    - from: ".planning/PROJECT.md"
      to: "the D-10 locked Core Value text"
      via: "verbatim substring match"
      pattern: "A multi-tenant x402 payment facilitator with multi-chain subscription management"
---

<objective>
Excise the entire $OPEN rewards program in one atomic commit per D-15. Deletes ~5,000 LOC across server (`routes/rewards.ts` 1274 lines + 8 rewards DB modules + 3 utility/services + bootstrap blocks + middleware/admin scaffolding) and dashboard (15 rewards/ components + 4 campaigns/ components + 2 standalone wrappers + 2 chain-specific signing helpers + 2 page routes + heavy partial edits to navbar, user-menu, auth-provider, dashboard page, landing page, FeaturesSpotlight, lib/api.ts). Adds migration `005_drop_rewards.ts` to drop the 5 reward tables on existing DBs (FK-safe order per D-01). Rewrites `.planning/PROJECT.md` `## Core Value` to the D-10 locked text and fixes the REWARDS-02 table list in `.planning/REQUIREMENTS.md` per D-04. Strips the "Rewards Program" section from `packages/server/.env.example` per D-07/D-12.

Purpose: Rewards program abandoned (decision 2026-05-12 — neither rewards nor storefronts found product-market fit). Single atomic plan per D-15 because splitting backend / frontend / docs would leave the dashboard build broken between commits — `lib/api.ts` rewards methods call deleted routes, dashboard tab renderers import deleted components, navbar queries deleted endpoints. Atomic commit = clean revert.

Output: After this plan lands, the repo is buildable (`pnpm --filter @openfacilitator/server build` and `pnpm --filter @openfacilitator/dashboard build` both exit 0), the grep gate returns zero rewards matches outside the migration file, the legacy `002_campaign_audit_table.ts` historical migration (kept by design — D-01 doesn't reverse it; migration 005 drops the table directly), and preserved-refunds files, the migration drops the 5 reward tables on existing DBs without error and is idempotent, and `PROJECT.md ## Core Value` reads as the new facilitator-+-subscriptions pitch. No 410, no redirect, no stub — `/rewards/*` and `/rewards/admin` URLs return the default 404 from Express and Next.js respectively.

**Refund preservation (load-bearing):** This phase MUST NOT touch the v1.1 refund claims subsystem. The `claims` table (`db/index.ts` ~line 568, scoped to `resource_owners`) is REFUND data, not rewards data. The preserve list:
- `packages/server/src/db/index.ts` `CREATE TABLE IF NOT EXISTS claims` block + `idx_claims_*` indexes
- `packages/server/src/db/claims.ts`
- `packages/server/src/services/claims.ts`
- `packages/server/src/routes/public.ts /api/claims*` endpoints
- `apps/dashboard/src/components/resource-owner/claims-list.tsx`
- `skills/openfacilitator/references/sdk-api.md ## Claims Functions` section (refund SDK surface — the actual H2 heading in the file is `## Claims Functions`, not `## Claims / Refunds`; CONTEXT.md D-11 cites the heading inaccurately and we align to the file)
- All subscription, product, payment-link, facilitator, user-wallet, refund-wallet, webhook code paths

The grep gate is INTENTIONALLY case-insensitive on `reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT` — it does NOT match `claim` (the refund preservation), so refund files stay clean.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/23-rewards-removal-backend-frontend-docs/23-CONTEXT.md

<!-- Phase 22 is the direct template — same atomic-deletion pattern, smaller scale. -->
@.planning/phases/22-storefronts-removal/22-01-PLAN.md
@.planning/phases/22-storefronts-removal/22-VERIFICATION.md

<!-- Migration templates — the executor follows this Migration interface shape and FK-safe drop pattern verbatim. -->
@packages/server/src/db/migrations/index.ts
@packages/server/src/db/migrations/004_drop_storefronts.ts

<interfaces>
<!--
  Contracts extracted from the codebase so the executor does not need exploration.
  All file paths and line numbers verified against the current working tree.
-->

### Migration interface (packages/server/src/db/migrations/index.ts lines 9–12):
```typescript
export interface Migration {
  name: string;
  up: (db: Database.Database) => void;
}
```

### Migration registration pattern (packages/server/src/db/migrations/index.ts lines 14–26):
```typescript
import { migration as m001 } from './001_claims_server_id_nullable.js';
import { migration as m002 } from './002_campaign_audit_table.js';
import { migration as m003 } from './003_user_wallets_multi_chain.js';
import { migration as m004 } from './004_drop_storefronts.js';

const migrations: Migration[] = [
  m001,
  m002,
  m003,
  m004,
];
```
Add: an `import { migration as m005 } from './005_drop_rewards.js';` after the m004 import, and append `m005,` to the array. Preserve trailing-comma style.

**Note on `m002` / `002_campaign_audit_table.ts`:** These two lines (the import line in `index.ts` and the migration file itself) WILL match the grep gate pattern `campaign`. They are historical migrations that already ran on existing DBs and CANNOT be deleted (migration history must be preserved — deleting them would cause the runtime to re-attempt them on next boot, or worse, lose history of what schema state existed). The grep gate exemption regex explicitly allows these two lines.

### FK relationships among reward tables (informs D-01 drop order):
- `volume_snapshots.reward_address_id` → `reward_addresses.id` ON DELETE CASCADE
- `volume_snapshots.campaign_id` → `campaigns.id` ON DELETE CASCADE
- `reward_claims.user_id` → `"user".id` ON DELETE CASCADE
- `reward_claims.campaign_id` → `campaigns.id` ON DELETE CASCADE
- `campaign_audit.campaign_id` → `campaigns.id` ON DELETE CASCADE
- `reward_addresses.user_id` → `"user".id` ON DELETE CASCADE

D-01 order: `volume_snapshots → reward_claims → campaign_audit → campaigns → reward_addresses`. The first three are children of `campaigns`; `volume_snapshots` is also a child of `reward_addresses`. After dropping the three children, `campaigns` drops cleanly, then `reward_addresses` last. `DROP TABLE IF EXISTS` makes every step idempotent.

### packages/server/src/db/index.ts — three deletion regions:

**Region A — early "facilitator chain_type" migration block (lines 245–277):** This is a one-shot pre-migrations-table bootstrap rewrite of `reward_addresses` to add `'facilitator'` to the `chain_type` CHECK constraint. Once `005_drop_rewards.ts` drops `reward_addresses`, this block is dead code AND would error on a fresh DB (the `SELECT sql FROM sqlite_master` lookup is a no-op when the table doesn't exist, so it's safe but dead). Delete lines 245–277 inclusive (the `// Migration: Add 'facilitator' to chain_type CHECK constraint in reward_addresses table` comment through the closing `}` of the `try/catch` block, plus the blank line at 277). Anchor start: the blank line at 245 (preserve it as a single blank between the prior `} catch (e) { ... }` block at 242–244 and the surviving next comment `// Create tables` at 279). Anchor end: line 277 (blank line). After deletion, line 244's closing `}` is followed by exactly one blank line, then the `// Create tables` comment on what is now line 246.

**Region B — bootstrap CREATE TABLE blocks for the 5 reward tables (lines 595–677 inclusive):** Per D-02, strip:
- Lines 595–596: `-- Reward addresses table (...)` comment block
- Lines 597–606: `CREATE TABLE IF NOT EXISTS reward_addresses (...);`
- Line 607: blank
- Lines 608–610: three `CREATE INDEX IF NOT EXISTS idx_reward_addresses_*` statements
- Line 611: blank
- Line 612: `-- Campaigns table (reward campaign configuration)` comment
- Lines 613–625: `CREATE TABLE IF NOT EXISTS campaigns (...);`
- Line 626: blank
- Lines 627–628: two `CREATE INDEX IF NOT EXISTS idx_campaigns_*` statements
- Line 629: blank
- Lines 630: `-- Campaign audit table (tracks all admin changes to campaigns)` comment
- Lines 631–638: `CREATE TABLE IF NOT EXISTS campaign_audit (...);`
- Line 639: blank
- Line 640: `CREATE INDEX IF NOT EXISTS idx_campaign_audit_campaign ON campaign_audit(campaign_id);`
- Line 641: blank
- Line 642: `-- Reward claims table (user claims against campaigns)` comment
- Lines 643–657: `CREATE TABLE IF NOT EXISTS reward_claims (...);`
- Line 658: blank
- Lines 659–661: three `CREATE INDEX IF NOT EXISTS idx_reward_claims_*` statements
- Line 662: blank
- Line 663: `-- Volume snapshots table (daily aggregated volume per address)` comment
- Lines 664–673: `CREATE TABLE IF NOT EXISTS volume_snapshots (...);`
- Line 674: blank
- Lines 675–677: three `CREATE INDEX IF NOT EXISTS idx_volume_snapshots_*` statements

Surviving line above (594): `CREATE INDEX IF NOT EXISTS idx_claims_status ON claims(status);` — this is the REFUND claims index, preserve. Surviving line below (679 in the pre-edit numbering): `-- User preferences table (chain preference for payments)` — preserve. Place exactly one blank line between the surviving `idx_claims_status` index and the `-- User preferences table` comment after deletion.

**Region C — rewards db re-exports (lines 785–790 inclusive):**
```typescript
export * from './reward-addresses.js';
export * from './campaigns.js';
export * from './campaign-audit.js';
export * from './reward-claims.js';
export * from './volume-snapshots.js';
export * from './volume-aggregation.js';
```
Delete all six lines. Surviving line above (784): `export * from './claims.js';` (REFUND — preserve). Surviving line below (791 in pre-edit numbering): `export * from './user-preferences.js';` (preserve). No blank-line normalization needed.

### packages/server/src/db/types.ts — reward type interfaces (lines 253–330 inclusive):

Delete:
- Lines 253–266: `/** Reward address database record ... */` block + `export interface RewardAddressRecord { ... }`
- Line 267: blank
- Lines 268–284: `/** Campaign database record ... */` block + `export interface CampaignRecord { ... }`
- Line 285: blank
- Lines 286–297: `/** Campaign audit database record ... */` block + `export interface CampaignAuditRecord { ... }`
- Line 298: blank
- Lines 299–316: `/** Reward claim database record ... */` block + `export interface RewardClaimRecord { ... }`
- Line 317: blank
- Lines 318–330: `/** Volume snapshot database record ... */` block + `export interface VolumeSnapshotRecord { ... }`

Surviving line above (252): closing `}` of the prior interface. Surviving line below (331 in pre-edit numbering): blank line then end-of-file (last non-blank content is the `}` at 330). After deletion, line 252's `}` is followed by EOF (or one trailing blank, whichever the previous file had — preserve the EOF newline convention).

### packages/server/src/db/facilitators.ts — three deletions:

1. **Line 4:** `import { createFacilitatorMarker } from './reward-addresses.js';` — delete the entire line.
2. **Lines 262–311 inclusive** (the `ensureFacilitatorMarker` function and its JSDoc):
   - Anchor start: JSDoc `/**\n * Ensure a facilitator owner has an enrollment marker for volume tracking ...` at line 262.
   - Anchor end: closing `}` of `ensureFacilitatorMarker` at line 311.
   - Surviving line above (261): blank line (preserve one). Surviving line below (312): blank line then JSDoc for `backfillFacilitatorMarkers`.
3. **Lines 313–338 inclusive** (`backfillFacilitatorMarkers` function and its JSDoc):
   - Anchor start: JSDoc `/**\n * Backfill missing facilitator enrollment markers ...` at line 313.
   - Anchor end: closing `}` of `backfillFacilitatorMarkers` at line 338 (read the file to confirm — the function loops over facilitator owners and calls `ensureFacilitatorMarker`; it ends at the closing brace of the `for ... of` body's enclosing function).
   - After this deletion the file ends cleanly with no dangling code.

Read `packages/server/src/db/facilitators.ts` lines 1–10 and 260–340 before editing to confirm exact line numbers. If `backfillFacilitatorMarkers` doesn't end at 338, anchor on its closing `}` whichever line that is.

### packages/server/src/routes/admin.ts — two deletions:

1. **Line 9 of the import block:** `  ensureFacilitatorMarker,` — delete this single named import. The surrounding import `{ ... } from '../db/facilitators.js';` block must remain intact with its other names (`createFacilitator`, `getFacilitatorById`, `getFacilitatorsByOwner`, `updateFacilitator`, `deleteFacilitator`).
2. **Lines 549–550 (a 2-line call site):**
   ```
       // Ensure facilitator owner has enrollment marker for volume tracking
       ensureFacilitatorMarker(ownerAddress);
   ```
   Delete both lines. Surviving line above (548): blank line after `return;`'s containing block. Surviving line below (551): blank line followed by `// Register custom domain with Railway`. Place exactly one blank line between them.

Read `packages/server/src/routes/admin.ts` lines 1–25 and 540–560 before editing to confirm.

### packages/server/src/routes/internal-webhooks.ts — two deletions:

1. **Line 20:** `import { createFacilitator, ensureFacilitatorMarker } from '../db/facilitators.js';` — change to: `import { createFacilitator } from '../db/facilitators.js';` (remove `, ensureFacilitatorMarker`).
2. **Lines 86–87 (a 2-line call site):**
   ```
       // Ensure facilitator owner has enrollment marker for volume tracking
       ensureFacilitatorMarker(pending.user_id);
   ```
   Delete both lines. Surviving line above (85): blank. Surviving line below (88): blank line then `console.log('[Subscription Webhook] Created facilitator ...');`. Place exactly one blank line between them.

Read `packages/server/src/routes/internal-webhooks.ts` lines 15–25 and 80–95 before editing to confirm.

### packages/server/src/server.ts — two deletions:

1. **Line 14:** `import { rewardsRouter } from './routes/rewards.js';` — delete the entire line.
2. **Lines 82–83 (the rewards mount):**
   ```
     // Rewards API routes (for rewards program)
     app.use('/api/rewards', rewardsRouter);
   ```
   Delete both lines. Surviving line above (81): blank line after `app.use('/api/admin', adminRouter);`. Surviving line below (84): blank line then `// Subscription routes (for Memeputer agent integration)`. Place exactly one blank line between them.

### packages/server/src/middleware/auth.ts — two deletions:

1. **Line 3:** `import { isAdmin } from '../utils/admin.js';` — delete the entire line.
2. **Lines 84–126 inclusive** (the `requireAdmin` middleware function and its JSDoc):
   - Anchor start: JSDoc `/**\n * Middleware to require admin access` at line 84.
   - Anchor end: closing `}` of `requireAdmin` at line 126.
   - Surviving line above (83): blank line (preserve one). Surviving line below (127): blank line at file end. After deletion the file ends after `optionalAuth`'s closing `}` at line 82 (or whatever line it ends at after the requireAdmin block is removed), followed by exactly one trailing newline.

### packages/server/.env.example — strip Rewards Program section (lines 93–99 inclusive):

Anchor start: line 93 `# -----------------------------------------------------------------------------`. Anchor end: line 99 `ADMIN_USER_IDS=`. The 7 lines to remove:
```
# -----------------------------------------------------------------------------
# Rewards Program
# -----------------------------------------------------------------------------

# Comma-separated list of user IDs that have admin access to rewards management
# These users can manage campaigns, view all claims, etc.
ADMIN_USER_IDS=
```
Surviving line above (92): blank line after `SUBSCRIPTION_WEBHOOK_SECRET=` at 91. Surviving line below (100): blank line then `# -----------------------------------------------------------------------------` opening the `Trusted Origins (Optional ...)` section. Place exactly one blank line between them.

Read `packages/server/.env.example` lines 85–105 before editing to confirm exact line numbers.

### apps/dashboard/src/lib/api.ts — two deletion regions:

**Region A — Types block (lines 3–110 inclusive):** Anchor start: line 3 `// Rewards Program types`. Anchor end: line 110 (closing `}` of `CreateCampaignRequest` plus its trailing blank). Surviving line above (2): blank (line 1 is `const API_BASE = ...`). Surviving line below (111 in pre-edit numbering): `// Payment Link types` or similar — read lines 105–115 first to confirm. Place exactly one blank line between the surviving `const API_BASE` and the next types comment.

The exact set of interfaces to delete in this region: `RewardsStatus`, `RewardsEnrollResponse`, `Campaign`, `CampaignAudit`, `CampaignWithAudit`, `CampaignStats`, `VolumeBreakdown`, `CampaignHistoryItem`, `RewardClaim`, `CreateCampaignRequest`.

**Region B — Methods block (lines 1304–1458 inclusive):** Anchor start: line 1304 `  // Rewards Program`. Anchor end: line 1458 (closing `}` of `getClaimHistory`'s method body — last method in the block). Surviving line above (1303): closing `}` (`});`) of the previous method. Surviving line below (1459): blank line then `}` closing the `ApiClient` class.

The exact set of methods to delete in this region: `getRewardsStatus`, `enrollInRewards`, `deleteRewardAddress`, `getCampaigns`, `getCampaign`, `createCampaign`, `updateCampaign`, `publishCampaign`, `endCampaign`, `deleteCampaign`, `getActiveCampaign`, `getCampaignHistory`, `getCampaignStats`, `getRewardsVolume`, `getVolumeBreakdown`, `getFacilitatorVolume`, `initiateClaim`, `getMyClaim`, `getClaimEligibility`, `getClaimHistory` — 20 methods total.

Read `apps/dashboard/src/lib/api.ts` lines 1–15, 100–115, 1295–1310, 1455–1462 before editing to confirm exact boundaries.

### apps/dashboard/src/lib/evm/verification.ts AND apps/dashboard/src/lib/solana/verification.ts — chain-specific signing helpers (whole-file deletes):

Both files exist solely to support `apps/dashboard/src/components/rewards/enrollment-modal.tsx` (which is itself deleted as part of the `components/rewards/` directory removal). Verified consumers:
- `apps/dashboard/src/lib/evm/verification.ts` line 10 contains the literal `"OpenFacilitator Rewards"`; line 32 calls `api.enrollInRewards({ chain_type: 'evm', ... })` (a method removed by Task 3c).
- `apps/dashboard/src/lib/solana/verification.ts` line 12 contains the literal `"OpenFacilitator Rewards"`; line 45 calls `api.enrollInRewards({ chain_type: 'solana', ... })` (same — removed by Task 3c).
- `apps/dashboard/src/components/rewards/enrollment-modal.tsx` imports `signAndEnroll` from `@/lib/solana/verification` (line 8); no other file in `apps/dashboard/src/` references either helper (confirmed by grep for `lib/evm/verification|lib/solana/verification|signAndEnrollEVM|signAndEnroll`).

After deleting both files and the `components/rewards/` directory (Task 3b), the dashboard build would fail at the `api.enrollInRewards` call sites if these helpers remained — they MUST be deleted in the same atomic commit. After deletion, both parent directories `apps/dashboard/src/lib/evm/` and `apps/dashboard/src/lib/solana/` become empty (each contained only `verification.ts`) — remove those empty directories too.

### apps/dashboard/src/app/dashboard/[id]/page.tsx — four deletions:

1. **Line 46:** `import { FacilitatorRewardsSection } from '@/components/facilitator-rewards-section';` — delete entire line.
2. **Line 58:** Change `type Tab = 'transactions' | 'products' | 'webhooks' | 'refunds' | 'rewards' | 'settings';` to `type Tab = 'transactions' | 'products' | 'webhooks' | 'refunds' | 'settings';` — remove only ` | 'rewards'`.
3. **Lines 466–476 inclusive** (the rewards tab button):
   ```
               <button
                 onClick={() => setActiveTab('rewards')}
                 className={cn(
                   'pb-3 text-sm font-medium border-b-2 -mb-px transition-colors',
                   activeTab === 'rewards'
                     ? 'border-primary text-foreground'
                     : 'border-transparent text-muted-foreground hover:text-foreground'
                 )}
               >
                 Rewards
               </button>
   ```
   Delete all 11 lines. Adjacent siblings — Refunds button (lines 455–465) and Settings button (lines 477–487) — remain intact.
4. **Lines 498–499 inclusive** (the rewards render branch):
   ```
           ) : activeTab === 'rewards' ? (
             <FacilitatorRewardsSection facilitatorId={id} />
   ```
   Delete both lines. After deletion, the chain reads:
   ```
           ) : activeTab === 'refunds' ? (
             <RefundsSection facilitatorId={id} facilitator={facilitator} />
           ) : activeTab === 'transactions' ? (
             ...
   ```
   Validate JSX balance — `) : activeTab === 'transactions' ? (` at what is currently line 500 must now immediately follow the `<RefundsSection />` closing.

### apps/dashboard/src/app/dashboard/page.tsx — three deletions:

1. **Line 22:** `import { RewardsInfoBanner } from '@/components/rewards-info-banner';` — delete entire line.
2. **Line 70:** `  const { isLoading: authLoading, isAuthenticated, isEnrolled } = useAuth();` — change to: `  const { isLoading: authLoading, isAuthenticated } = useAuth();` — remove `, isEnrolled` (this property no longer exists on the auth context after the auth-provider surgery below).
3. **Lines 148–149 inclusive** (the rewards banner render):
   ```
           {/* Rewards Info Banner */}
           <RewardsInfoBanner />
   ```
   Delete both lines. Surviving line above (147): blank. Surviving line below (150): blank then `{/* Loading State */}`. Place exactly one blank line between them.
4. **Line 185** (in the `<FeaturesSpotlight>` props block): `          isEnrolled={isEnrolled}` — delete this single line. The FeaturesSpotlight component's `isEnrolled` prop is also being removed (see featureCards.ts / FeaturesSpotlight/index.tsx edits below).

### apps/dashboard/src/app/page.tsx — two `<li>` bullet deletions per D-09 (JSX-anchored, NOT line-number-anchored):

The Free-tier and Starter-tier pricing card both contain a `<li>` bullet whose body is `<Link href="/rewards" ...>Earn $OPEN rewards...</Link>`. Both bullets MUST be removed; the surrounding `<ul>` keeps its other bullets. Both `<li>` elements have the same outer shape:

```jsx
<li className="flex items-center gap-2">
  <Check className="w-4 h-4 text-primary" />
  <Link href="/rewards" className="hover:text-primary transition-colors">
    Earn $OPEN rewards{Starter tier adds: " (2x bonus)"}
  </Link>
</li>
```

Free-tier bullet text: `Earn $OPEN rewards`. Starter-tier bullet text: `Earn $OPEN rewards (2x bonus)`. Per D-09, delete the entire `<li>...</li>` element for each — no substitute, Free tier drops from 5 bullets to 4, Starter tier drops from 5 to 4.

See Task 3f for the JSX-anchored deletion procedure.

### apps/dashboard/src/components/navbar.tsx — extensive surgery:

This file has the most rewards entanglement on the dashboard side. Deletions (read the whole file first):

1. **Import line 6:** Remove `Trophy` from the lucide-react import — change `import { Menu, X, Trophy, Github, FileText } from 'lucide-react';` to `import { Menu, X, Github, FileText } from 'lucide-react';`.
2. **Imports lines 7, 11:** Remove `useQuery` from `@tanstack/react-query` (if no other code in this file uses it after the deletions below — verify by grep) and `api` from `@/lib/api`. Verify with grep before deleting these imports; if other surviving code in navbar.tsx uses them, leave them.
3. **Helper function `formatTokenAmount` (lines 13–22):** Delete this entire function — it's only used by the rewards banner that's being deleted.
4. **Inside `Navbar()` function:**
   - Line 26: change `const { isAuthenticated, isLoading, isAdmin } = useAuth();` to `const { isAuthenticated, isLoading } = useAuth();` — remove `, isAdmin` (the `isAdmin` property is being removed from auth context per the auth-provider surgery below).
   - Line 28: `const [bannerDismissed, setBannerDismissed] = useState(true);` — delete this state.
   - Lines 30–33: the `useQuery({ queryKey: ['activeCampaign'], ... })` block — delete entirely.
   - Lines 35–39: the `daysRemaining` computation — delete entirely.
   - Lines 41–46: the `useEffect` reading `localStorage.getItem('rewards-banner-dismissed')` — delete entirely.
   - Lines 48–51: the `dismissBanner` callback — delete entirely.
   - Line 54: `const showBanner = !bannerDismissed && activeCampaign?.campaign;` — delete entirely.
5. **JSX — rewards banner (lines 60–86 inclusive):** Delete the entire `{showBanner && (<div className="fixed top-0 ..."> ... </div>)}` block — this is the blue "Building with x402? We're paying you in $OPEN" banner.
6. **JSX — nav top class (line 88):** Currently `<nav className={`fixed w-full z-50 border-b border-border/50 backdrop-blur-xl bg-background/80 ${showBanner ? 'top-10' : 'top-0'}`}>`. Change to: `<nav className="fixed w-full z-50 border-b border-border/50 backdrop-blur-xl bg-background/80 top-0">` — remove the template literal, hardcode `top-0`.
7. **JSX — desktop `$OPEN` CoinGecko link (lines 98–106 inclusive):** Delete the entire `<a href="https://www.coingecko.com/en/coins/openfacilitator" ...>$OPEN</a>` element.
8. **JSX — desktop admin badge (lines 134–138 inclusive):** Delete the entire `{isAdmin && (<span ...>Admin</span>)}` block.
9. **JSX — mobile `$OPEN` CoinGecko link (lines 166–175 inclusive):** Delete the entire `<a href="https://www.coingecko.com/en/coins/openfacilitator" ...>$OPEN</a>` block AND the `<div className="border-t border-border/50 my-2" />` separator on line 175 that visually grouped it. Verify by reading lines 162–180 first — adjust to whatever the actual mobile `$OPEN` block + its separator looks like.
10. **JSX — mobile admin badge (lines 208–212 inclusive):** Delete the entire `{isAdmin && (<span ...>Admin</span>)}` block.

After deletions, verify the navbar still compiles: nav renders, mobile menu opens/closes, dashboard/refunds/subscriptions links work, NotificationBell + UserMenu still render, sign-in flow intact.

### apps/dashboard/src/components/user-menu.tsx — three deletions:

1. **Line 5 import:** Remove `Trophy` from the lucide-react import — change `import { User, LogOut, LayoutDashboard, CreditCard, Trophy, ChevronDown, Sun, Moon, Monitor } from 'lucide-react';` to `import { User, LogOut, LayoutDashboard, CreditCard, ChevronDown, Sun, Moon, Monitor } from 'lucide-react';`.
2. **Line 23:** `  const { user, signOut, hasClaimable } = useAuth();` — change to: `  const { user, signOut } = useAuth();` — remove `, hasClaimable`.
3. **Lines 54–62 inclusive** (the Rewards menu item):
   ```
           <DropdownMenuItem asChild>
             <Link href="/rewards" className="flex items-center cursor-pointer relative">
               <Trophy className="w-4 h-4 mr-2" />
               Rewards
               {hasClaimable && (
                 <span className="absolute right-2 w-2 h-2 bg-green-500 rounded-full animate-pulse" />
               )}
             </Link>
           </DropdownMenuItem>
   ```
   Delete this entire `<DropdownMenuItem>` block. The surrounding items (Subscriptions above, DropdownMenuSeparator below) remain. Verify with a read of lines 47–67.

### apps/dashboard/src/components/archive/wallet-dropdown.tsx — three deletions:

This file is "archive" — it's not on the active render path but still gets typechecked. Same shape of edits:

1. **Line 28:** `const { user, signOut, hasClaimable } = useAuth();` — remove `, hasClaimable`. If there's also a `Trophy` import, remove it.
2. **Lines 107–121 inclusive** (the Rewards Link block, including the `{/* Rewards Link */}` comment + border-t separator + the `<Link href="/rewards">Rewards ... hasClaimable ...</Link>` block). Read lines 105–125 to confirm exact boundaries.

### apps/dashboard/src/components/auth/auth-provider.tsx — heavy surgery, ~75% of file:

Goal: Reduce this provider to plain Better Auth session passthrough with `user`, `isLoading`, `isAuthenticated`, `signOut` only. Remove all rewards/admin/claim state.

Action: Replace the entire file contents with:
```typescript
'use client';

import { createContext, useContext, useState, type ReactNode } from 'react';
import { useSession, signOut as authSignOut } from '@/lib/auth-client';

interface User {
  id: string;
  email: string;
  name?: string;
  emailVerified: boolean;
  createdAt: Date;
}

interface AuthContextType {
  user: User | null;
  isLoading: boolean;
  isAuthenticated: boolean;
  signOut: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  isLoading: true,
  isAuthenticated: false,
  signOut: async () => {},
});

export function AuthProvider({ children }: { children: ReactNode }) {
  const { data: session, isPending } = useSession();
  const [isSigningOut, setIsSigningOut] = useState(false);

  const handleSignOut = async () => {
    try {
      setIsSigningOut(true);
      await authSignOut();
      window.location.href = '/';
    } finally {
      setIsSigningOut(false);
    }
  };

  const isAuthenticated = !!session?.user;

  return (
    <AuthContext.Provider
      value={{
        user: session?.user || null,
        isLoading: isPending || isSigningOut,
        isAuthenticated,
        signOut: handleSignOut,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
```

This removes: `RewardsStatus` import, `rewardsStatus` state, `rewardsChecked` state, `hasClaimable` state, `fetchRewardsStatus` callback, `useEffect` calling it, the `isAdmin / isEnrolled / isFacilitatorOwner / hasClaimable / refetchRewardsStatus` context properties, the `api.getRewardsStatus()` / `api.getActiveCampaign()` / `api.getClaimEligibility()` calls.

**Consumer migration (already handled in other edits above):** Every existing `useAuth()` call site that destructured a removed property is being updated in this plan — navbar.tsx (`isAdmin`), user-menu.tsx (`hasClaimable`), wallet-dropdown.tsx (`hasClaimable`), dashboard/page.tsx (`isEnrolled`). Any remaining consumer destructuring those properties is inside a deleted file (`rewards/*`, `rewards-info-banner.tsx`, `app/rewards/page.tsx`) and goes away with the file.

### apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts — two deletions:

1. **Interface `UserState` (lines 14–18):** Remove the `isEnrolled: boolean;` field. Resulting `UserState`:
   ```typescript
   interface UserState {
     hasFacilitators: boolean;
     firstFacilitatorId?: string;
   }
   ```
2. **Body of `getFeatureCards` (lines 20–79):** Replace the entire function body to remove the rewards card and the four enrollment-branched returns. New body:
   ```typescript
   export function getFeatureCards(userState: UserState): FeatureCardConfig[] {
     const { hasFacilitators, firstFacilitatorId } = userState;

     const refundsCard: FeatureCardConfig = {
       id: 'refunds',
       icon: 'shield',
       headline: 'Refund Protection',
       description: 'Automatically refund customers when your API fails. Build trust and reduce disputes.',
       ctaText: hasFacilitators ? 'Configure →' : 'Get Started →',
       ctaHref: hasFacilitators && firstFacilitatorId
         ? `/dashboard/${firstFacilitatorId}?tab=refunds`
         : '/refunds/setup?facilitator=pay.openfacilitator.io',
     };

     const facilitatorCard: FeatureCardConfig = {
       id: 'facilitator',
       icon: 'globe',
       headline: hasFacilitators ? 'Add Another Facilitator' : 'Run Your Own Facilitator',
       description: 'Accept payments on your own domain with full control. $5/month per facilitator.',
       ctaText: hasFacilitators ? 'Create New →' : 'Create Facilitator →',
       onClick: 'createFacilitator',
     };

     return hasFacilitators
       ? [facilitatorCard, refundsCard]
       : [refundsCard, facilitatorCard];
   }
   ```
3. **`FeatureCardConfig` interface (lines 3–12):** Remove `'gift'` from the `icon` union and `'enrollRewards'` from the `onClick` union. Resulting interface:
   ```typescript
   export interface FeatureCardConfig {
     id: string;
     icon: 'shield' | 'globe';
     headline: string;
     description: string;
     ctaText: string;
     ctaHref?: string;
     onClick?: 'createFacilitator';
     badge?: string;
   }
   ```

### apps/dashboard/src/components/dashboard/FeaturesSpotlight/index.tsx — three deletions:

1. **Imports lines 4–7:** Remove `useState` from React (no longer needed), remove `dynamic` from `next/dynamic`, remove `Gift` from lucide-react. After edits:
   ```typescript
   import { Shield, Globe } from 'lucide-react';
   import { FeatureCard } from './FeatureCard';
   import { getFeatureCards, type FeatureCardConfig } from './featureCards';
   ```
2. **Lines 9–13** (the EnrollmentModal dynamic import): Delete entirely.
3. **Interface `FeaturesSpotlightProps` (lines 15–20):** Remove `isEnrolled: boolean;`. Resulting:
   ```typescript
   interface FeaturesSpotlightProps {
     hasFacilitators: boolean;
     firstFacilitatorId?: string;
     onCreateFacilitator: () => void;
   }
   ```
4. **Function body (lines 22–75):** Replace with the simplified version that no longer manages enrollment modal state:
   ```typescript
   const iconMap = {
     shield: <Shield className="w-5 h-5" />,
     globe: <Globe className="w-5 h-5" />,
   };

   export function FeaturesSpotlight({
     hasFacilitators,
     firstFacilitatorId,
     onCreateFacilitator,
   }: FeaturesSpotlightProps) {
     const cards = getFeatureCards({
       hasFacilitators,
       firstFacilitatorId,
     });

     const handleCardAction = (card: FeatureCardConfig) => {
       if (card.onClick === 'createFacilitator') {
         onCreateFacilitator();
       }
     };

     return (
       <div className="my-8">
         <div className="flex flex-col md:flex-row gap-4">
           {cards.map((card) => (
             <FeatureCard
               key={card.id}
               icon={iconMap[card.icon]}
               headline={card.headline}
               description={card.description}
               ctaText={card.ctaText}
               ctaHref={card.ctaHref}
               onClick={card.onClick ? () => handleCardAction(card) : undefined}
               badge={card.badge}
             />
           ))}
         </div>
       </div>
     );
   }
   ```

### .planning/PROJECT.md — Core Value + What This Is + Validated requirements rewrite per D-10:

The exact replacement texts (literal — DO NOT paraphrase):

**`## What This Is` (lines 3–5 in current file):** Replace with:
```
## What This Is

A multi-tenant x402 payment facilitator with multi-chain subscription management. Users can create facilitators on custom domains and accept payments across Base and Solana with built-in subscription billing.
```

**`## Core Value` (lines 7–9 in current file) — D-10 LOCKED EXACT TEXT:** Replace with:
```
## Core Value

A multi-tenant x402 payment facilitator with multi-chain subscription management. Run a free shared facilitator at pay.openfacilitator.io, or stand up your own at a custom domain. Built-in subscription billing across Base and Solana, with chain preference, grace periods, and payment notifications.
```

**`### Validated` block (lines 49–80 in current file):** Remove rewards-related bullets:
- Delete: `- ✓ Free users can register for rewards tracking — v1.0`
- Delete: `- ✓ Users can add/verify Solana and EVM pay-to addresses — v1.0`
- Delete: `- ✓ Dashboard shows volume, threshold progress, estimated rewards — v1.0`
- Delete: `- ✓ Facilitator owners get 2x multiplier automatically — v1.0`
- Delete: `- ✓ Admin can create and manage reward campaigns — v1.0`
- Delete: `- ✓ Volume calculated from transaction logs for verified addresses — v1.0`
- Delete: `- ✓ Users can claim $OPEN tokens when threshold met — v1.0`
- Delete: `- ✓ SPL token transfer from rewards wallet on claim — v1.0`
- Delete: `- ✓ Claim history with transaction signatures — v1.0`

Preserve all non-rewards bullets in the `### Validated` block. The remaining bullets cover authentication, facilitator system, transaction logging, free facilitator, dashboard, Solana wallet generation, SDK x402 work, refund docs/whitelabel, subscription wallet overhaul.

**`### Future` block (lines 83–88):** Delete the bullet `- Email notifications when threshold reached or claim available` — change to `- Email notifications` (the "threshold/claim" qualifier was rewards-specific). Delete the bullet `- Sybil cluster detection dashboard for admins` (rewards-dependent — already noted as likely-dropped in REQUIREMENTS.md).

**`## Context` block (lines 100–110 in current file):** Delete the entire "Production readiness" subsection that references `REWARDS_WALLET_PRIVATE_KEY`, `OPEN_TOKEN_MINT`, `CRON_SECRET for volume snapshot`, and "First campaign needs creation via /rewards/admin". Also delete the entire "Known limitations" subsection (the "Single active campaign / 5 address limit / 30-day claim window" entries are all rewards-specific). After these deletions the `## Context` section is empty — replace with:
```
## Context

**Production readiness:**
- CRON_SECRET for subscription billing cron
- Stripe-style billing wallet flow for facilitator subscriptions
```

**`## Constraints` block (lines 113–117):** Delete the line `- **Token**: $OPEN on Solana — SPL token transfers via @solana/spl-token` and `- **Timeline**: v1.0-v1.2 shipped, claims available March 2026`. Preserve the Database / Auth / UI bullets.

**`## Key Decisions` table (lines 121–146):** Remove rows whose `Decision` column is rewards-specific:
- "Solana signature verification (Ed25519)" (rewards-specific — it was the reward enrollment verification)
- "EVM signature verification (EIP-191)" (rewards-specific — same flow)
- "Proportional distribution"
- "Soft anti-gaming (track, don't block)"
- "Snapshot + live delta for volume"
- "Ephemeral wallet connection for claims"
- "Combined initiate + execute claim"
- "5 address limit per user"
- "Single active campaign"
- "Facilitator markers in reward_addresses"

Preserve all non-rewards decisions: "Integrate into existing dashboard", "SQLite for all tables", the x402 v2 decisions, "Middleware-first refund docs", "Show wallet addresses directly", "7-day grace period", "Pre-fund any amount", "No mid-cycle refunds", "Prominent chain preference toggle", "node-cron for billing", "Notification duplicate prevention".

**`## Current Milestone` block (lines 18–37):** Preserve as-is (the "target work" mentions rewards removal which is what we're doing right now — leaving that intact is honest history). Optionally tighten the language; if uncertain, leave alone.

### .planning/REQUIREMENTS.md — fix REWARDS-02 table list per D-04:

Find the line:
```
- [ ] **REWARDS-02**: Rewards DB tables dropped via migration (`reward_addresses`, `campaigns`, `claims`, `volume_snapshots`)
```
Change to:
```
- [ ] **REWARDS-02**: Rewards DB tables dropped via migration (`reward_addresses`, `reward_claims`, `campaigns`, `campaign_audit`, `volume_snapshots`)
```
(Replace `claims` with `reward_claims`; add `campaign_audit` as the 4th entry — the canonical 5-table set from D-01.)

No other changes to REQUIREMENTS.md.

### skills/openfacilitator/references/sdk-api.md — remove rewards content per D-11:

**Heading reality check:** The actual H2 headings in this file (as of the working tree) are:
- `## All Exports`
- `## OpenFacilitator Class`
- `## Middleware Functions`
- `## Claims Functions` ← line 50 — refund claims SDK (`reportFailure`, `getClaimable`, `getClaimHistory`, `executeClaim`, etc.). PRESERVE UNCHANGED.
- `## Network Utilities`
- `## Error Classes`
- `## Type Guards`

The string `Claims / Refunds` appears only in a code-block JS comment on line 64 (`// Claims / Refunds`) — that's a comment inside an `export { ... }` block, NOT a heading. CONTEXT.md D-11 refers to the heading as `## Claims / Refunds` which is an outdated read; align the plan with the actual file: `## Claims Functions` is the canonical preserved heading.

Read the file first. Per D-11, the existing `## Claims Functions` section is REFUND claims SDK — KEEP unchanged. Delete only sections / examples that are about $OPEN rewards, campaigns, reward enrollment, or reward-claim signature verification. The CONTEXT.md notes this is "likely a near-no-op; flag in plan if there's truly nothing to remove." If grep finds no rewards/campaign/`$OPEN` content in this file, record that fact in the summary; the file's grep gate participation is satisfied trivially.
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add migration 005_drop_rewards and register in migrations index</name>
  <files>packages/server/src/db/migrations/005_drop_rewards.ts, packages/server/src/db/migrations/index.ts</files>
  <read_first>
    - packages/server/src/db/migrations/004_drop_storefronts.ts (direct template — same drop-migration shape with IF EXISTS and FK-safe order)
    - packages/server/src/db/migrations/002_campaign_audit_table.ts (alt template — the CREATE TABLE migration for `campaign_audit` that 005 will reverse; confirms `campaign_audit` was added post-bootstrap)
    - packages/server/src/db/migrations/index.ts (registration point — lines 14–26)
  </read_first>
  <action>
    Create `packages/server/src/db/migrations/005_drop_rewards.ts` with this exact content. The 5 DROP statements MUST be in this order per D-01 (volume_snapshots → reward_claims → campaign_audit → campaigns → reward_addresses) — children before parents.

    ```typescript
    /**
     * Migration: Drop $OPEN rewards program tables
     *
     * Rewards program abandoned in v1.3 (Phase 23). Drops the five reward tables
     * in FK-safe order:
     *   volume_snapshots  → child of reward_addresses + campaigns
     *   reward_claims     → child of campaigns
     *   campaign_audit    → child of campaigns
     *   campaigns         → parent
     *   reward_addresses  → parent
     *
     * IF EXISTS on every statement makes this idempotent for fresh DBs (where
     * the bootstrap no longer creates the tables) and for existing DBs.
     */
    import type Database from 'better-sqlite3';
    import type { Migration } from './index.js';

    export const migration: Migration = {
      name: '005_drop_rewards',

      up(db: Database.Database): void {
        db.exec(`
          DROP TABLE IF EXISTS volume_snapshots;
          DROP TABLE IF EXISTS reward_claims;
          DROP TABLE IF EXISTS campaign_audit;
          DROP TABLE IF EXISTS campaigns;
          DROP TABLE IF EXISTS reward_addresses;
        `);
        console.log('  reward tables dropped (if present): volume_snapshots, reward_claims, campaign_audit, campaigns, reward_addresses');
      },
    };
    ```

    Then edit `packages/server/src/db/migrations/index.ts`:
    - Add `import { migration as m005 } from './005_drop_rewards.js';` immediately after the existing `m004` import on line 18.
    - Append `m005,` to the `migrations: Migration[]` array (currently `[m001, m002, m003, m004,]` on lines 21–26) so it reads `[m001, m002, m003, m004, m005,]`. Preserve the trailing-comma style.

    Do NOT modify any other file in this task. Project-wide tsc will fail until Tasks 2 and 3 land (server still contains rewards source that imports deleted files); atomic build verification happens in Task 5.
  </action>
  <verify>
    <automated>test -f packages/server/src/db/migrations/005_drop_rewards.ts && grep -q "DROP TABLE IF EXISTS volume_snapshots" packages/server/src/db/migrations/005_drop_rewards.ts && grep -q "DROP TABLE IF EXISTS reward_claims" packages/server/src/db/migrations/005_drop_rewards.ts && grep -q "DROP TABLE IF EXISTS campaign_audit" packages/server/src/db/migrations/005_drop_rewards.ts && grep -q "DROP TABLE IF EXISTS campaigns" packages/server/src/db/migrations/005_drop_rewards.ts && grep -q "DROP TABLE IF EXISTS reward_addresses" packages/server/src/db/migrations/005_drop_rewards.ts && [ "$(grep -nE 'DROP TABLE IF EXISTS (volume_snapshots|reward_claims|campaign_audit|campaigns|reward_addresses)' packages/server/src/db/migrations/005_drop_rewards.ts | head -1 | grep -c volume_snapshots)" = "1" ] && [ "$(grep -nE 'DROP TABLE IF EXISTS (volume_snapshots|reward_claims|campaign_audit|campaigns|reward_addresses)' packages/server/src/db/migrations/005_drop_rewards.ts | tail -1 | grep -c reward_addresses)" = "1" ] && grep -q "m005" packages/server/src/db/migrations/index.ts && grep -q "from './005_drop_rewards" packages/server/src/db/migrations/index.ts</automated>
  </verify>
  <acceptance_criteria>
    - `packages/server/src/db/migrations/005_drop_rewards.ts` exists.
    - File contains all five `DROP TABLE IF EXISTS` strings: `volume_snapshots`, `reward_claims`, `campaign_audit`, `campaigns`, `reward_addresses`.
    - Drop ORDER is correct: `grep -nE 'DROP TABLE IF EXISTS ...' 005_drop_rewards.ts | head -1` contains `volume_snapshots` (first); `tail -1` contains `reward_addresses` (last). This enforces D-01 FK-safe ordering.
    - Literal `name: '005_drop_rewards'` present in the new file.
    - `packages/server/src/db/migrations/index.ts` contains `from './005_drop_rewards.js'` (import) AND `m005` (registration in the array literal).
    - Full-package tsc is intentionally NOT run at task 1 time: server source still has rewards files that import each other, so project-wide tsc would fail by design. Atomic build verification happens in Task 5.
  </acceptance_criteria>
  <done>Migration file created with correct content and registered in the migrations array; structure mirrors 004_drop_storefronts.ts pattern.</done>
</task>

<task type="auto">
  <name>Task 2: Delete rewards code from server (routes, services, db modules, bootstrap, types, middleware, env)</name>
  <files>
    packages/server/src/routes/rewards.ts,
    packages/server/src/services/reward-transfer.ts,
    packages/server/src/services/reward-claims.ts,
    packages/server/src/db/reward-addresses.ts,
    packages/server/src/db/reward-claims.ts,
    packages/server/src/db/campaigns.ts,
    packages/server/src/db/campaign-audit.ts,
    packages/server/src/db/volume-snapshots.ts,
    packages/server/src/db/volume-aggregation.ts,
    packages/server/src/utils/admin.ts,
    packages/server/src/utils/solana-verify.ts,
    packages/server/src/utils/evm-verify.ts,
    packages/server/src/server.ts,
    packages/server/src/middleware/auth.ts,
    packages/server/src/db/index.ts,
    packages/server/src/db/types.ts,
    packages/server/src/db/facilitators.ts,
    packages/server/src/routes/admin.ts,
    packages/server/src/routes/internal-webhooks.ts,
    packages/server/.env.example
  </files>
  <read_first>
    - packages/server/src/server.ts (lines 1–25 for import context, lines 78–90 for the rewards mount)
    - packages/server/src/middleware/auth.ts (full file — 127 lines; line 3 isAdmin import, lines 84–126 requireAdmin function)
    - packages/server/src/db/index.ts (lines 240–280 for Region A facilitator chain_type migration block; lines 590–700 for Region B reward CREATE TABLE blocks; lines 780–795 for Region C re-exports)
    - packages/server/src/db/types.ts (lines 250–335 for reward interface block)
    - packages/server/src/db/facilitators.ts (lines 1–10 for imports; lines 255–340 for ensureFacilitatorMarker + backfillFacilitatorMarkers functions)
    - packages/server/src/routes/admin.ts (lines 1–25 for imports — specifically line 9 `ensureFacilitatorMarker,`; lines 540–560 for the lone call site)
    - packages/server/src/routes/internal-webhooks.ts (lines 15–25 for imports; lines 80–95 for the lone call site)
    - packages/server/.env.example (lines 85–105 for the Rewards Program section boundaries)
    - packages/server/src/utils/solana-verify.ts (full 66 lines — confirm it's purely rewards by reading the message string "OpenFacilitator Rewards" on line 23)
    - packages/server/src/utils/evm-verify.ts (full file — confirm `createEVMVerificationMessage` on lines 7–14 returns the literal `"OpenFacilitator Rewards\n\nSign to verify ownership of: ..."` and `verifyEVMSignature` on lines 20–41; its only consumer is `routes/rewards.ts` lines 20–22 (named imports) and 139, 148 (call sites) — both deleted via `git rm routes/rewards.ts`. Same shape as `utils/solana-verify.ts`.)
  </read_first>
  <action>
    Order of operations matters for keeping the build coherent at the final commit. Execute deletions in this order; after each phase, do NOT yet attempt `pnpm build` — Task 5 runs the atomic build verification.

    **2a. Delete the consumer (the rewards route + mount) FIRST so the downstream DB module deletes don't dangle.**

    - Delete file: `packages/server/src/routes/rewards.ts` (1274 lines). `git rm packages/server/src/routes/rewards.ts` or plain `rm`. The only consumer was `server.ts` line 14 import + line 83 mount, both deleted below.
    - Edit `packages/server/src/server.ts`:
      - Delete line 14: `import { rewardsRouter } from './routes/rewards.js';`
      - Delete lines 82–83:
        ```
          // Rewards API routes (for rewards program)
          app.use('/api/rewards', rewardsRouter);
        ```
        Preserve exactly one blank line between line 80's `app.use('/api/admin', adminRouter);` and the surviving `// Subscription routes` comment that was on line 85.

    **2b. Delete admin middleware scaffolding (D-07).**

    - Delete file: `packages/server/src/utils/admin.ts` (29 lines). Sole consumer was `middleware/auth.ts` line 3 import (deleted below).
    - Edit `packages/server/src/middleware/auth.ts`:
      - Delete line 3: `import { isAdmin } from '../utils/admin.js';`
      - Delete lines 84–126 (the `requireAdmin` middleware function and its JSDoc — anchor start: `/**\n * Middleware to require admin access`; anchor end: closing `}` of `requireAdmin`). After deletion, the file ends with `optionalAuth`'s closing `}` (currently line 82) followed by a single trailing newline. Read lines 78–128 first to confirm exact anchor lines.
    - Verify no other consumers of `requireAdmin` exist (per CONTEXT.md only `routes/rewards.ts` used it, and that file is gone now): `grep -r 'requireAdmin' packages/server/src/` should return zero matches after this edit.

    **2c. Delete the rewards services + utils (including BOTH chain-signature helpers).**

    - Delete file: `packages/server/src/services/reward-transfer.ts` (191 lines). Holds `REWARDS_WALLET_PRIVATE_KEY` + `OPEN_TOKEN_MINT` references — removing this file completes REWARDS-08 / D-12.
    - Delete file: `packages/server/src/services/reward-claims.ts` (249 lines). Consumer was `routes/rewards.ts` (gone).
    - Delete file: `packages/server/src/utils/solana-verify.ts` (66 lines). Confirm `grep -r 'solana-verify\|verifySolanaSignature' packages/server/src/` returns zero matches after `routes/rewards.ts` is gone. If it returns any matches outside `solana-verify.ts` itself, STOP and report — there's an unexpected consumer that needs investigation.
    - Delete file: `packages/server/src/utils/evm-verify.ts` (41 lines). Contains the rewards-only enrollment-verification helpers `createEVMVerificationMessage` (returns `"OpenFacilitator Rewards\n\nSign to verify ownership of: ..."`) and `verifyEVMSignature`. Its only consumer is `routes/rewards.ts` (already deleted in 2a). Symmetry with the Solana variant: both files supported rewards enrollment signature verification; both go in the same atomic commit. Confirm `grep -r 'evm-verify\|verifyEVMSignature\|createEVMVerificationMessage' packages/server/src/` returns zero matches after `routes/rewards.ts` is gone. If any other consumer surfaces, STOP and report.

    **2d. Delete the rewards DB modules + the facilitator-marker integration.**

    - Delete file: `packages/server/src/db/reward-addresses.ts` (135 lines). Consumer 1: `db/index.ts` line 785 re-export (deleted in 2e). Consumer 2: `db/facilitators.ts` line 4 `import { createFacilitatorMarker }` (deleted in 2f).
    - Delete file: `packages/server/src/db/reward-claims.ts` (129 lines).
    - Delete file: `packages/server/src/db/campaigns.ts` (157 lines).
    - Delete file: `packages/server/src/db/campaign-audit.ts` (48 lines).
    - Delete file: `packages/server/src/db/volume-snapshots.ts` (123 lines).
    - Delete file: `packages/server/src/db/volume-aggregation.ts` (672 lines, exclusively rewards/campaign + 2x multiplier per D-08). Confirms ROADMAP SC#3.

    **2e. Strip rewards from `packages/server/src/db/index.ts` (three deletion regions per Region A/B/C in <interfaces>).**

    - **Region A — early `reward_addresses` chain_type migration block (lines 245–277 inclusive):** Delete from the `// Migration: Add 'facilitator' to chain_type CHECK constraint in reward_addresses table` comment through the closing `}` of the `try/catch` plus the trailing blank. Re-read lines 240–280 to verify boundaries before deleting.
    - **Region B — bootstrap CREATE TABLE blocks (lines 595–677 inclusive):** Delete the `-- Reward addresses table` comment through the last `CREATE INDEX IF NOT EXISTS idx_volume_snapshots_date` statement. Surviving line above (594): `CREATE INDEX IF NOT EXISTS idx_claims_status ON claims(status);` (REFUND — preserve). Surviving line below (the line that was at 679): `-- User preferences table (chain preference for payments)`. Place exactly one blank line between them.
    - **Region C — db re-exports (lines 785–790 inclusive):** Delete the 6 `export * from './{reward-addresses,campaigns,campaign-audit,reward-claims,volume-snapshots,volume-aggregation}.js';` lines. Surviving line above: `export * from './claims.js';` (REFUND — preserve). Surviving line below: `export * from './user-preferences.js';`.

    **2f. Strip rewards from `packages/server/src/db/types.ts` (lines 253–330 inclusive):** Delete the 5 reward interfaces and their JSDoc blocks (`RewardAddressRecord`, `CampaignRecord`, `CampaignAuditRecord`, `RewardClaimRecord`, `VolumeSnapshotRecord`). Re-read lines 250–335 to confirm exact line numbers. After deletion the file ends cleanly.

    **2g. Strip the rewards-tracking facilitator-marker glue from `packages/server/src/db/facilitators.ts`:**
    - Delete line 4: `import { createFacilitatorMarker } from './reward-addresses.js';`
    - Delete the `ensureFacilitatorMarker` function and its JSDoc (currently lines 262–311). Anchor on the JSDoc `/**\n * Ensure a facilitator owner has an enrollment marker for volume tracking` and the closing `}` of the function.
    - Delete the `backfillFacilitatorMarkers` function and its JSDoc (currently lines 313–end-of-file or until 338). Anchor on the JSDoc `/**\n * Backfill missing facilitator enrollment markers` and the closing `}` of the function. Read 310–345 to find the exact closing brace.
    - After deletion the file ends cleanly. No `createFacilitatorMarker` reference should remain.

    **2h. Update call sites of `ensureFacilitatorMarker`:**
    - `packages/server/src/routes/admin.ts`: delete line 9 (`  ensureFacilitatorMarker,`) from the existing `import { ... } from '../db/facilitators.js';` block, and delete lines 549–550 (`// Ensure facilitator owner has enrollment marker for volume tracking\n    ensureFacilitatorMarker(ownerAddress);`). Place exactly one blank line where the call site was.
    - `packages/server/src/routes/internal-webhooks.ts`: change line 20 from `import { createFacilitator, ensureFacilitatorMarker } from '../db/facilitators.js';` to `import { createFacilitator } from '../db/facilitators.js';`, and delete lines 86–87 (the comment + call). Place exactly one blank line where the call site was.

    **2i. Strip the Rewards Program section from `packages/server/.env.example`:**
    - Delete lines 93–99 inclusive (the section separator, the `# Rewards Program` header, the closing separator, the two comment lines, and `ADMIN_USER_IDS=`).
    - Surviving line above (92): blank after `SUBSCRIPTION_WEBHOOK_SECRET=` at 91. Surviving line below (100 in pre-edit numbering): `# -----------------------------------------------------------------------------` opening the Trusted Origins section. Place exactly one blank line between them.

    **No 410, no redirect, no stub** — per D-05, Express default 404 is the desired behavior for the deleted `/api/rewards/*` URLs.

    **PROGRESS NOTE (per W8):** After Task 2 completes (all 12 server file deletions + 3 partial-edit regions of db/index.ts + types/middleware/facilitators/routes/env edits land), emit a single-line progress note in the executor's working log: `"Task 2 complete: 12 server files deleted (incl. evm-verify.ts), 9 partial-edit files updated; refund claims subsystem untouched."` This is a non-structural pause point — D-15 still requires a single atomic commit at the end. The note exists to break up context for Task 5's build verification.

    **Refund preservation reminder (DO NOT TOUCH any of these):**
    - `packages/server/src/db/index.ts` `CREATE TABLE IF NOT EXISTS claims` block (~line 568) and its `idx_claims_*` indexes (lines 590–593) — REFUND
    - `packages/server/src/db/index.ts` line 784 `export * from './claims.js';` — REFUND
    - `packages/server/src/db/claims.ts`, `packages/server/src/services/claims.ts`, `packages/server/src/routes/public.ts /api/claims*`, `apps/dashboard/src/components/resource-owner/claims-list.tsx` — REFUND
  </action>
  <verify>
    <automated>test ! -f packages/server/src/routes/rewards.ts && test ! -f packages/server/src/services/reward-transfer.ts && test ! -f packages/server/src/services/reward-claims.ts && test ! -f packages/server/src/db/reward-addresses.ts && test ! -f packages/server/src/db/reward-claims.ts && test ! -f packages/server/src/db/campaigns.ts && test ! -f packages/server/src/db/campaign-audit.ts && test ! -f packages/server/src/db/volume-snapshots.ts && test ! -f packages/server/src/db/volume-aggregation.ts && test ! -f packages/server/src/utils/admin.ts && test ! -f packages/server/src/utils/solana-verify.ts && test ! -f packages/server/src/utils/evm-verify.ts && [ "$(grep -ciE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT|ADMIN_USER_IDS' packages/server/src/server.ts)" = "0" ] && [ "$(grep -ciE 'reward|campaign|\$OPEN|requireAdmin|isAdmin' packages/server/src/middleware/auth.ts)" = "0" ] && [ "$(grep -cE 'CREATE TABLE IF NOT EXISTS reward_addresses|CREATE TABLE IF NOT EXISTS campaigns|CREATE TABLE IF NOT EXISTS reward_claims|CREATE TABLE IF NOT EXISTS volume_snapshots|CREATE TABLE IF NOT EXISTS campaign_audit' packages/server/src/db/index.ts)" = "0" ] && [ "$(grep -cE \"from './(reward-addresses|reward-claims|campaigns|campaign-audit|volume-snapshots|volume-aggregation)\" packages/server/src/db/index.ts)" = "0" ] && [ "$(grep -ciE 'reward|campaign' packages/server/src/db/types.ts)" = "0" ] && [ "$(grep -cE 'createFacilitatorMarker|ensureFacilitatorMarker|backfillFacilitatorMarkers' packages/server/src/db/facilitators.ts)" = "0" ] && [ "$(grep -cE 'ensureFacilitatorMarker' packages/server/src/routes/admin.ts)" = "0" ] && [ "$(grep -cE 'ensureFacilitatorMarker' packages/server/src/routes/internal-webhooks.ts)" = "0" ] && [ "$(grep -cE 'ADMIN_USER_IDS|Rewards Program' packages/server/.env.example)" = "0" ] && [ "$(grep -c 'CREATE TABLE IF NOT EXISTS claims' packages/server/src/db/index.ts)" = "1" ] && [ "$(grep -c \"from './claims.js'\" packages/server/src/db/index.ts)" = "1" ]</automated>
  </verify>
  <acceptance_criteria>
    - All 12 whole-file deletes from D-13 are absent: `rewards.ts`, `reward-transfer.ts`, `reward-claims.ts` (service), `reward-addresses.ts`, `reward-claims.ts` (db), `campaigns.ts`, `campaign-audit.ts`, `volume-snapshots.ts`, `volume-aggregation.ts`, `admin.ts`, `solana-verify.ts`, `evm-verify.ts`. Verified by: `test ! -f packages/server/src/utils/evm-verify.ts` in particular (B1 fix).
    - `grep -ciE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT|ADMIN_USER_IDS' packages/server/src/server.ts` returns `0`.
    - `grep -ciE 'reward|campaign|\$OPEN|requireAdmin|isAdmin' packages/server/src/middleware/auth.ts` returns `0`.
    - `grep -cE 'CREATE TABLE IF NOT EXISTS (reward_addresses|campaigns|reward_claims|volume_snapshots|campaign_audit)' packages/server/src/db/index.ts` returns `0` (5 reward CREATE TABLE blocks gone).
    - `grep -cE "from './(reward-addresses|reward-claims|campaigns|campaign-audit|volume-snapshots|volume-aggregation)" packages/server/src/db/index.ts` returns `0` (re-exports gone).
    - `grep -ciE 'reward|campaign' packages/server/src/db/types.ts` returns `0` (5 reward record interfaces gone).
    - `grep -cE 'createFacilitatorMarker|ensureFacilitatorMarker|backfillFacilitatorMarkers' packages/server/src/db/facilitators.ts` returns `0`.
    - `grep -cE 'ensureFacilitatorMarker' packages/server/src/routes/admin.ts` returns `0`.
    - `grep -cE 'ensureFacilitatorMarker' packages/server/src/routes/internal-webhooks.ts` returns `0`.
    - `grep -cE 'ADMIN_USER_IDS|Rewards Program' packages/server/.env.example` returns `0`.
    - **REFUND PRESERVATION (load-bearing):** `grep -c 'CREATE TABLE IF NOT EXISTS claims' packages/server/src/db/index.ts` returns `1` (refund claims table still bootstrapped); `grep -c "from './claims.js'" packages/server/src/db/index.ts` returns `1` (refund claims db module still re-exported); `test -f packages/server/src/db/claims.ts` true; `test -f packages/server/src/services/claims.ts` true.
    - Full-package server build is intentionally NOT run here — atomic build verification happens in Task 5 after dashboard deletions land.
  </acceptance_criteria>
  <done>All server-side rewards code deleted (incl. evm-verify.ts symmetric with solana-verify.ts); refund claims subsystem preserved untouched; env example stripped of Rewards Program section. Build re-verified atomically in Task 5.</done>
</task>

<task type="auto">
  <name>Task 3: Delete rewards code from dashboard (pages, components, lib/api, chain-signing helpers, partial edits across nav/menu/auth/landing)</name>
  <files>
    apps/dashboard/src/components/rewards/,
    apps/dashboard/src/components/campaigns/,
    apps/dashboard/src/components/facilitator-rewards-section.tsx,
    apps/dashboard/src/components/rewards-info-banner.tsx,
    apps/dashboard/src/lib/evm/verification.ts,
    apps/dashboard/src/lib/solana/verification.ts,
    apps/dashboard/src/app/rewards/page.tsx,
    apps/dashboard/src/app/rewards/admin/page.tsx,
    apps/dashboard/src/lib/api.ts,
    apps/dashboard/src/app/dashboard/[id]/page.tsx,
    apps/dashboard/src/app/dashboard/page.tsx,
    apps/dashboard/src/app/page.tsx,
    apps/dashboard/src/components/navbar.tsx,
    apps/dashboard/src/components/user-menu.tsx,
    apps/dashboard/src/components/archive/wallet-dropdown.tsx,
    apps/dashboard/src/components/auth/auth-provider.tsx,
    apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts,
    apps/dashboard/src/components/dashboard/FeaturesSpotlight/index.tsx
  </files>
  <read_first>
    - apps/dashboard/src/lib/api.ts (lines 1–15 for the rewards types block start; lines 100–115 for the types block end + next section; lines 1295–1315 for methods block start; lines 1455–1462 for methods block end + ApiClient closing brace)
    - apps/dashboard/src/lib/evm/verification.ts (full 50 lines — confirm line 10 has the `"OpenFacilitator Rewards"` literal and line 32 has `api.enrollInRewards({ chain_type: 'evm', ... })`; only consumer is the to-be-deleted `enrollment-modal.tsx`)
    - apps/dashboard/src/lib/solana/verification.ts (full 57 lines — confirm line 12 has the `"OpenFacilitator Rewards"` literal and line 45 has `api.enrollInRewards({ chain_type: 'solana', ... })`; only consumer is `apps/dashboard/src/components/rewards/enrollment-modal.tsx` line 8 import)
    - apps/dashboard/src/components/rewards/enrollment-modal.tsx (line 8 — confirms `import { signAndEnroll } from '@/lib/solana/verification';`; this file is whole-file-deleted as part of `git rm -r components/rewards/` in 3b)
    - apps/dashboard/src/app/dashboard/[id]/page.tsx (lines 40–70 for import + Tab type; lines 440–510 for tab nav + render branches)
    - apps/dashboard/src/app/dashboard/page.tsx (lines 1–30 for imports; lines 65–80 for useAuth; lines 145–195 for RewardsInfoBanner render + FeaturesSpotlight props)
    - apps/dashboard/src/app/page.tsx (lines 480–550 — re-anchor on JSX `<li>` tags rather than fixed line numbers; the two `<li>` blocks to remove both contain `<Link href="/rewards">`)
    - apps/dashboard/src/components/navbar.tsx (full file — 235+ lines; rewards entanglement throughout)
    - apps/dashboard/src/components/user-menu.tsx (full file)
    - apps/dashboard/src/components/archive/wallet-dropdown.tsx (lines 1–130)
    - apps/dashboard/src/components/auth/auth-provider.tsx (full file — 135 lines)
    - apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts (full file — 80 lines)
    - apps/dashboard/src/components/dashboard/FeaturesSpotlight/index.tsx (full file — 76 lines)
  </read_first>
  <action>
    Order matters here too — delete consumers before the modules they consume to keep the file system consistent at every intermediate step. The build is verified atomically in Task 5.

    **3a. Delete dashboard rewards pages (D-06).**

    - `git rm apps/dashboard/src/app/rewards/page.tsx`
    - `git rm apps/dashboard/src/app/rewards/admin/page.tsx`
    - After both files are gone, remove the now-empty `apps/dashboard/src/app/rewards/admin/` and `apps/dashboard/src/app/rewards/` directories: `rmdir apps/dashboard/src/app/rewards/admin && rmdir apps/dashboard/src/app/rewards` (or `git rm -r` the directories). Next.js returns the default 404 for `/rewards` and `/rewards/admin` per D-06.

    **3b. Delete dashboard rewards/campaigns component directories + standalone wrappers + chain-specific signing helpers (D-13 + B2 fix).**

    - `git rm -r apps/dashboard/src/components/rewards/` (entire directory — 15 files: `address-breakdown.tsx`, `address-card.tsx`, `address-list.tsx`, `addresses-tab.tsx`, `claim-button.tsx`, `claim-history.tsx`, `claim-modal.tsx`, `enrollment-modal.tsx`, `history-tab.tsx`, `landing-page.tsx`, `progress-bar.tsx`, `progress-dashboard.tsx`, `progress-tab.tsx`, `remove-address-dialog.tsx`, `reward-estimate.tsx`, `rewards-dashboard.tsx` — note: 15 files matches the actual count; CONTEXT.md says "17 files" which was slightly off).
    - `git rm -r apps/dashboard/src/components/campaigns/` (entire directory — 4 files: `campaign-card.tsx`, `campaign-form.tsx`, `campaign-history.tsx`, `campaign-rules.tsx`).
    - `git rm apps/dashboard/src/components/facilitator-rewards-section.tsx`
    - `git rm apps/dashboard/src/components/rewards-info-banner.tsx`
    - `git rm apps/dashboard/src/lib/evm/verification.ts` — chain-specific EVM signing helper, used only by the now-deleted `enrollment-modal.tsx`. After Task 3c removes `api.enrollInRewards` from `lib/api.ts`, this file's `api.enrollInRewards({ chain_type: 'evm', ... })` call (line 32) would fail typecheck and break `pnpm build` — MUST be deleted in the same atomic commit.
    - `git rm apps/dashboard/src/lib/solana/verification.ts` — chain-specific Solana signing helper, same situation (line 45 calls `api.enrollInRewards`).
    - After deleting both verification files, check if the parent directories are empty and remove them if so:
      ```bash
      [ -z "$(ls -A apps/dashboard/src/lib/evm 2>/dev/null)" ] && rmdir apps/dashboard/src/lib/evm
      [ -z "$(ls -A apps/dashboard/src/lib/solana 2>/dev/null)" ] && rmdir apps/dashboard/src/lib/solana
      ```
      Both directories should contain only `verification.ts` pre-delete; both directories should be removed after delete.

    **3c. Strip rewards from `apps/dashboard/src/lib/api.ts` (two regions).**

    - **Region A — Types block:** Delete lines 3–110 inclusive (the `// Rewards Program types` comment block through `CreateCampaignRequest` interface closing `}`). Re-read lines 1–115 to confirm exact boundaries (the `// Payment Link types` or equivalent comment marking the next section should remain). Place exactly one blank line between `const API_BASE = ...` (line 1) and the next surviving types section comment.
    - **Region B — Methods block:** Delete lines 1304–1458 inclusive (the `  // Rewards Program` comment through the closing `}` of `getClaimHistory`'s method body — the last method in the block). Re-read lines 1295–1462 to confirm. Place exactly one blank line between the previous method's closing `});` and the closing `}` of the `ApiClient` class on line 1459.

    The 10 interfaces removed: `RewardsStatus`, `RewardsEnrollResponse`, `Campaign`, `CampaignAudit`, `CampaignWithAudit`, `CampaignStats`, `VolumeBreakdown`, `CampaignHistoryItem`, `RewardClaim`, `CreateCampaignRequest`. The 20 methods removed: `getRewardsStatus`, `enrollInRewards`, `deleteRewardAddress`, `getCampaigns`, `getCampaign`, `createCampaign`, `updateCampaign`, `publishCampaign`, `endCampaign`, `deleteCampaign`, `getActiveCampaign`, `getCampaignHistory`, `getCampaignStats`, `getRewardsVolume`, `getVolumeBreakdown`, `getFacilitatorVolume`, `initiateClaim`, `getMyClaim`, `getClaimEligibility`, `getClaimHistory`.

    **3d. Strip rewards from `apps/dashboard/src/app/dashboard/[id]/page.tsx` (four edits).**

    - Delete line 46: `import { FacilitatorRewardsSection } from '@/components/facilitator-rewards-section';`.
    - Line 58: change `type Tab = 'transactions' | 'products' | 'webhooks' | 'refunds' | 'rewards' | 'settings';` to `type Tab = 'transactions' | 'products' | 'webhooks' | 'refunds' | 'settings';` — remove only ` | 'rewards'`.
    - Delete lines 466–476 inclusive (the entire rewards tab `<button>` element including its surrounding `<button>...</button>` tags). Verify by reading lines 460–490 — the Refunds button must end at the line immediately above the Rewards button's `<button` opening tag, and the Settings button must start at the line immediately below the Rewards button's closing `</button>` tag.
    - Delete lines 498–499 inclusive (the two-line render branch `) : activeTab === 'rewards' ? (\n            <FacilitatorRewardsSection facilitatorId={id} />`). After deletion the JSX chain reads `... refunds ? ( <RefundsSection /> ) : activeTab === 'transactions' ? ( ...`. Re-read lines 495–505 after the edit to confirm JSX balance.

    **3e. Strip rewards from `apps/dashboard/src/app/dashboard/page.tsx` (four edits).**

    - Delete line 22: `import { RewardsInfoBanner } from '@/components/rewards-info-banner';`.
    - Line 70: change `const { isLoading: authLoading, isAuthenticated, isEnrolled } = useAuth();` to `const { isLoading: authLoading, isAuthenticated } = useAuth();` — remove `, isEnrolled` (this property no longer exists on the auth context after 3i).
    - Delete lines 148–149 (`{/* Rewards Info Banner */}\n        <RewardsInfoBanner />`). Place exactly one blank line where they were.
    - Delete line 185 (`          isEnrolled={isEnrolled}`) from the `<FeaturesSpotlight>` props block. The FeaturesSpotlight prop `isEnrolled` is also removed in 3j.

    **3f. Strip rewards bullets from `apps/dashboard/src/app/page.tsx` (D-09, JSX-anchored per B5).**

    Line numbers in this file shift with every preceding edit and the original B5 finding showed inconsistent block sizes for what should be identical `<li>` blocks — anchoring on fixed line ranges is brittle. Use JSX-tag anchoring instead:

    1. Read `apps/dashboard/src/app/page.tsx` lines 480–550 first (about 70 lines of context).
    2. Locate the two `<li>` blocks that contain `<Link href="/rewards">`. Both blocks have the same outer shape:
       ```jsx
       <li className="flex items-center gap-2">
         <Check className="w-4 h-4 text-primary" />
         <Link href="/rewards" className="hover:text-primary transition-colors">
           Earn $OPEN rewards{...optional " (2x bonus)" for Starter tier}
         </Link>
       </li>
       ```
       — verify by visual inspection before edit. The Free-tier bullet contains plain `Earn $OPEN rewards`; the Starter-tier bullet contains `Earn $OPEN rewards (2x bonus)`.
    3. For each `<li>` block: locate the `<li ...>` opening tag (line begins with whitespace then `<li`); locate its matching `</li>` closing tag (the FIRST `</li>` after the corresponding `<Link href="/rewards">`). Delete every line from the `<li ...>` opening through and including the `</li>` closing — typically 5 lines (`<li>` open, `<Check />`, `<Link>` open, "Earn ..." text, `</Link>` close + `</li>` close, depending on how the text + closing tags are split).
    4. After both deletions, both `<ul>` containers retain their other 4 bullets each (Free: 5→4; Starter: 5→4) per D-09. No substitution, no replacement.

    Verification post-edit (run from repo root):
    ```bash
    [ "$(grep -c '\$OPEN' apps/dashboard/src/app/page.tsx)" = "0" ]
    [ "$(grep -c 'href=\"/rewards\"' apps/dashboard/src/app/page.tsx)" = "0" ]
    ```
    Both MUST return zero. If either non-zero, repeat step 2–3 to find the missed `<li>` block.

    **3g. Heavy surgery on `apps/dashboard/src/components/navbar.tsx`. Follow the <interfaces> spec for each of the 10 edits:**

    1. Line 6: remove `Trophy` from lucide-react import.
    2. Lines 7, 11: After all the other edits below, scan `apps/dashboard/src/components/navbar.tsx` for surviving uses of `useQuery` and `api` (the `import { api } from '@/lib/api'` and `import { useQuery } from '@tanstack/react-query'`). If neither is used elsewhere in the file post-edit, remove both imports. The only known consumers were the `activeCampaign` useQuery (deleted in step 4) and any `api.*` calls inside the same block.
    3. Lines 13–22: delete the `formatTokenAmount` helper.
    4. Inside `Navbar()`:
       - Line 26: drop `, isAdmin` from the `useAuth()` destructure.
       - Line 28: delete the `bannerDismissed` state.
       - Lines 30–33: delete the `useQuery({ queryKey: ['activeCampaign'], ... })` block.
       - Lines 35–39: delete the `daysRemaining` computation.
       - Lines 41–46: delete the localStorage `useEffect`.
       - Lines 48–51: delete the `dismissBanner` callback.
       - Line 54: delete `const showBanner = ...`.
    5. Lines 60–86: delete the entire `{showBanner && (...)}` Rewards Banner JSX block.
    6. Line 88: change the `<nav>` className from the template literal `\`fixed w-full ... ${showBanner ? 'top-10' : 'top-0'}\`` to the plain string `"fixed w-full z-50 border-b border-border/50 backdrop-blur-xl bg-background/80 top-0"`.
    7. Lines 98–106: delete the desktop `<a href="https://www.coingecko.com/en/coins/openfacilitator" ...>$OPEN</a>` element.
    8. Lines 134–138: delete the desktop `{isAdmin && (<span ...>Admin</span>)}` block.
    9. Lines 166–175 (mobile menu): delete the mobile `<a ...>$OPEN</a>` block AND the immediately following separator `<div className="border-t border-border/50 my-2" />`. Re-read lines 160–185 to confirm.
    10. Lines 208–212: delete the mobile `{isAdmin && (<span ...>Admin</span>)}` block.

    After all edits, the navbar contains zero rewards/`$OPEN`/Trophy/isAdmin references. The grep gate verifies this in Task 4.

    **3h. Strip rewards from `apps/dashboard/src/components/user-menu.tsx` (three edits).**

    - Line 5: remove `Trophy` from the lucide-react import.
    - Line 23: change `const { user, signOut, hasClaimable } = useAuth();` to `const { user, signOut } = useAuth();`.
    - Lines 54–62: delete the entire `<DropdownMenuItem>` block containing `<Link href="/rewards">Rewards ... hasClaimable ...</Link>`. The surrounding Subscriptions item (above) and DropdownMenuSeparator (below) remain.

    **3i. Replace `apps/dashboard/src/components/auth/auth-provider.tsx` wholesale with the simplified version specified in <interfaces>.** This removes rewards status fetching, the `isAdmin / isEnrolled / isFacilitatorOwner / hasClaimable / refetchRewardsStatus` context properties, and the `api.getRewardsStatus()` / `api.getActiveCampaign()` / `api.getClaimEligibility()` calls. Use Write tool to overwrite the file with the contents from the <interfaces> section.

    **3j. Replace `apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts` and `index.tsx` wholesale with the simplified versions specified in <interfaces>.** Remove `isEnrolled` prop, remove rewards card config, remove enrollment modal dynamic import, remove `'enrollRewards'` onClick branch, simplify the card-ordering logic to 2 cards (refunds + facilitator).

    **3k. Strip rewards from `apps/dashboard/src/components/archive/wallet-dropdown.tsx` (two edits).**

    Read the file first (lines 1–130). Then:
    - Line 28: change `const { user, signOut, hasClaimable } = useAuth();` to `const { user, signOut } = useAuth();`.
    - Find and delete the Rewards Link block: the `{/* Rewards Link */}` comment, the preceding `<div className="border-t border-border" />` separator if it exists for the rewards link grouping, and the entire `<div className="p-2"><Link href="/rewards">Rewards ... hasClaimable ... </Link></div>` block. Confirm with a read of lines 105–125. If `Trophy` is imported at the top of the file, remove it from the lucide-react import.

    **PROGRESS NOTE (per W8):** After Task 3 completes (rewards-page deletes + 19 component-directory/file deletes incl. the two chain-signing helpers + 11 partial-edit files), emit: `"Task 3 complete: 21 dashboard files/dirs deleted (incl. lib/evm + lib/solana verification helpers), 11 partial-edit files updated; consumer migrations clean."` Then proceed to Task 4 (planning docs + grep gate).

    **DO NOT TOUCH:**
    - `apps/dashboard/src/components/resource-owner/claims-list.tsx` — REFUND claims UI
    - `apps/dashboard/src/app/refunds/`, `apps/dashboard/src/components/refunds-section.tsx`, `apps/dashboard/src/app/docs/sdk/refunds/page.mdx` — REFUND
    - Anything containing only the word `claim` / `Claim` without `reward` context (REFUND uses `claim` too — the grep gate is intentionally NOT matching `claim` alone).
  </action>
  <verify>
    <automated>test ! -d apps/dashboard/src/components/rewards && test ! -d apps/dashboard/src/components/campaigns && test ! -f apps/dashboard/src/components/facilitator-rewards-section.tsx && test ! -f apps/dashboard/src/components/rewards-info-banner.tsx && test ! -f apps/dashboard/src/lib/evm/verification.ts && test ! -f apps/dashboard/src/lib/solana/verification.ts && test ! -f apps/dashboard/src/app/rewards/page.tsx && test ! -f apps/dashboard/src/app/rewards/admin/page.tsx && [ "$(grep -ciE 'reward|campaign|\$OPEN' apps/dashboard/src/lib/api.ts)" = "0" ] && [ "$(grep -ciE 'reward|FacilitatorRewards' apps/dashboard/src/app/dashboard/\[id\]/page.tsx)" = "0" ] && [ "$(grep -ciE 'reward|RewardsInfo' apps/dashboard/src/app/dashboard/page.tsx)" = "0" ] && [ "$(grep -c '\$OPEN' apps/dashboard/src/app/page.tsx)" = "0" ] && [ "$(grep -c 'href=\"/rewards\"' apps/dashboard/src/app/page.tsx)" = "0" ] && [ "$(grep -ciE 'reward|\$OPEN|Trophy|isAdmin' apps/dashboard/src/components/navbar.tsx)" = "0" ] && [ "$(grep -ciE 'reward|hasClaimable|Trophy' apps/dashboard/src/components/user-menu.tsx)" = "0" ] && [ "$(grep -ciE 'reward|hasClaimable|isAdmin|isEnrolled|isFacilitatorOwner|refetchRewardsStatus|RewardsStatus' apps/dashboard/src/components/auth/auth-provider.tsx)" = "0" ] && [ "$(grep -ciE 'reward|enrollRewards|isEnrolled' apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts)" = "0" ] && [ "$(grep -ciE 'reward|isEnrolled|EnrollmentModal' apps/dashboard/src/components/dashboard/FeaturesSpotlight/index.tsx)" = "0" ] && test -f apps/dashboard/src/components/resource-owner/claims-list.tsx</automated>
  </verify>
  <acceptance_criteria>
    - Dashboard rewards directories absent: `test ! -d apps/dashboard/src/components/rewards`, `test ! -d apps/dashboard/src/components/campaigns`.
    - Standalone rewards components absent: `facilitator-rewards-section.tsx`, `rewards-info-banner.tsx`.
    - **Chain-specific signing helpers absent (B2 fix):** `test ! -f apps/dashboard/src/lib/evm/verification.ts` AND `test ! -f apps/dashboard/src/lib/solana/verification.ts`. Both parent directories (`apps/dashboard/src/lib/evm/`, `apps/dashboard/src/lib/solana/`) should also be gone (empty after deletion).
    - Dashboard rewards pages absent: `apps/dashboard/src/app/rewards/page.tsx`, `apps/dashboard/src/app/rewards/admin/page.tsx`. The `apps/dashboard/src/app/rewards/` directory tree is gone.
    - `grep -ciE 'reward|campaign|\$OPEN' apps/dashboard/src/lib/api.ts` returns `0`.
    - `grep -ciE 'reward|FacilitatorRewards' apps/dashboard/src/app/dashboard/[id]/page.tsx` returns `0`. The `Tab` union no longer contains `'rewards'`: `grep -c "'rewards'" apps/dashboard/src/app/dashboard/[id]/page.tsx` returns `0`.
    - `grep -ciE 'reward|RewardsInfo' apps/dashboard/src/app/dashboard/page.tsx` returns `0`.
    - **Landing page (B5 fix — JSX-anchored deletion):** `grep -c '\$OPEN' apps/dashboard/src/app/page.tsx` returns `0` AND `grep -c 'href="/rewards"' apps/dashboard/src/app/page.tsx` returns `0`. Both pricing bullets deleted per D-09.
    - `grep -ciE 'reward|\$OPEN|Trophy|isAdmin' apps/dashboard/src/components/navbar.tsx` returns `0`.
    - `grep -ciE 'reward|hasClaimable|Trophy' apps/dashboard/src/components/user-menu.tsx` returns `0`.
    - `grep -ciE 'reward|hasClaimable|isAdmin|isEnrolled|isFacilitatorOwner|refetchRewardsStatus|RewardsStatus' apps/dashboard/src/components/auth/auth-provider.tsx` returns `0`.
    - `grep -ciE 'reward|enrollRewards|isEnrolled' apps/dashboard/src/components/dashboard/FeaturesSpotlight/featureCards.ts` returns `0`.
    - `grep -ciE 'reward|isEnrolled|EnrollmentModal' apps/dashboard/src/components/dashboard/FeaturesSpotlight/index.tsx` returns `0`.
    - **REFUND PRESERVATION (load-bearing):** `test -f apps/dashboard/src/components/resource-owner/claims-list.tsx` true (refund claims UI preserved).
    - Dashboard build is intentionally NOT run here — atomic build verification in Task 5.
  </acceptance_criteria>
  <done>Dashboard rewards code fully removed (incl. lib/evm + lib/solana verification helpers per B2); refund claims UI preserved; landing page bullets dropped per D-09 with JSX-anchored deletion per B5; auth-provider simplified to passthrough; navbar / user-menu / FeaturesSpotlight reduced to non-rewards surface. Build re-verified atomically in Task 5.</done>
</task>

<task type="auto">
  <name>Task 4: Update planning docs and SDK reference (REQUIREMENTS, PROJECT, sdk-api), then full-repo grep gate</name>
  <files>
    .planning/REQUIREMENTS.md,
    .planning/PROJECT.md,
    skills/openfacilitator/references/sdk-api.md
  </files>
  <read_first>
    - .planning/REQUIREMENTS.md (full — already loaded via planning context; find the REWARDS-02 line)
    - .planning/PROJECT.md (full — already loaded; identify exact line numbers for `## What This Is`, `## Core Value`, `### Validated`, `### Future`, `## Context`, `## Constraints`, `## Key Decisions`)
    - skills/openfacilitator/references/sdk-api.md (full — verify whether any rewards/campaign/`$OPEN` content exists outside the refund `## Claims Functions` section at line 50)
  </read_first>
  <action>
    **4a. Fix REWARDS-02 in `.planning/REQUIREMENTS.md` per D-04.**

    Find this exact line:
    ```
    - [ ] **REWARDS-02**: Rewards DB tables dropped via migration (`reward_addresses`, `campaigns`, `claims`, `volume_snapshots`)
    ```
    Replace with:
    ```
    - [ ] **REWARDS-02**: Rewards DB tables dropped via migration (`reward_addresses`, `reward_claims`, `campaigns`, `campaign_audit`, `volume_snapshots`)
    ```
    Three changes: `claims` → `reward_claims`, add `campaign_audit` as the 4th entry, keep the canonical 5-table set from D-01.

    No other edits to REQUIREMENTS.md.

    **4b. Rewrite `.planning/PROJECT.md` per D-10. Use the Write or Edit tool to make the following changes, in order.**

    1. `## What This Is` block — replace the current paragraph with this EXACT text:
       ```
       A multi-tenant x402 payment facilitator with multi-chain subscription management. Users can create facilitators on custom domains and accept payments across Base and Solana with built-in subscription billing.
       ```
    2. `## Core Value` block — D-10 LOCKED EXACT TEXT. Replace the current paragraph with this EXACT text:
       ```
       A multi-tenant x402 payment facilitator with multi-chain subscription management. Run a free shared facilitator at pay.openfacilitator.io, or stand up your own at a custom domain. Built-in subscription billing across Base and Solana, with chain preference, grace periods, and payment notifications.
       ```
    3. `### Validated` block — delete the 9 rewards-specific bullets enumerated in <interfaces>:
       - `Free users can register for rewards tracking — v1.0`
       - `Users can add/verify Solana and EVM pay-to addresses — v1.0`
       - `Dashboard shows volume, threshold progress, estimated rewards — v1.0`
       - `Facilitator owners get 2x multiplier automatically — v1.0`
       - `Admin can create and manage reward campaigns — v1.0`
       - `Volume calculated from transaction logs for verified addresses — v1.0`
       - `Users can claim $OPEN tokens when threshold met — v1.0`
       - `SPL token transfer from rewards wallet on claim — v1.0`
       - `Claim history with transaction signatures — v1.0`
    4. `### Future` block — delete the `Sybil cluster detection dashboard for admins` bullet; change `Email notifications when threshold reached or claim available` to `Email notifications`.
    5. `## Context` block — replace the entire section (currently "Production readiness" + "Known limitations" subsections, both rewards-specific) with:
       ```
       ## Context

       **Production readiness:**
       - CRON_SECRET for subscription billing cron
       - Stripe-style billing wallet flow for facilitator subscriptions
       ```
    6. `## Constraints` block — delete the `- **Token**: $OPEN on Solana — SPL token transfers via @solana/spl-token` bullet and the `- **Timeline**: v1.0-v1.2 shipped, claims available March 2026` bullet. Preserve the Database, Auth, UI bullets.
    7. `## Key Decisions` table — delete the 10 rewards-specific rows enumerated in <interfaces>. Preserve the non-rewards decision rows. The "Facilitator markers in reward_addresses" row in particular MUST be deleted (it's stale — that mechanism is gone with this phase).
    8. `## Current Milestone` block (lines 18–37) — preserve as-is (it accurately describes the work this phase is doing).

    Update the trailing `*Last updated:* ...` line at the bottom of `.planning/PROJECT.md` to `*Last updated: 2026-05-17 — Phase 23 rewards removal: Core Value rewritten, rewards entries removed from Validated / Future / Context / Constraints / Key Decisions*`.

    **4c. Audit `skills/openfacilitator/references/sdk-api.md` per D-11.**

    Read the file fully. Then:
    - Run `grep -niE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' skills/openfacilitator/references/sdk-api.md` to enumerate any rewards content.
    - For each match, determine whether it's inside the existing `## Claims Functions` section (line 50 — refund claims SDK with `withRefundProtection`, `createRefundMiddleware`, `honoRefundMiddleware`, `reportFailure`, `getClaimable`, `executeClaim`, `getClaimHistory` per D-11; KEEP unchanged) or outside it (rewards content; DELETE).
    - **Heading reality:** The actual H2 heading is `## Claims Functions` (line 50), not `## Claims / Refunds` (the string `Claims / Refunds` appears only as a code-block JS comment on line 64). CONTEXT.md D-11 cites the heading inaccurately; align to the file.
    - If the file has NO rewards content (CONTEXT.md called this "likely a near-no-op"), record that fact in the SUMMARY: `sdk-api.md — no rewards content found; refund Claims Functions section (line 50) preserved unchanged. D-11/REWARDS-09 satisfied trivially.`
    - If rewards content IS present, delete only the rewards-specific portions (entire sections, code blocks, or links per the surrounding markdown structure). Anchor each deletion on the exact heading or code fence. Do NOT touch the refund `## Claims Functions` section regardless of textual proximity.

    **4d. Full-repo grep gate (run from repo root, after 4a–4c land).**

    The grep gate from D-15 / CONTEXT.md verification-surface item #1:
    ```bash
    grep -riE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' packages/ apps/ skills/openfacilitator/ --include='*.ts' --include='*.tsx' --include='*.md'
    ```

    Expected: zero matches **outside** the exemption set. Exempt matches are allowed in:
    - `packages/server/src/db/migrations/005_drop_rewards.ts` (the new migration MUST name the tables it drops — by design)
    - `packages/server/src/db/migrations/index.ts` (imports both the new migration `from './005_drop_rewards.js'` / `m005` AND the legacy `from './002_campaign_audit_table.js'` / `m002` — both must remain to preserve migration history)
    - `packages/server/src/db/migrations/002_campaign_audit_table.ts` (legacy migration that created the campaign_audit table; cannot be deleted — migration files are append-only or they break replay history. The file is referenced by `m002` in `migrations/index.ts`.)
    - `.planning/` historical docs (NOT included in the grep above — `.planning/` is not under `packages/`, `apps/`, or `skills/`)

    Any match outside those exempt files indicates incomplete deletion. The executor MUST:
    1. Run the grep gate. Pipe through `grep -vE` to remove the exempt files (B3 fix — covers BOTH `005_drop_rewards` AND `002_campaign_audit_table` files plus their imports in `migrations/index.ts`):
       ```bash
       grep -riE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' packages/ apps/ skills/openfacilitator/ --include='*.ts' --include='*.tsx' --include='*.md' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.next --exclude-dir=.turbo | grep -vE '005_drop_rewards|002_campaign_audit_table|migrations/index\.ts:.*(005_drop_rewards|m005|002_campaign_audit_table|m002)'
       ```
    2. If the filtered count is non-zero, dump matches, classify each one (in-scope deletion missed vs. comment-only on preserved code vs. truly out-of-scope), and either delete the match or rewrite the comment prose (Phase 22 2b.1 pattern).
    3. Per the "grep gate hygiene" rule in the planner config: use `grep -v '^#'` style filtering only when matches are pure comments — here we expect zero matches total in source code, so any match warrants inspection.

    Do NOT consider this task complete until the filtered grep count is `0`.

    Also note: in-progress build artifacts (`apps/dashboard/.next/`, `packages/server/dist/`) WILL contain rewards strings until rebuilt — those are not in the grep above (the `--include='*.ts'` filter excludes `.next/` cache files which are mostly `.pack` binaries). If `node_modules/` or `dist/` matches appear, exclude them via `--exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.next --exclude-dir=.turbo`.
  </action>
  <verify>
    <automated>grep -q "reward_claims.*campaigns.*campaign_audit.*volume_snapshots" .planning/REQUIREMENTS.md && grep -qF "A multi-tenant x402 payment facilitator with multi-chain subscription management. Run a free shared facilitator at pay.openfacilitator.io" .planning/PROJECT.md && [ "$(grep -ciE '\$OPEN|reward campaign|reward tokens' .planning/PROJECT.md | head -1)" -le "5" ] && [ "$(grep -ciE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' skills/openfacilitator/references/sdk-api.md)" = "0" ] && [ "$(grep -c '^## Claims Functions$' skills/openfacilitator/references/sdk-api.md)" = "1" ] && [ "$(grep -riE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' packages/ apps/ skills/openfacilitator/ --include='*.ts' --include='*.tsx' --include='*.md' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.next --exclude-dir=.turbo 2>/dev/null | grep -vE '005_drop_rewards|002_campaign_audit_table|migrations/index\.ts:.*(005_drop_rewards|m005|002_campaign_audit_table|m002)' | wc -l | tr -d ' ')" = "0" ]</automated>
  </verify>
  <acceptance_criteria>
    - `.planning/REQUIREMENTS.md` REWARDS-02 line contains the 5-table set `reward_addresses, reward_claims, campaigns, campaign_audit, volume_snapshots` (not `claims`). Verified by `grep -q "reward_claims.*campaigns.*campaign_audit.*volume_snapshots" .planning/REQUIREMENTS.md`.
    - `.planning/PROJECT.md` `## Core Value` contains the D-10 locked text verbatim: `grep -qF "A multi-tenant x402 payment facilitator with multi-chain subscription management. Run a free shared facilitator at pay.openfacilitator.io"`.
    - `.planning/PROJECT.md` has the 9 rewards-specific Validated bullets removed (manual spot-check; `grep -c "Earn .OPEN\|2x multiplier\|reward campaigns" .planning/PROJECT.md` should be low — the historical `## Current Milestone` text legitimately mentions "rewards" in the context of removing it, which is fine).
    - `skills/openfacilitator/references/sdk-api.md` has zero matches for `reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT` (case-insensitive). The refund `## Claims Functions` section header is preserved exactly (B4 fix): `grep -c "^## Claims Functions$" skills/openfacilitator/references/sdk-api.md` returns `1`. The refund SDK exports use `claim` not `reward` and are preserved.
    - Filtered full-repo grep gate (B3 fix — exemption regex covers both 005_drop_rewards AND 002_campaign_audit_table) returns `0`:
      ```
      grep -riE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' packages/ apps/ skills/openfacilitator/ --include='*.ts' --include='*.tsx' --include='*.md' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.next --exclude-dir=.turbo | grep -vE '005_drop_rewards|002_campaign_audit_table|migrations/index\.ts:.*(005_drop_rewards|m005|002_campaign_audit_table|m002)' | wc -l
      ```
      MUST return `0`. Satisfies ROADMAP SC#1, REWARDS-01/03/04/05/06/07/08/09.
    - **REFUND PRESERVATION:** `grep -c "^## Claims Functions$" skills/openfacilitator/references/sdk-api.md` returns `1` (the refund SDK section header preserved verbatim).
  </acceptance_criteria>
  <done>Planning docs match D-04 (REQUIREMENTS-02 5-table set) and D-10 (PROJECT.md Core Value locked text). SDK reference contains no rewards content; refund `## Claims Functions` section unchanged. Full-repo grep gate clean (B3 exemption covers both new and legacy migration files).</done>
</task>

<task type="auto">
  <name>Task 5: Build verification + migration smoke test (server + dashboard + 005_drop_rewards on existing AND fresh DBs)</name>
  <files>(verification-only task — no file edits; reads server DB after startup)</files>
  <read_first>
    - packages/server/src/db/index.ts (confirm `runMigrations(db)` is invoked at bootstrap and that the 5 reward CREATE TABLE blocks are gone from Region B per Task 2e)
    - packages/server/src/db/migrations/index.ts (confirm m005 is registered post-Task 1)
    - packages/server/scripts/smoke-test-migration.mjs if present from Phase 22 (smoke test runner — reuse if available)
  </read_first>
  <action>
    Validates both builds compile and migration 005 runs cleanly + idempotently on the existing dev DB AND on a fresh DB. Phase 22 established the smoke-test pattern (running migrations via the bootstrap path or via a one-shot script); reuse whatever method Phase 22 settled on.

    **5a. Clear build caches before any build invocation (W7 fix).**

    The current working tree has `M apps/dashboard/tsconfig.tsbuildinfo` (dirty incremental TS build cache). After bulk deletions, a cached `pnpm build` might short-circuit type-checking of deleted-import paths. Clear all caches first:
    ```bash
    rm -rf apps/dashboard/.next apps/dashboard/.turbo packages/server/dist apps/dashboard/tsconfig.tsbuildinfo packages/server/tsconfig.tsbuildinfo
    ```
    Also nuke any `node_modules/.cache` if present from prior builds. This ensures the builds in 5b/5c re-check every import path against the post-deletion file system.

    **5b. Server build:**
    ```bash
    pnpm --filter @openfacilitator/server build
    ```
    Must exit 0. Common failure modes and fixes:
    - Missing-import errors mentioning `./reward-*.js`, `./campaigns.js`, `./campaign-audit.js`, `./volume-snapshots.js`, `./volume-aggregation.js`, `RewardAddressRecord`, `CampaignRecord`, etc. → Task 2 deletion was incomplete; grep the failing import path, locate the consumer, delete or update the reference.
    - Missing-import errors for `createFacilitatorMarker`, `ensureFacilitatorMarker`, `backfillFacilitatorMarkers` → Task 2g/2h missed a call site; grep `packages/server/src/` for the function name and remove the consumer.
    - Errors on `m005` or the new migration file → Task 1 registration incomplete or syntax error; re-check `005_drop_rewards.ts` and `migrations/index.ts`.
    - Errors mentioning `requireAdmin`, `isAdmin`, `ADMIN_USER_IDS` → Task 2b missed a consumer; grep and delete.
    - Errors mentioning `verifySolanaSignature`, `createVerificationMessage`, `solana-verify` → Task 2c missed a consumer; grep and delete.
    - Errors mentioning `verifyEVMSignature`, `createEVMVerificationMessage`, `evm-verify` → Task 2c missed `evm-verify.ts` consumer (B1 — should be impossible since only `routes/rewards.ts` consumed it and that's deleted); grep `packages/server/src/` for the symbol and remove.
    - Other errors → out of scope; report to user before continuing.

    **5c. Dashboard build:**
    ```bash
    pnpm --filter @openfacilitator/dashboard build
    ```
    Must exit 0. Common failure modes and fixes:
    - Missing-import errors mentioning `@/components/rewards/*`, `@/components/campaigns/*`, `@/components/facilitator-rewards-section`, `@/components/rewards-info-banner` → Task 3 deletion was incomplete; grep for the import path and remove.
    - Missing-import errors mentioning `@/lib/evm/verification` or `@/lib/solana/verification` (B2 — should be impossible since only the deleted `enrollment-modal.tsx` consumed them) → grep `apps/dashboard/src/` and remove the import; ensure the parent `lib/evm/` and `lib/solana/` directories are gone.
    - Missing-import errors for `Reward*` / `Campaign*` types from `@/lib/api` → Task 3c regions A/B were incomplete.
    - JSX balance errors in `dashboard/[id]/page.tsx` → 3d tab button or render-branch edits left unbalanced parens/JSX. Re-read lines 460–510 and repair.
    - JSX balance errors in `app/page.tsx` → 3f's JSX-anchored deletion (B5) missed a closing tag. Re-read the file around the affected `<ul>` and ensure the surrounding bullets close properly.
    - TypeScript errors on `auth.isAdmin` / `auth.isEnrolled` / `auth.isFacilitatorOwner` / `auth.hasClaimable` / `auth.refetchRewardsStatus` → some consumer of `useAuth()` still destructures a removed property. Grep `useAuth()` consumers and fix.
    - Errors mentioning `Trophy` from lucide-react → 3g/3h missed a Trophy import.
    - Errors on `formatTokenAmount` / `activeCampaign` / `daysRemaining` / `showBanner` / `dismissBanner` / `bannerDismissed` → 3g navbar surgery incomplete.

    **5d. Migration smoke test — UNCONDITIONAL (W6 fix). Two sub-cases: existing dev DB AND fresh DB.**

    Per ROADMAP SC#2: "Migration drops 5 reward tables cleanly on FRESH AND EXISTING DBs". Previous gated approach (`[ ! -f dev.db ] || ( ... )`) would silently pass when no dev DB existed — leaving SC#2 unverified. The smoke test MUST run regardless of dev DB presence.

    **5d-i. Existing-DB smoke (only if dev DB present):**

    1. Check the dev DB exists:
       ```bash
       test -f packages/server/data/openfacilitator.db && echo "EXISTING_DB" || echo "NO_DEV_DB"
       ```
    2. If `EXISTING_DB`: pre-state snapshot:
       ```bash
       sqlite3 packages/server/data/openfacilitator.db ".tables" | tr ' ' '\n' | grep -E "^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$" | sort
       ```
       Record output. On a dev DB that has been running since v1.0, expect to see all 5 tables. On a newer dev DB it may be a subset.
    3. Trigger the migration. Three acceptable methods (planner's discretion per CONTEXT.md):
       a. **Reuse Phase 22's smoke test runner** if `packages/server/scripts/smoke-test-migration.mjs` exists:
          ```bash
          node packages/server/scripts/smoke-test-migration.mjs
          ```
       b. **Start dev server briefly:**
          ```bash
          pnpm --filter @openfacilitator/server dev
          ```
          in the background, wait for log line `✅ Migration complete: 005_drop_rewards` (or `🔄 Running migration: 005_drop_rewards` followed by no error in 5s), then kill the process.
       c. **One-shot node script:**
          ```bash
          node --input-type=module -e "import('./packages/server/dist/db/index.js').then(m => m.initializeDatabase('./packages/server/data/openfacilitator.db'))"
          ```
          (requires server to be built first per 5b). Record chosen method in SUMMARY.
    4. Post-state check:
       ```bash
       [ "$(sqlite3 packages/server/data/openfacilitator.db '.tables' | tr ' ' '\n' | grep -cE '^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$')" = "0" ]
       ```
       Must be true. All 5 reward tables dropped.
    5. Migration recorded:
       ```bash
       sqlite3 packages/server/data/openfacilitator.db "SELECT name FROM migrations WHERE name='005_drop_rewards';"
       ```
       Must return exactly one row.
    6. Idempotency — second run: Trigger the migration again by the same method. Expected: runner sees `005_drop_rewards` already in `migrations` table, skips it (no-op). No error. The post-state table check still passes. The `migrations` table still has exactly one row for `005_drop_rewards` (no duplicate).
    7. **REFUND PRESERVATION CHECK** — confirm the `claims` table (refund) is still present:
       ```bash
       [ "$(sqlite3 packages/server/data/openfacilitator.db '.tables' | tr ' ' '\n' | grep -c '^claims$')" = "1" ]
       ```
       Must return true.

    **5d-ii. Fresh-DB smoke (ALWAYS run — W6 fix; do NOT skip when no dev DB exists):**

    Create a temp DB at a unique path and run the full bootstrap (which invokes `runMigrations` and applies all 5 migrations including 005) against it. This verifies fresh-DB behavior unconditionally — ROADMAP SC#2's "fresh DB" half.

    ```bash
    TMP_DB="/tmp/of-smoke-$$.db"
    export DATABASE_PATH="$TMP_DB"
    # Trigger bootstrap (uses the same method as 5d-i step 3; choose one):
    # Method A (preferred — reuse Phase 22 runner if it accepts a path arg):
    DATABASE_PATH="$TMP_DB" node packages/server/scripts/smoke-test-migration.mjs 2>/dev/null || \
    # Method B (one-shot):
    DATABASE_PATH="$TMP_DB" node --input-type=module -e "import('./packages/server/dist/db/index.js').then(m => m.initializeDatabase(process.env.DATABASE_PATH))"
    # Verify migrations table has all 4 prior + 005:
    [ "$(sqlite3 "$TMP_DB" 'SELECT COUNT(*) FROM migrations;')" = "5" ] && \
    sqlite3 "$TMP_DB" "SELECT name FROM migrations WHERE name='005_drop_rewards';" | grep -q "^005_drop_rewards$"
    # Verify zero reward tables exist (fresh DB never created them — Task 2e Region B removed the CREATE TABLE blocks):
    [ "$(sqlite3 "$TMP_DB" '.tables' | tr ' ' '\n' | grep -cE '^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$')" = "0" ]
    # Verify the refund claims table IS present (REFUND preservation — created by bootstrap):
    [ "$(sqlite3 "$TMP_DB" '.tables' | tr ' ' '\n' | grep -c '^claims$')" = "1" ]
    # Cleanup:
    rm -f "$TMP_DB"
    ```

    All four conditions MUST hold:
    - migrations table has exactly 5 rows (m001 through m005)
    - `005_drop_rewards` row present
    - zero reward tables on fresh DB
    - refund `claims` table present

    If `smoke-test-migration.mjs` requires a fixed path (not `DATABASE_PATH` env), fall back to Method B. If neither works, document the failure mode in the SUMMARY and report to user.

    **5e. Build-success implies route deletion is effective.** If `routes/rewards.ts` were still referenced anywhere, server build would have failed in 5b; therefore server boots with no `/api/rewards/*` mount and Express returns 404 for those URLs. Satisfies ROADMAP SC#1's "no `/rewards/*` route is registered" criterion.
  </action>
  <verify>
    <automated>rm -rf apps/dashboard/.next apps/dashboard/.turbo packages/server/dist apps/dashboard/tsconfig.tsbuildinfo packages/server/tsconfig.tsbuildinfo && pnpm --filter @openfacilitator/server build && pnpm --filter @openfacilitator/dashboard build && TMP_DB="/tmp/of-smoke-$$.db" && DATABASE_PATH="$TMP_DB" node --input-type=module -e "import('./packages/server/dist/db/index.js').then(m => m.initializeDatabase(process.env.DATABASE_PATH))" && [ "$(sqlite3 "$TMP_DB" 'SELECT COUNT(*) FROM migrations;')" = "5" ] && sqlite3 "$TMP_DB" "SELECT name FROM migrations WHERE name='005_drop_rewards';" | grep -q "^005_drop_rewards$" && [ "$(sqlite3 "$TMP_DB" '.tables' | tr ' ' '\n' | grep -cE '^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$')" = "0" ] && [ "$(sqlite3 "$TMP_DB" '.tables' | tr ' ' '\n' | grep -c '^claims$')" = "1" ] && rm -f "$TMP_DB" && { [ ! -f packages/server/data/openfacilitator.db ] || ( [ "$(sqlite3 packages/server/data/openfacilitator.db '.tables' | tr ' ' '\n' | grep -cE '^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$')" = "0" ] && [ "$(sqlite3 packages/server/data/openfacilitator.db '.tables' | tr ' ' '\n' | grep -c '^claims$')" = "1" ] ); }</automated>
  </verify>
  <acceptance_criteria>
    - Build caches cleared before build invocation (W7 fix): no stale `tsconfig.tsbuildinfo`, no `.next` cache, no `packages/server/dist` from prior commit.
    - `pnpm --filter @openfacilitator/server build` exits 0.
    - `pnpm --filter @openfacilitator/dashboard build` exits 0.
    - **Fresh-DB smoke (W6 fix — unconditional):** Temp DB created at `/tmp/of-smoke-*.db`, bootstrap runs, migrations table contains 5 rows including `005_drop_rewards`, zero reward tables exist, refund `claims` table IS present. Temp DB cleaned up.
    - **Existing-DB smoke (only if dev DB present):** After migration runs on existing dev DB, all 5 reward tables are gone; migration is recorded exactly once; idempotent on second run; refund `claims` table preserved.
    - **REFUND PRESERVATION:** Both fresh and existing DB scenarios verify the refund `claims` table is present.
    - Fresh-DB bootstrap-source sanity: `grep -cE 'CREATE TABLE IF NOT EXISTS (reward_addresses|campaigns|reward_claims|volume_snapshots|campaign_audit)' packages/server/src/db/index.ts` returns `0` (bootstrap will not recreate reward tables — verified at file level + at runtime in 5d-ii).
    - Express default 404 for deleted routes confirmed by build success: server boots with no `/api/rewards/*` mount. Satisfies ROADMAP SC#1.
  </acceptance_criteria>
  <done>Both builds green (with cleared cache per W7); fresh DB smoke verified unconditionally per W6 (5 migrations applied, zero reward tables, refund claims present); existing dev DB smoke run if present; migration idempotent. Satisfies ROADMAP SC#1, #2 (both fresh + existing halves), #3 (via D-08 deletion), #4, #5, #6, #8.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| client → `/api/rewards/*` (routes/rewards.ts) | Authenticated (`requireAuth`) and admin (`requireAdmin`) requests crossed this boundary; rewards enrollment, claim, and admin campaign CRUD lived here. All handlers deleted. |
| client → `/api/admin/*` (routes/admin.ts) | Authenticated facilitator-owner admin requests; the `ensureFacilitatorMarker` call at line 549 wrote to `reward_addresses` (rewards integration). Call site removed. |
| client → `/api/internal/webhooks` (routes/internal-webhooks.ts) | Internal subscription webhook; same `ensureFacilitatorMarker` integration removed. |
| migration runner → SQLite dev DB | Local DDL execution at server startup; 005 runs `DROP TABLE IF EXISTS` against 5 reward tables. Destructive but intentional + irreversible by design. |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-23-01 | Elevation of Privilege | `routes/admin.ts` after `ensureFacilitatorMarker` import + call removal; `middleware/auth.ts` after `requireAdmin` removal | mitigate | Task 2's grep gate (`grep -cE 'ensureFacilitatorMarker' packages/server/src/routes/admin.ts` returns `0`; `grep -ciE 'requireAdmin\|isAdmin' packages/server/src/middleware/auth.ts` returns `0`) plus Task 5's `pnpm build` confirms no dangling consumers of either symbol. Per CONTEXT.md D-07: only `routes/rewards.ts` consumed `requireAdmin`, and that route is whole-file deleted. No other surviving handler depends on the `isAdmin` predicate (verified by `grep -r 'isAdmin\|requireAdmin' packages/server/src/` returning zero matches post-Task 2b). |
| T-23-02 | Information Disclosure | `/api/rewards/*`, `/rewards/*`, `/rewards/admin/*` deletions (server + dashboard) | mitigate | Per D-05/D-06, no stub handlers replace the deleted routes. Express default 404 (server) and Next.js default 404 (dashboard) leak no rewards metadata, no enrollment state, no campaign info. Deletion is verified by Task 4's full-repo grep gate (no `'/api/rewards'` or `href="/rewards"` strings remain in source). The previously-accessible `rewards-banner-dismissed` localStorage key from `navbar.tsx` is left orphaned in users' browsers — accepted (cosmetic only; no data leak). |
| T-23-03 | Tampering | Migration 005 on production-like DB | mitigate | `DROP TABLE IF EXISTS` is irreversible-by-design — rewards program abandoned 2026-05-12 per CONTEXT.md decisions log; no data is preserved by design. The migration runs inside a `BEGIN TRANSACTION` per `migrations/index.ts` line 57, so a partial failure rolls back. Smoke-tested on dev DB in Task 5d-i AND on a fresh temp DB in Task 5d-ii (W6 fix — unconditional fresh-DB verification); idempotency re-verified by running twice. The 5-table drop order (volume_snapshots → reward_claims → campaign_audit → campaigns → reward_addresses) was hand-verified against FK declarations: children of `campaigns` drop first, child of `reward_addresses` drops first, parents drop last. |
| T-23-04 | Denial of Service | Fresh DB bootstrap after reward table removal | accept | Removing the bootstrap `CREATE TABLE` blocks for the 5 reward tables could in theory crash a legacy client expecting them. Accepted: no such client exists — rewards UI is whole-file deleted in the same atomic commit (Task 3), the rewards SDK surface (lib/api.ts methods + lib/evm + lib/solana signing helpers) is also deleted (Task 3b + 3c), and external consumers of the rewards HTTP API are out of scope (no public SDK or API consumer ever exposed rewards endpoints outside the OpenFacilitator dashboard itself). Verified by Task 4's full-repo grep gate. |
| T-23-05 | Repudiation | Audit log entries referencing reward campaigns | accept | The `campaign_audit` table was the rewards-specific audit log — it's being dropped (D-01). No surviving audit subsystem references rewards (verified absence: Task 2 + Task 4 grep gates). The subscription/billing audit trail is in a separate table (`subscription_payments`) and is untouched. |
| T-23-06 | Spoofing | Replacement of `auth-provider.tsx` (3i) regresses auth surface | mitigate | The replacement narrows the auth context (removes `isAdmin / isEnrolled / isFacilitatorOwner / hasClaimable / refetchRewardsStatus`) but does NOT change session validation: `useSession()`, `signOut`, `isPending` from `@/lib/auth-client` remain the source of truth. Better Auth handles session validation; this provider only forwards the result. Task 5c's `pnpm build` exit 0 confirms every consumer of the removed properties has been updated (navbar `isAdmin`, user-menu `hasClaimable`, wallet-dropdown `hasClaimable`, dashboard `isEnrolled`). Phase 24 (Security Audit) will revisit the full auth surface; pre-emptive narrowing here shrinks that surface. |
| T-23-07 | Tampering | Solana + EVM rewards signature verification (`utils/solana-verify.ts`, `utils/evm-verify.ts`, `apps/dashboard/src/lib/evm/verification.ts`, `apps/dashboard/src/lib/solana/verification.ts`) deletion | accept | These four files implemented the rewards enrollment signature flow: server-side Ed25519 (Solana) + EIP-191 (EVM) verification, and client-side message-construction + signing helpers that all wrapped the rewards-only `"OpenFacilitator Rewards\n\nSign to verify ownership of: ..."` message. After rewards is gone they have no caller (verified pre-plan: server-side, only `routes/rewards.ts` imported them; client-side, only `enrollment-modal.tsx` imported them — all four consumers are deleted in this same commit). The signature primitives (`@noble/curves/ed25519.js`, `viem`'s `verifyMessage`) remain available for any future signing work. Phase 24 (SEC-03) will audit any remaining Solana co-signing surface — payment co-signing lives in `services/x402-client.ts` and is unrelated. |
</threat_model>

<verification>
Phase-level checks (run after all 5 tasks complete, before commit):

1. **Grep gate (ROADMAP SC#1, REWARDS-01/03/04/05/07) — B3 fix: exemption regex covers both new and legacy migration files:**
   ```bash
   grep -riE 'reward|campaign|\$OPEN|REWARDS_WALLET|OPEN_TOKEN_MINT' packages/ apps/ skills/openfacilitator/ --include='*.ts' --include='*.tsx' --include='*.md' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.next --exclude-dir=.turbo | grep -vE '005_drop_rewards|002_campaign_audit_table|migrations/index\.ts:.*(005_drop_rewards|m005|002_campaign_audit_table|m002)' | wc -l
   ```
   Expected: `0`.

2. **Server build (ROADMAP SC#1, SC#3, SC#4) — with cleared cache per W7:**
   ```bash
   rm -rf packages/server/dist packages/server/tsconfig.tsbuildinfo
   pnpm --filter @openfacilitator/server build
   ```
   Expected: exit 0.

3. **Dashboard build (ROADMAP SC#6, SC#7) — with cleared cache per W7:**
   ```bash
   rm -rf apps/dashboard/.next apps/dashboard/.turbo apps/dashboard/tsconfig.tsbuildinfo
   pnpm --filter @openfacilitator/dashboard build
   ```
   Expected: exit 0.

4. **Migration drop verified on existing dev DB (ROADMAP SC#2, existing-DB half):**
   ```bash
   sqlite3 packages/server/data/openfacilitator.db ".tables" | tr ' ' '\n' | grep -cE "^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$"
   ```
   Expected: `0` (after migration runs).

5. **Migration recorded:**
   ```bash
   sqlite3 packages/server/data/openfacilitator.db "SELECT name FROM migrations WHERE name='005_drop_rewards';"
   ```
   Expected: one row.

6. **Fresh-DB sanity (ROADMAP SC#2 fresh-DB half — W6 fix, runtime-verified):**
   Per Task 5d-ii: create a temp DB, run bootstrap, assert exactly 5 migrations recorded, zero reward tables, refund `claims` table present.
   ```bash
   TMP_DB="/tmp/of-verify-$$.db"
   DATABASE_PATH="$TMP_DB" node --input-type=module -e "import('./packages/server/dist/db/index.js').then(m => m.initializeDatabase(process.env.DATABASE_PATH))"
   [ "$(sqlite3 "$TMP_DB" 'SELECT COUNT(*) FROM migrations;')" = "5" ]
   [ "$(sqlite3 "$TMP_DB" '.tables' | tr ' ' '\n' | grep -cE '^(reward_addresses|reward_claims|campaigns|campaign_audit|volume_snapshots)$')" = "0" ]
   [ "$(sqlite3 "$TMP_DB" '.tables' | tr ' ' '\n' | grep -c '^claims$')" = "1" ]
   rm -f "$TMP_DB"
   ```
   Expected: all conditions true.

   Plus file-level confirmation that the bootstrap source no longer creates reward tables:
   ```bash
   grep -cE "CREATE TABLE IF NOT EXISTS (reward_addresses|campaigns|reward_claims|volume_snapshots|campaign_audit)" packages/server/src/db/index.ts
   ```
   Expected: `0`.

7. **db/index.ts rewards re-exports removed:**
   ```bash
   grep -cE "from '\./(reward-addresses|reward-claims|campaigns|campaign-audit|volume-snapshots|volume-aggregation)" packages/server/src/db/index.ts
   ```
   Expected: `0`.

8. **db/types.ts rewards interfaces removed:**
   ```bash
   grep -cE "RewardAddressRecord|CampaignRecord|CampaignAuditRecord|RewardClaimRecord|VolumeSnapshotRecord" packages/server/src/db/types.ts
   ```
   Expected: `0`.

9. **Server.ts rewardsRouter mount removed:**
   ```bash
   grep -cE "rewardsRouter|/api/rewards" packages/server/src/server.ts
   ```
   Expected: `0`.

10. **requireAdmin removed (REWARDS-06 / D-07):**
    ```bash
    grep -cE "requireAdmin|isAdmin" packages/server/src/middleware/auth.ts
    ```
    Expected: `0`.

11. **`.env.example` Rewards Program section gone (REWARDS-08 / D-12):**
    ```bash
    grep -cE "ADMIN_USER_IDS|Rewards Program|REWARDS_WALLET|OPEN_TOKEN_MINT" packages/server/.env.example
    ```
    Expected: `0`.

12. **Volume-aggregation file gone (ROADMAP SC#3, REWARDS-05 / D-08):**
    ```bash
    test ! -f packages/server/src/db/volume-aggregation.ts && grep -rcE "FACILITATOR_MULTIPLIER|multiplier_facilitator" packages/server/src/ 2>/dev/null | grep -v ':0' | wc -l
    ```
    First half: file deleted. Second half: `0` (no surviving references to the rewards multiplier).

13. **`Tab` union no longer has `'rewards'` (ROADMAP SC#6 / REWARDS-03):**
    ```bash
    grep -c "'rewards'" apps/dashboard/src/app/dashboard/\[id\]/page.tsx
    ```
    Expected: `0`.

14. **Landing page rewards bullets gone (ROADMAP SC#7 / REWARDS-04 / D-09) — B5 fix uses JSX-anchored deletion:**
    ```bash
    grep -c '\$OPEN' apps/dashboard/src/app/page.tsx
    grep -c 'href="/rewards"' apps/dashboard/src/app/page.tsx
    ```
    Both expected: `0`.

15. **PROJECT.md Core Value matches D-10 (ROADMAP SC#10 / REWARDS-10):**
    ```bash
    grep -cF "A multi-tenant x402 payment facilitator with multi-chain subscription management. Run a free shared facilitator at pay.openfacilitator.io" .planning/PROJECT.md
    ```
    Expected: `1`.

16. **REQUIREMENTS.md REWARDS-02 table list correct (D-04):**
    ```bash
    grep -q "reward_claims.*campaigns.*campaign_audit.*volume_snapshots" .planning/REQUIREMENTS.md && echo OK
    ```
    Expected: `OK`.

17. **SDK skill ref has zero rewards content (REWARDS-09 / D-11) — B4 fix anchors on actual heading:**
    ```bash
    grep -ciE 'reward|campaign|\$OPEN' skills/openfacilitator/references/sdk-api.md
    ```
    Expected: `0`. Refund `## Claims Functions` section preserved (the actual H2 heading in the file, line 50):
    ```bash
    grep -c "^## Claims Functions$" skills/openfacilitator/references/sdk-api.md
    ```
    Expected: `1`.

18. **REFUND PRESERVATION (load-bearing — checked across the full preserve list) — B4 fix references the actual heading:**
    ```bash
    test -f packages/server/src/db/claims.ts \
      && test -f packages/server/src/services/claims.ts \
      && test -f apps/dashboard/src/components/resource-owner/claims-list.tsx \
      && [ "$(grep -c 'CREATE TABLE IF NOT EXISTS claims' packages/server/src/db/index.ts)" = "1" ] \
      && [ "$(grep -c "from './claims.js'" packages/server/src/db/index.ts)" = "1" ] \
      && [ "$(grep -c '^## Claims Functions$' skills/openfacilitator/references/sdk-api.md)" = "1" ]
    ```
    Expected: all `test -f` succeed; all three `grep -c` return `1`.

19. **B1/B2 reward-signing helpers deleted (both server-side and dashboard-side):**
    ```bash
    test ! -f packages/server/src/utils/evm-verify.ts \
      && test ! -f packages/server/src/utils/solana-verify.ts \
      && test ! -f apps/dashboard/src/lib/evm/verification.ts \
      && test ! -f apps/dashboard/src/lib/solana/verification.ts
    ```
    Expected: all true.
</verification>

<success_criteria>
All ten ROADMAP.md Phase 23 success criteria satisfied:

1. **No `/rewards/*` route registered; 404 on former endpoints** — `routes/rewards.ts` deleted; `server.ts` mount deleted; Express returns default 404. Dashboard `/rewards` and `/rewards/admin` pages deleted; Next.js returns default 404. Verified by build success (mount-line removal would fail if any consumer survived) and by full-repo grep.
2. **Migration drops 5 reward tables cleanly on fresh + existing DBs** — `005_drop_rewards.ts` drops in FK-safe order with `DROP TABLE IF EXISTS`; smoke-tested in Task 5d-i (existing dev DB if present) AND Task 5d-ii (fresh temp DB unconditionally — W6 fix); bootstrap CREATE TABLE blocks deleted from `db/index.ts` so fresh DBs never create them.
3. **Volume calculation identical for facilitator owners vs. regular users** — `db/volume-aggregation.ts` deleted entirely (D-08). The 2x `multiplier_facilitator` branch lived only in that file. `routes/stats.ts` computes its own volume independently and is unchanged. No `multiplier_facilitator` or `FACILITATOR_MULTIPLIER` reference remains.
4. **Admin campaign CRUD + audit log removed; admin surface compiles** — `routes/rewards.ts` (server CRUD), `app/rewards/admin/page.tsx` (dashboard CRUD UI), `components/campaigns/` (campaign forms), `campaign_audit` table (audit log) all deleted. `middleware/auth.ts requireAdmin` and `utils/admin.ts` also deleted per D-07. Server build succeeds.
5. **SPL transfer + reward signature verification removed (both server + dashboard sides)** — `services/reward-transfer.ts`, `services/reward-claims.ts`, `utils/solana-verify.ts`, `utils/evm-verify.ts` (B1) all deleted server-side; `apps/dashboard/src/lib/evm/verification.ts`, `apps/dashboard/src/lib/solana/verification.ts` (B2) all deleted dashboard-side. No surviving references (verified by grep).
6. **Dashboard renders without rewards UI** — `components/rewards/` (15 files), `components/campaigns/` (4 files), `facilitator-rewards-section.tsx`, `rewards-info-banner.tsx`, `lib/evm/verification.ts`, `lib/solana/verification.ts` all deleted. `Tab` union no longer contains `'rewards'`. Dashboard build succeeds.
7. **Landing page no longer mentions $OPEN, claims, or rewards** — two `<li>` bullets deleted per D-09 using JSX-anchored deletion (B5). `grep -c '\$OPEN' apps/dashboard/src/app/page.tsx` returns `0` AND `grep -c 'href="/rewards"' apps/dashboard/src/app/page.tsx` returns `0`.
8. **REWARDS_WALLET_PRIVATE_KEY + OPEN_TOKEN_MINT gone; server starts without them** — `services/reward-transfer.ts` was sole consumer (deleted). `.env.example` Rewards Program section deleted per D-07/D-12 (took `ADMIN_USER_IDS` too). Server build succeeds; no consumer surfaces.
9. **SDK skill ref has no rewards content** — `skills/openfacilitator/references/sdk-api.md` audited per D-11; rewards content (if any) deleted; refund `## Claims Functions` section (actual heading per B4) preserved unchanged.
10. **PROJECT.md Core Value rewritten per D-10** — `## Core Value` contains the D-10 locked exact text verbatim. `## What This Is` no longer mentions rewards. Validated requirements list has rewards entries removed.

Plus all 15 locked decisions honored:
- **D-01:** Migration `005_drop_rewards.ts` drops in FK-safe order: volume_snapshots → reward_claims → campaign_audit → campaigns → reward_addresses. `IF EXISTS` on every statement.
- **D-02:** Bootstrap CREATE TABLE blocks for all 5 reward tables removed from `db/index.ts` Region B.
- **D-03:** `claims` table (REFUND) preserved untouched — never in the drop list, `CREATE TABLE IF NOT EXISTS claims` bootstrap block preserved, `db/claims.ts` re-export preserved.
- **D-04:** `.planning/REQUIREMENTS.md` REWARDS-02 line corrected to list `reward_addresses, reward_claims, campaigns, campaign_audit, volume_snapshots` (not `claims`).
- **D-05:** No 410/redirect/stub for `/rewards/*` server routes — `routes/rewards.ts` whole-file deleted.
- **D-06:** No redirect for `/rewards` or `/rewards/admin` dashboard routes — both page.tsx files deleted.
- **D-07:** `utils/admin.ts`, `requireAdmin` middleware export, `ADMIN_USER_IDS` env var section all removed.
- **D-08:** `db/volume-aggregation.ts` (672 lines) whole-file deleted; satisfies ROADMAP SC#3 trivially because the 2x branch lived only there.
- **D-09:** Two landing-page `<li>` bullets deleted outright via JSX-anchored deletion per B5 — no substitute.
- **D-10:** `## Core Value` of `.planning/PROJECT.md` matches the locked text verbatim.
- **D-11:** `skills/openfacilitator/references/sdk-api.md` audited; refund `## Claims Functions` section (actual heading, per B4) preserved unchanged.
- **D-12:** `REWARDS_WALLET_PRIVATE_KEY` and `OPEN_TOKEN_MINT` no longer have any consumer; `.env.example` Rewards Program section stripped.
- **D-13:** All 12 whole-file server deletions (incl. `evm-verify.ts` per B1) + 6 whole-file/directory dashboard deletions (incl. `lib/evm/verification.ts` and `lib/solana/verification.ts` per B2) executed.
- **D-14:** All partial edits applied per the file inventory.
- **D-15:** Single atomic plan (`23-01-PLAN.md`) — split into 5 tasks but landed in one commit. Build is green at the final commit. Atomic = clean revert.
</success_criteria>

<output>
After completion, create `.planning/phases/23-rewards-removal-backend-frontend-docs/23-01-SUMMARY.md` summarizing:
- Files deleted (count, paths) — should be ~23 whole-file/directory deletions (12 server incl. evm-verify + 6 dashboard incl. 2 lib helpers + 2 page routes + 3 directory empties)
- Lines removed (approx total, per file) — target ~5,000+ LOC
- Migration added and registered (005_drop_rewards.ts + index.ts entry)
- Files partially edited (count, paths) — 20+ files across server + dashboard + planning docs
- The exact D-10 locked text confirmed inserted into `## Core Value` (paste the substring grep result)
- The REQUIREMENTS.md REWARDS-02 line before/after for D-04 verification
- Build verification results (server + dashboard exit codes; build cache cleared first per W7; pino-pretty or other unrelated warnings noted)
- Fresh-DB smoke test result (W6 — temp DB at /tmp/of-smoke-*, 5 migrations applied, zero reward tables, refund claims preserved)
- Existing-DB smoke test method chosen (Phase 22's runner / dev-server-then-kill / node one-liner) and result (or NO_DEV_DB note)
- Idempotency check result (second migration run was a no-op)
- Refund preservation confirmation (all 6 refund-preserve grep/test results from verification check #18 above — note the heading is `## Claims Functions` per B4, not `## Claims / Refunds`)
- Any consumer of `useAuth()` that needed updating beyond the planned list (should be zero — but list any found in case of plan drift)
- Any deviations from line-precise deletion boundaries and why (expected: minor adjustments for line-number drift if the file was edited in a prior task — that's fine, anchors are the authoritative guide; in particular Task 3f uses JSX-anchored deletion not line-anchored per B5)
- Confirmation that `skills/openfacilitator/references/sdk-api.md` either (a) had no rewards content to remove (D-11 trivial pass) or (b) had specific rewards content deleted (list what was removed). Note: the preserved heading is `## Claims Functions` (line 50), not `## Claims / Refunds`.
</output>
</content>
</invoke>
