This repository is moving from planning into implementation.
- FastAPI project scaffold
- Environment config loading via
pydantic-settings - SQLAlchemy DB session wiring
- Alembic migration framework setup
seed_accountsmodel and initial migration- Seed API endpoints:
POST /seed-accountsGET /seed-accountsPATCH /seed-accounts/{id}
- Campaign API endpoints:
POST /campaignsGET /campaignsPOST /campaigns/{id}/checks/runGET /campaigns/{id}/results
- Dashboard API endpoints:
GET /dashboard/summary?from_ts=&to_ts=GET /dashboard/esp-breakdown?campaign_id=
- Alerts endpoint:
GET /alerts
- Validation via Pydantic (
EmailStr, enum constraints) - Deterministic check-run simulation for local development
- Alert generation for low inbox and high missing rates
seed_accountscampaignsdelivery_resultsalert_events
- Install dependencies
- Copy
.env.exampleto.envand updateDATABASE_URL - Run migrations
- Start API
Example commands:
pip install -e .
alembic upgrade head
uvicorn app.main:app --reload- A campaign can be created and listed.
- Triggering checks creates queue jobs against seed accounts.
- Worker writes normalized
delivery_resultsrecords linked to a campaign and seed. - Duplicate check-trigger requests are safely ignored or merged according to idempotency key.
Expose a minimal read model/UI surface for immediate campaign and ESP health visibility.
- Summary query/service for global placement rates in a date range
- ESP breakdown query/service (inbox %, spam %, missing %, total seeds checked)
-
GET /dashboard/summary?from=&to=endpoint -
GET /dashboard/esp-breakdown?campaign_id=&from=&to=endpoint - Basic frontend view (or API-only placeholder if UI stack not initialized)
- Empty/error/loading states defined
- CSV export of current view (optional if time allows)
inbox_rate = inbox_count / total_checkedspam_rate = spam_count / total_checkedmissing_rate = missing_count / total_checkedtotal_checkedexcludes seeds not yet in terminal state for active runs
- Dashboard endpoints return accurate aggregated values for a known fixture dataset.
- At least one campaign and one date-range summary can be inspected end to end.
- Results are grouped by ESP consistently using canonical ESP values.
placement:inbox | spam | promotions | missingseed_status:active | paused | auth_expired | errorauth_method:oauth2 | imap_secretsource_provider:imap | gmail_api | ms_graph
- Scheduled checks at minute offsets:
+1, +3, +5, +10fromsent_at - Transition to
missingonly after final attempt - Exponential backoff with jitter for network/provider errors
- Unique
seed_accounts.email - One terminal result per (
campaign_id,seed_account_id) per run - All writes include
created_atand source metadata for traceability
- Never store plaintext mailbox credentials in DB
- Store only
credential_ref - Log redaction required for secrets and tokens
- PR A — Project skeleton + config
- API service scaffold
- DB migration framework setup
- Environment config loading + validation
- PR B — Part 1 core
seed_accountsmigration + CRUD endpoints + validators
- PR C — Part 2 queue/worker baseline
- Queue bootstrap + worker process + job contract
- PR D — Part 3 campaign/results baseline
campaigns+delivery_resultsmigrations + enqueue endpoint + results endpoint
- PR E — Part 4 dashboard API
- Summary and ESP breakdown endpoints
- End-to-end flow: create campaign → enqueue checks → write terminal results → query dashboard summaries
- Basic reliability: retries verified in test/staging
- Security baseline: credential references only, secrets redacted in logs
We start coding with PR A (project skeleton + config) and then proceed through PR B–E in order.
If you want to start implementation immediately, use this local stack:
- Frontend: Next.js
- API: FastAPI (Python)
- Database: PostgreSQL
- Queue/Cache: Redis
- Worker: Celery (or RQ) worker process
This aligns with the architecture in seed-list-app-plan.md and keeps the API + worker split clear from day one.
Install locally:
- Docker + Docker Compose (recommended for DB/Redis)
- Python 3.11+
- Node.js 20+
- pnpm or npm
seed-list-app/
apps/
web/ # Next.js UI
api/ # FastAPI service
worker/ # Celery worker + polling jobs
infra/
docker-compose.yml # Postgres + Redis
.env.example
Create infra/docker-compose.yml:
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: seed
POSTGRES_PASSWORD: seed
POSTGRES_DB: seedlist
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7
ports:
- "6379:6379"
volumes:
pgdata:Run:
docker compose -f infra/docker-compose.yml up -d# Shared
DATABASE_URL=postgresql://seed:seed@localhost:5432/seedlist
REDIS_URL=redis://localhost:6379/0
# API
API_PORT=8000
SECRET_BACKEND=local
# Worker polling cadence (minutes)
POLL_SCHEDULE_MINUTES=1,3,5,10
# Optional provider credentials
GMAIL_CLIENT_ID=
GMAIL_CLIENT_SECRET=
MS_GRAPH_CLIENT_ID=
MS_GRAPH_CLIENT_SECRET=Inside apps/api:
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn sqlalchemy psycopg[binary] alembic pydantic-settings redisStart API:
uvicorn main:app --reload --host 0.0.0.0 --port 8000Inside apps/worker:
python -m venv .venv
source .venv/bin/activate
pip install celery redis imapclientStart worker:
celery -A worker.app worker --loglevel=INFOInside apps/web:
npx create-next-app@latest . --ts --eslint --app --src-dir
pnpm devSet API base URL in the web app env:
NEXT_PUBLIC_API_BASE_URL=http://localhost:8000-
docker compose ... up -d(Postgres + Redis running) - API responds on
GET /health - Worker connected to Redis and accepting jobs
- Frontend loads and can call API base URL
If your team prefers Node end-to-end, you can substitute:
- API: Express/NestJS
- Worker/Queue: BullMQ + Redis
- ORM/Migrations: Prisma or Drizzle
Keep Postgres + Redis and the same service boundaries.
- Start local Postgres Docker container:
powershell -ExecutionPolicy Bypass -File skills/local-deploy-seed-list-app/scripts/start_postgres_docker.ps1- Run migrations:
& .\.venv\Scripts\Activate.ps1
alembic upgrade head- Start API:
uvicorn app.main:app --host 127.0.0.1 --port 8001- Verify:
GET http://127.0.0.1:8001/health- Create seeds and campaign, run checks, then query:
/campaigns/{id}/results/dashboard/summary/dashboard/esp-breakdown?campaign_id={id}/alerts
Planning is complete through all Phase 1 parts and coding kickoff. See seed-list-app-plan.md for the full roadmap.