Code Findings While Writing the Course
Version: 1.0 | Status: Found by reading code on 2026-09-16, not fixed | Owner: Engineering
Issues found while writing the 7-day lessons. None are patched. "Confirmed" means re-read in code by a second pass; everything else is from one reading and needs a check before acting.
Money & orders
| Finding | Where | Status |
|---|---|---|
With DELIVERY_OTP_ENFORCE=false (prod default), a rider can only mark an order delivered by typing the emergency OTP; orders with an OTP row are otherwise blocked | internal/order/services/delivery.go:431 | Confirmed |
PO RecordPayment has no idempotency key; a retried partial payment records twice | internal/procurement | Read once |
Voiding a payout does not free the week: ExistsForPeriod counts voided rows, so Generate skips it | internal/finance | Read once |
| Mid-week fee change: old and new fee both match, which one pays is not deterministic | internal/finance | Read once |
| Scheduler payouts are created by the system actor, so one super-admin can move them pending to paid alone | internal/finance | Read once |
Confirming a PO never moves hospital requests to procured | internal/procurement | Read once |
Webhook dedupe uses SHA-256 of body; X-Razorpay-Event-Id is read then ignored | internal/order | Read once |
| Reconciler "already ran" marker is per-process memory; runs once per task when scaled | internal/order | Read once |
| Only checkout, walk-in and generic status change publish realtime events | internal/order, internal/realtime | Read once |
Security & privacy
| Finding | Where | Status |
|---|---|---|
Audit row takes the first X-Forwarded-For value (caller-controlled); rate limiter takes the last | internal/middleware/audit.go | Read once |
prescription_reviews.patient_name stored in plaintext, not covered by migration 253 | internal/prescription | Read once |
Schedule check covers h/h1 only, not x/narcotic; no prescription date check | internal/prescription | Read once — ask legal-compliance |
With KYC_ENCRYPTION_KEY unset, employee Aadhaar/bank numbers silently not stored; main.go warning is stale | internal/hr, cmd/server/main.go | Read once |
No erasure/retention path for rider KYC or HR documents; deletion_requested_at only records the request | internal/deliverykyc, internal/hr | Read once |
.env.example calls legacy R2_BUCKET_NAME bucket world-readable | .env.example | Read once |
Leads UpdateStatus skips the live super-admin check that list/export do | internal/leads | Read once |
Docs & tooling drift
| Finding | Where |
|---|---|
docker-compose.yml API cannot boot: default JWT_SECRET, no patient encryption keys, no Redis service | docker-compose.yml |
ops/pending-migrations/index.md says move parked files back and deploy; with prod at 295, lower numbers silently never apply | ops/pending-migrations/index.md |
CLAUDE.md and skills/*.md describe gin, godotenv, RequireRole, date-named migrations — code uses ServeMux, viper, DB-backed permissions, numbered migrations | backend CLAUDE.md, skills/ |
payment_anomaly metric filter counts every error log, not payment errors | deploy/aws/monitoring.tf |
Swagger annotations exist only in internal/dashboard/handlers | internal/*/handlers |
Offer expired and review patient_responded/expired states are allowed but never set | internal/hiring, internal/prescription |
Calendar OAuth comment in main.go and HIRING_CALENDAR_ENCRYPTION_KEY in .env.example are leftovers from before migration 268 | cmd/server/main.go, .env.example |