|
| 1 | +# Coldflow — Take-Home Assignment |
| 2 | + |
| 3 | +Welcome, and thanks for taking the time. This exercise mirrors the real day-to-day |
| 4 | +of working on Coldflow: clone the repo, pick up open GitHub issues, ship a PR. |
| 5 | + |
| 6 | +## What you're building |
| 7 | + |
| 8 | +Coldflow is an open-source cold email tool (Next.js 15 + Payload CMS 3 + Postgres). |
| 9 | +You'll resolve **two open issues** in a single pull request against `main`: |
| 10 | + |
| 11 | +1. **[#4 — actually be able to send emails](https://github.com/pypesdev/coldflow/issues/4)** |
| 12 | + > Right now the Google OAuth config is too confusing to get it wired up where it works for sending emails. |
| 13 | +
|
| 14 | +2. **[#5 — CF-1: Eazy — be able to upload a contact list](https://github.com/pypesdev/coldflow/issues/5)** |
| 15 | + > The list of leads, that we are sequencing outreach to, should have the ability to upload a CSV. |
| 16 | +
|
| 17 | +## Time budget |
| 18 | + |
| 19 | +Aim for **~4–6 hours**. If you hit the ceiling, stop and document what's left in |
| 20 | +the PR description — we'd rather see a clean, honest cut-off than a rushed |
| 21 | +finish. |
| 22 | + |
| 23 | +## Setup |
| 24 | + |
| 25 | +```bash |
| 26 | +git clone https://github.com/pypesdev/coldflow.git |
| 27 | +cd coldflow |
| 28 | +cp .env.example .env # fill in the values you need (see issue #4) |
| 29 | +pnpm i |
| 30 | +docker compose up -d db |
| 31 | +pnpm dev |
| 32 | +``` |
| 33 | + |
| 34 | +Open `http://localhost:3000`. Prereqs: **pnpm**, **Docker + Docker CLI**, **Node 20+**. |
| 35 | + |
| 36 | +CI runs on every PR (`.github/workflows/ci.yml`): |
| 37 | +- `pnpm exec tsc --noEmit` |
| 38 | +- `pnpm lint` |
| 39 | +- `pnpm test:int` |
| 40 | + |
| 41 | +Your PR must be green. |
| 42 | + |
| 43 | +## Scope |
| 44 | + |
| 45 | +### Issue #4 — Google OAuth → sending works end-to-end |
| 46 | + |
| 47 | +A new contributor should be able to clone the repo, follow the README, connect |
| 48 | +their Google account, and successfully send a test email from a campaign. |
| 49 | + |
| 50 | +Expectations: |
| 51 | +- Document the **exact** Google Cloud setup (OAuth client type, redirect URIs, |
| 52 | + scopes, consent screen state) in the README or a `docs/` page. Screenshots |
| 53 | + welcome. |
| 54 | +- Make the required `.env` keys obvious in `.env.example` with comments on |
| 55 | + where each value comes from. |
| 56 | +- Fix any code-level friction you hit (bad error messages, missing scopes, |
| 57 | + broken redirect handling, token refresh, etc.). Root-cause it — don't paper |
| 58 | + over it. |
| 59 | +- Verify the send path actually works against a real Gmail account before |
| 60 | + declaring done. |
| 61 | + |
| 62 | +### Issue #5 — CSV upload for contact lists |
| 63 | + |
| 64 | +Users should be able to upload a CSV of leads and have those leads attached to |
| 65 | +a campaign for sequencing. |
| 66 | + |
| 67 | +Expectations: |
| 68 | +- A clear UI entry point on the leads/contacts surface (wherever it belongs |
| 69 | + given existing patterns). |
| 70 | +- Reasonable column mapping: at minimum `email`, `first_name`, `last_name`. |
| 71 | + Bonus for arbitrary columns flowing through to template variables. |
| 72 | +- Validate rows: skip/report invalid emails, dedupe within the file and against |
| 73 | + existing contacts. |
| 74 | +- Give the user feedback: how many imported, how many skipped, and why. |
| 75 | +- Handle a 1k-row file without falling over. You don't need to design for 1M. |
| 76 | + |
| 77 | +## Ground rules |
| 78 | + |
| 79 | +- **One PR**, targeting `main`. Title: `take-home: <your name>`. |
| 80 | +- Write code in the style of the existing codebase. Don't restructure things |
| 81 | + that aren't in your way. |
| 82 | +- Tests where they add real signal (parsing logic, validation, OAuth token |
| 83 | + handling). Don't pad coverage. |
| 84 | +- Keep commits coherent — we read history. |
| 85 | +- If you make a non-obvious tradeoff, call it out in the PR description. |
| 86 | + |
| 87 | +## What we evaluate |
| 88 | + |
| 89 | +| Area | What good looks like | |
| 90 | +|---|---| |
| 91 | +| **Problem framing** | You understood what the issue actually meant, not just the literal words | |
| 92 | +| **Code quality** | Reads cleanly, fits the codebase, no dead code or speculative abstractions | |
| 93 | +| **Correctness** | The send works. The CSV import works. Edge cases considered | |
| 94 | +| **Developer experience** | A teammate could pick up your changes tomorrow without asking you questions | |
| 95 | +| **Communication** | PR description tells us what, why, and what you'd do next | |
| 96 | + |
| 97 | +## Submitting |
| 98 | + |
| 99 | +1. Push your branch to a fork. |
| 100 | +2. Open a PR against `pypesdev/coldflow:main`. |
| 101 | +3. In the PR description include: |
| 102 | + - A short summary per issue |
| 103 | + - How you tested (the actual steps, including a screenshot or short clip of |
| 104 | + a send going through and a CSV import succeeding) |
| 105 | + - Anything you'd do with another day |
| 106 | +4. Reply to the email thread with the PR link. |
| 107 | + |
| 108 | +Questions during the exercise are fine — ask. We'd rather unblock you than |
| 109 | +watch you guess. |
| 110 | + |
| 111 | +Good luck. |
0 commit comments