Skip to main content

drop-sprint1-implementation-plan

Plan: Drop Sprint 1 Implementation

Date: 2026-02-26 Author: John (AI Director) MC Task: #2110+ Status: PENDING APPROVAL Sprint Duration: 5 weeks (UI prototype with mock integrations)

Research Summary

Existing Codebase (MUCH more than expected)

  • 46 API routes with complete business logic (auth, transactions, GDPR, admin, cards, merchants)
  • 20 UI pages all with real content (zero stubs)
  • 936-line db.ts with dual-driver (SQLite + PostgreSQL) — needs PostgreSQL-only refactor
  • 13 database tables via node-pg-migrate (raw SQL, NOT Drizzle) — need 12 more tables
  • BankID OIDC fully implemented with PSD2 dynamic linking (298-line callback)
  • 12 Figma Make export screens (Vite+React) as UI source of truth
  • docker-compose.production.yml already has PostgreSQL 16
  • 2 test files — needs major expansion
  • NO Drizzle ORM — currently raw SQL via better-sqlite3 + pg
  • NO BullMQ/Redis — not yet implemented
  • SHA-256 national_id_hash — must change to AES-256-GCM
  • Email/password + PIN paths still in code — must remove for BankID-only

Key Insight

This is NOT a greenfield build. It's a refactor to align with architecture decisions + add missing infrastructure + extend with new FRs. The existing code is functional but pre-dates the architecture review.


Objective

Refactor existing Drop codebase to align with all 16 ADRs, add missing database tables and infrastructure (Redis, BullMQ, Drizzle), implement BankID-only auth, and bring UI in line with Figma Make export designs. All external integrations (BankID, ZTL, FX, compliance) remain mocked.


Team Orchestration

Team Members

ID Name Role Agent Type Model
B1 db-builder Database: PostgreSQL-only + Drizzle + 12 new tables builder sonnet
V1 db-validator Validate database migration + schema validator sonnet
B2 auth-builder BankID-only auth refactor + AES-256-GCM builder sonnet
V2 auth-validator Validate auth + security validator sonnet
B3 infra-builder Docker, Redis, BullMQ, graceful shutdown builder sonnet
V3 infra-validator Validate infrastructure validator sonnet
B4 api-builder New API routes for FR-073 through FR-077 builder sonnet
V4 api-validator Validate API routes validator sonnet
B5 ui-builder Align UI with Figma Make + admin portal builder sonnet
V5 ui-validator Validate UI against Figma Make validator sonnet

Step-by-Step Tasks

Phase 1: Foundation (Week 1) — Database + Infrastructure

Task 1: PostgreSQL-only db.ts refactor

  • Owner: B1
  • BlockedBy: none
  • Description: Replace 936-line dual-driver src/lib/db.ts with PostgreSQL-only via Drizzle ORM. Remove better-sqlite3 dependency. Remove all SQLite code paths. Install drizzle-orm + drizzle-kit + @neondatabase/serverless (for edge compat) or postgres driver. Create Drizzle schema matching existing 13 tables.
  • Files: src/lib/db.ts, package.json, drizzle.config.ts
  • Acceptance:
    • better-sqlite3 removed from package.json
    • drizzle-orm and drizzle-kit in dependencies
    • src/lib/db.ts uses Drizzle with pg driver only
    • All existing getOne(), getAll(), run() calls still work (adapter layer or full migration)
    • docker-compose.yml updated with PostgreSQL service for dev
    • drop.db SQLite file removed from project

Task 2: Drizzle schema for all 25 tables

  • Owner: B1
  • BlockedBy: 1
  • Description: Create src/lib/schema.ts with Drizzle table definitions for all 25 tables per database-design.md. Remove node-pg-migrate, use Drizzle Kit for migrations. Include the 12 new tables: aml_alerts, str_reports, screening_results, consents, data_access_requests, complaints, reconciliation_reports, reconciliation_discrepancies, circuit_breaker_state, webhook_events, webhook_dlq, disputes.
  • Files: src/lib/schema.ts, drizzle.config.ts, migrations/ (Drizzle format)
  • Acceptance:
    • 25 Drizzle table definitions matching database-design.md
    • All FKs, indexes, constraints defined
    • national_id_hash column renamed to national_id_encrypted + national_id_hmac
    • password_hash column still exists but default='BANKID_ONLY' (backwards compat)
    • npx drizzle-kit generate produces valid migration
    • npx drizzle-kit push applies to local PostgreSQL without errors

Task 3: Validate database migration

  • Owner: V1
  • BlockedBy: 2
  • Acceptance:
    • All 25 tables created in PostgreSQL
    • Schema matches database-design.md exactly
    • Indexes from indexing-strategy.md present
    • No SQLite references remain in codebase (grep -r "better-sqlite3\|sqlite" src/)
    • Docker PostgreSQL starts and accepts connections

Task 4: Redis + BullMQ infrastructure

  • Owner: B3
  • BlockedBy: none (parallel with Task 1)
  • Description: Add Redis and BullMQ per ADR-015. Create src/lib/redis.ts (connection), src/lib/queues.ts (5 queue definitions), src/lib/workers/ (worker stubs for each queue). Add Redis to docker-compose.yml. Add SIGTERM handler per ADR-016.
  • Files: src/lib/redis.ts, src/lib/queues.ts, src/lib/workers/, docker-compose.yml, src/lib/shutdown-handlers.node.ts
  • Acceptance:
    • bullmq and ioredis in package.json
    • Redis service in docker-compose.yml (port 6379)
    • 5 queues defined: payment-critical, settlement, compliance, reporting, notifications
    • SIGTERM handler with 25s drain, shutdown_interrupted state
    • docker compose up starts both PostgreSQL and Redis
    • Health endpoint includes Redis connectivity check

Task 5: Validate infrastructure

  • Owner: V3
  • BlockedBy: 4
  • Acceptance:
    • docker compose up starts app + PostgreSQL + Redis
    • Health endpoint reports all services healthy
    • BullMQ can enqueue and process a test job
    • SIGTERM handler tested (send SIGTERM, verify graceful shutdown)

Phase 2: Auth + Security (Week 2)

Task 6: BankID-only authentication

  • Owner: B2
  • BlockedBy: 2 (needs Drizzle schema)
  • Description: Remove all email/password and phone/PIN auth paths. Keep BankID OIDC as sole auth. Remove src/app/api/auth/login/, src/app/api/auth/register/, src/app/api/auth/verify-otp/. Update src/app/api/auth/bankid/callback/route.ts to use AES-256-GCM for fødselsnummer (not SHA-256). Update session policy: 30min sliding idle, 8h absolute, 5min payment SCA. Remove bcryptjs dependency (no passwords).
  • Files: src/app/api/auth/, src/lib/auth.ts, src/lib/services/auth-provider.ts, src/app/login/page.tsx, src/app/register/page.tsx
  • Acceptance:
    • /api/auth/login — REMOVED
    • /api/auth/register — REMOVED
    • /api/auth/verify-otp — REMOVED
    • /api/auth/bankid — sole auth entry point
    • BankID callback stores national_id_encrypted (AES-256-GCM) + national_id_hmac (HMAC-SHA256)
    • JWT has 30min expiry, refresh token server-side
    • Login page shows only "Logg inn med BankID" button
    • Registration page redirects to BankID flow
    • bcryptjs removed from package.json
    • Zero references to password, pin, email+password in auth flows

Task 7: Validate auth + security

  • Owner: V2
  • BlockedBy: 6
  • Acceptance:
    • No email/password/PIN auth paths reachable
    • BankID mock flow works end-to-end
    • national_id stored as AES-256-GCM encrypted (verify with DB inspection)
    • Session expiry at 30min verified
    • grep -r "SHA-256\|sha256\|national_id_hash" src/ returns zero results in auth code
    • grep -r "bcrypt\|password_hash" src/ — only legacy column definition, no active auth use

Phase 3: API Routes (Week 2-3)

Task 8: Webhook handling API (FR-076)

  • Owner: B4
  • BlockedBy: 2, 4 (needs Drizzle + BullMQ)
  • Description: Create POST /api/webhooks/banking-partner per FR-076. HMAC-SHA256 validation, IP whitelist, idempotent processing, state machine (pending→processing→completed/failed), DLQ after 3 attempts.
  • Files: src/app/api/webhooks/banking-partner/route.ts, src/lib/services/webhook-processor.ts
  • Acceptance:
    • HMAC-SHA256 signature validation with 5-min timestamp check
    • webhook_events table populated on each webhook
    • Duplicate webhook_id returns existing result (idempotent)
    • Failed webhooks retry 3 times then move to webhook_dlq
    • Returns 200 within 5 seconds

Task 9: Reconciliation API (FR-073)

  • Owner: B4
  • BlockedBy: 2, 4
  • Description: Create reconciliation job and API. BullMQ cron job at 06:00 Oslo time. Compares Drop transactions with mock banking partner data. Creates reconciliation_reports and reconciliation_discrepancies records. Admin endpoint GET /api/admin/reconciliation to view reports.
  • Files: src/lib/workers/reconciliation-worker.ts, src/app/api/admin/reconciliation/route.ts
  • Acceptance:
    • BullMQ cron job scheduled at 06:00 Europe/Oslo
    • Reconciliation report created with matched/discrepancy counts
    • Discrepancies categorized by type
    • Admin API returns reports with pagination

Task 10: Circuit breaker service (FR-075)

  • Owner: B4
  • BlockedBy: 2, 4
  • Description: Create circuit breaker service per FR-075. Shared state in PostgreSQL circuit_breaker_state table. 5 instances (BankID, ZTL, FX, compliance, push). Fallback behaviors per dependency.
  • Files: src/lib/services/circuit-breaker.ts, src/lib/workers/circuit-breaker-monitor.ts
  • Acceptance:
    • 5 circuit breaker instances initialized on startup
    • State transitions: closed→open after 5 failures, open→half-open after 30s
    • Each external service call wrapped in circuit breaker
    • Fallback behavior per dependency (read-only mode, cached rates, etc.)

Task 11: Dispute/refund API (FR-077)

  • Owner: B4
  • BlockedBy: 2
  • Description: Create dispute endpoints per FR-077. POST /api/transactions/:id/dispute for user submission. GET /api/admin/disputes for admin queue. State machine (submitted→acknowledged→investigating→decided→closed).
  • Files: src/app/api/transactions/[id]/dispute/route.ts, src/app/api/admin/disputes/route.ts, src/lib/services/dispute.ts
  • Acceptance:
    • User can submit dispute from transaction detail
    • Auto-acknowledgement (logged, notification created)
    • Admin can view/manage dispute queue
    • State machine enforces valid transitions only
    • 5-year retention with do_not_delete flag

Task 12: Validate API routes

  • Owner: V4
  • BlockedBy: 8, 9, 10, 11
  • Acceptance:
    • All new endpoints respond correctly (mock data)
    • Webhook idempotency verified (send same webhook twice)
    • Circuit breaker state transitions verified
    • Dispute lifecycle flows correctly
    • All endpoints require auth (except webhook which uses HMAC)

Phase 4: UI Alignment (Week 3-4)

Task 13: Align existing screens with Figma Make export

  • Owner: B5
  • BlockedBy: 6 (needs BankID-only auth)
  • Description: Compare each of the 10 existing screens against Figma Make export (mockups/figma-make-export/src/app/screens/). Update layouts, colors, typography, spacing to match. Screens: Login, Onboarding, Dashboard, SendMoney, BankAccounts, TransactionHistory, ScanQR, Profile, Notifications, MerchantDashboard. Plus 2 merchant QR screens.
  • Files: All page.tsx files in src/app/
  • Acceptance:
    • Each screen visually matches Figma Make export
    • Login page: BankID-only (no email/password form)
    • Dashboard: balance card, recent transactions, quick actions
    • SendMoney: multi-step flow matching Make export
    • Brand colors match Drop brand guidelines (green gradient #0B6E35→#064E25)
    • Mobile-first responsive design

Task 14: Admin portal UI (EP-09)

  • Owner: B5
  • BlockedBy: 8, 9, 10, 11 (needs admin APIs)
  • Description: Create admin portal pages per EP-09. MFA login (mock), AML alert dashboard, STR filing, KYC review, transaction search, settlement management, report generation, audit log viewer. Route: /app/admin/.
  • Files: src/app/admin/ (new directory), 8 page files
  • Acceptance:
    • Admin login page with MFA (mocked)
    • AML alert queue with filter/sort/action buttons
    • STR filing form pre-filled from alert data
    • User KYC search and detail view
    • Transaction search with filters and CSV export
    • Settlement dashboard with batch status
    • Compliance report list with PDF/CSV download stubs
    • Audit log viewer with search

Task 15: Validate UI

  • Owner: V5
  • BlockedBy: 13, 14
  • Acceptance:
    • All 10 consumer screens match Figma Make export (visual comparison)
    • Admin portal: all 8 pages render without errors
    • No broken routes (404s)
    • Mobile responsive (viewport 375px)
    • Brand colors consistent
    • No email/password UI anywhere

Phase 5: Integration + Testing (Week 4-5)

Task 16: Mock services for all external integrations

  • Owner: B4
  • BlockedBy: 6, 10 (needs auth + circuit breaker)
  • Description: Create/update mock services for: BankID (already exists), ZTL banking partner (PISP/AISP mock), FX rate provider (mock with realistic NOK→RSD/BAM/PLN/EUR/PKR/TRY rates), compliance provider (mock PEP/sanctions screening), push notifications (mock). All mocks should be realistic and toggleable via feature flags.
  • Files: src/lib/services/mock-*.ts (update existing + create new)
  • Acceptance:
    • BankID mock returns valid OIDC flow
    • PISP mock returns realistic payment confirmation after delay
    • AISP mock returns balance data for linked accounts
    • FX mock returns rates for all 6 corridors
    • Compliance mock returns PEP clear/match based on test data
    • All mocks activated via MOCK_MODE=true env var

Task 17: End-to-end test suite

  • Owner: B4
  • BlockedBy: 16
  • Description: Expand test suite (currently 2 files). Add Playwright E2E tests for critical user flows: BankID login → dashboard → send money → transaction history. Add API integration tests for webhook, reconciliation, dispute. Add unit tests for circuit breaker, idempotency logic.
  • Files: tests/e2e/, tests/integration/, tests/unit/
  • Acceptance:
    • E2E: Login flow → Dashboard renders
    • E2E: Send money flow (mock) completes
    • E2E: Admin portal accessible
    • Integration: Webhook processing with HMAC
    • Integration: Reconciliation job produces report
    • Unit: Circuit breaker state transitions
    • Unit: Idempotency key generation deterministic
    • All tests pass in CI (docker-compose up + test)

Task 18: Final validation

  • Owner: V1 + V2 + V3 + V4 + V5 (joint)
  • BlockedBy: 17
  • Description: Full system validation. Start docker-compose, run all tests, verify all acceptance criteria from all tasks, check for security issues.
  • Acceptance:
    • docker compose up → app healthy within 30s
    • All 25 tables present in PostgreSQL
    • BankID-only auth (no other paths)
    • All new API routes functional
    • All UI screens render correctly
    • Zero SQLite references in codebase
    • Zero SHA-256 national_id references
    • QA-19 check passes (≥17/19 for H priority)

Validation Commands

# Database
docker compose up -d
npx drizzle-kit push
psql -h localhost -U drop -d drop -c "\dt" | wc -l  # Should show 25 tables

# Auth
grep -r "better-sqlite3\|bcrypt\|password.*login\|pin.*login" src/ # Should be zero
grep -r "national_id_hash" src/ # Should be zero
curl http://localhost:3000/api/auth/login # Should 404

# Infrastructure
docker compose ps # PostgreSQL + Redis + app all healthy
curl http://localhost:3000/api/health # Should include redis: ok, db: ok

# Tests
npm test
npx playwright test

# QA Gate
node ~/system/tools/qa-19.js check <task-id>

Risk Mitigation

Risk Mitigation
db.ts refactor breaks all 46 API routes Task 1 creates adapter layer first, then migrates route-by-route
BankID mock breaks existing flow Keep BANKID_MOCK=true env var, test before removing legacy paths
Drizzle migration incompatible with existing data Fresh PostgreSQL for Sprint 1 (no production data yet)
UI alignment takes longer than expected Prioritize 5 core screens (Login, Dashboard, Send, Scan, Transactions), defer rest

Summary

Phase Duration Tasks Builders Validators
1: Foundation Week 1 5 B1, B3 V1, V3
2: Auth + Security Week 2 2 B2 V2
3: API Routes Week 2-3 5 B4 V4
4: UI Alignment Week 3-4 3 B5 V5
5: Integration + Testing Week 4-5 3 B4 All
Total 5 weeks 18 tasks 5 builders 5 validators

Approve plan? Then run /build-plan to execute.