Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9fdbf6e
fix(core): unbreak the outreach loop, then make it fast
Aryanban Oct 21, 2026
b0007ee
perf(core): make the data paths fast at scale
Aryanban Oct 21, 2026
0793dc3
perf(api): batch the remaining per-row queries
Aryanban Oct 21, 2026
a0c1dd1
perf(scrape): batch the scrape-time dedupe, and fix the identity it uses
Aryanban Oct 21, 2026
bf579e9
perf(send): check the suppression list once per batch, not once per c…
Aryanban Oct 21, 2026
9ab4b5e
docs: record the measured performance results in the roadmap
Aryanban Oct 21, 2026
01913be
perf: remove the last per-row queries from the write paths
Aryanban Oct 21, 2026
297fe54
docs: final import numbers
Aryanban Oct 21, 2026
c44b031
feat(ui): add an accessibility gate, and fix how the app is served an…
Aryanban Oct 21, 2026
e5d3a4c
fix(data): stop the demo data from masquerading as real leads, and en…
Aryanban Oct 21, 2026
8589c26
feat(ui): type the is_demo flag on the lead payload
Aryanban Oct 21, 2026
980c254
docs: the measured UX, accessibility and performance audit
Aryanban Oct 21, 2026
c7f3f9a
fix(data): tell demo data apart from real leads, and repair legacy da…
Aryanban Oct 21, 2026
7f62414
docs: document the accessibility gate and the UX audit
Aryanban Oct 21, 2026
a3c8545
feat(api): let the lead list filter out the demo rows
Aryanban Oct 21, 2026
a7bb84d
fix(ui): one level-1 heading per page, readable text floor, spinners …
Aryanban Oct 21, 2026
9cc13c3
feat(ui): move focus to the main landmark after a keyboard navigation
Aryanban Oct 21, 2026
1641e0e
fix(api): stop the DNS probe from freezing the API
Aryanban Oct 21, 2026
3d64b0c
feat(ui): the dashboard's accessibility, layout stability and mobile …
Aryanban Oct 21, 2026
575eaa3
docs: the final measured UX numbers
Aryanban Oct 21, 2026
ca8ed39
fix(send): stop the scheduler dying silently, and stop a fresh instal…
Aryanban Oct 22, 2026
459f995
fix(india): wire the three India features that were built but never c…
Aryanban Oct 22, 2026
8e26c93
fix(sources): repair the HTTP layer that was silently breaking every …
Aryanban Oct 22, 2026
d3d598d
feat(people): a real People table - one row per human, filterable
Aryanban Oct 22, 2026
629a55a
feat(people): the People table in the dashboard, and honest city/role…
Aryanban Oct 22, 2026
9d2c872
feat(people): company size, turnover and technology - so an ICP is ex…
Aryanban Oct 22, 2026
7b9945f
feat(mcp): a People tool, and one query shared by the API and the agent
Aryanban Oct 22, 2026
18ef75f
feat(people): score people against their ICP, and filter by it
Aryanban Oct 22, 2026
ceb1b53
feat(analytics): a funnel that shows where the pipeline leaks
Aryanban Oct 22, 2026
e50582a
feat(icp): an ICP that says what a company looks like, not what it is…
Aryanban Oct 22, 2026
bc4d142
feat(india): the copy kit, where the operator actually writes the email
Aryanban Oct 22, 2026
56e7681
docs: the roadmap reflects what actually shipped
Aryanban Oct 22, 2026
f8733bf
feat(india): GSTIN and Udyam - the identifiers Apollo does not have
Aryanban Oct 22, 2026
1d9c5b0
feat(india): a cold-email linter that understands Indian email
Aryanban Oct 22, 2026
93a0051
feat(queue): the heavy jobs go to Celery when it is enabled
Aryanban Oct 22, 2026
9f51543
feat(india): the send window - no email lands at 03:00 IST
Aryanban Oct 22, 2026
393df90
feat(icp): the structured criteria, in the wizard where they are set
Aryanban Oct 22, 2026
d9a2e87
feat(india): regional-language closers, and a copy kit that stops cra…
Aryanban Oct 22, 2026
93a3fd5
feat(people): select people and put them in a sequence
Aryanban Oct 22, 2026
124f4af
feat(audit): a log of what the product did, and why
Aryanban Oct 22, 2026
81c14e4
docs: the activity log and the regional copy in the README and roadmap
Aryanban Oct 22, 2026
c1f0530
fix: preserve email hygiene and source attribution
Aryanban Oct 22, 2026
c5f4b9d
docs: refresh remaining roadmap work
Aryanban Oct 22, 2026
5865391
feat: calibrate source trust from outcomes
Aryanban Oct 23, 2026
b6132ac
test: isolate content checks from send schedule
Aryanban Oct 23, 2026
de37946
test: seed business target for People filter check
Aryanban Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ CORS_ORIGINS=*
# Public base URL used in open/click tracking and unsubscribe links.
TRACKING_DOMAIN=http://localhost:8000

# --- API authentication (OPTIONAL but strongly recommended) -----------------
# Unset => the REST API is OPEN. That is fine for a local clone-and-run, and
# unsafe the moment the port is reachable from a LAN: with CORS_ORIGINS="*"
# any website could delete your lead database or send cold email from your
# mailbox.
#
# When set, every endpoint requires it:
# curl -H "X-API-Key: <key>" http://localhost:8000/api/v1/leads
# The campaign tracker (/api/v1/campaigns/tracker/*) is exempt - it serves
# the one-click unsubscribe and the click tracker, which are fetched by the
# recipient's mail client.
#
# Generate with: openssl rand -hex 32
API_KEY=

# --- Scraper ----------------------------------------------------------------
# Optional rotating egress proxies for directory scraping, comma-separated.
# Recommended for sustained Google Maps volume (residential/Datacenter IPs
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,106 @@ jobs:
run: |
cd dashboard
npm run build

- name: Type-check
run: |
cd dashboard
npm run typecheck

frontend-a11y:
name: Accessibility & Layout Stability
runs-on: ubuntu-latest
needs: frontend-check
env:
# The dashboard talks to the API through a same-origin Next.js rewrite,
# so the browser only ever calls the dashboard's own origin.
API_PROXY_URL: http://127.0.0.1:8000/api
CI: "true"
steps:
- uses: actions/checkout@v5

- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "24"
cache: "npm"
cache-dependency-path: dashboard/package-lock.json

- name: Install Dashboard Dependencies
run: |
cd dashboard
npm ci

- name: Install Playwright browser
run: |
cd dashboard
npx playwright install --with-deps chromium

- name: Start the API
env:
DATABASE_URL: sqlite+aiosqlite:///./a11y-leadforge.db
USE_CELERY: "false"
PYTHONPATH: backend
run: |
cd backend
python -m pip install --upgrade pip
pip install -r requirements.txt
nohup python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 > /tmp/lf-api.log 2>&1 &
for i in $(seq 1 60); do
if curl -sf http://127.0.0.1:8000/health > /dev/null; then echo "API up"; exit 0; fi
sleep 2
done
echo "API did not start"; cat /tmp/lf-api.log; exit 1

- name: Run the accessibility suite
run: |
cd dashboard
npx playwright test e2e/a11y.spec.ts --reporter=list

frontend-e2e:
name: End-to-End Suite
runs-on: ubuntu-latest
needs: frontend-check
env:
API_PROXY_URL: http://127.0.0.1:8000/api
CI: "true"
steps:
- uses: actions/checkout@v5

- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "24"
cache: "npm"
cache-dependency-path: dashboard/package-lock.json

- name: Install Dashboard Dependencies
run: |
cd dashboard
npm ci

- name: Install Playwright browser
run: |
cd dashboard
npx playwright install --with-deps chromium

- name: Start the API
env:
DATABASE_URL: sqlite+aiosqlite:///./e2e-leadforge.db
USE_CELERY: "false"
PYTHONPATH: backend
run: |
cd backend
python -m pip install --upgrade pip
pip install -r requirements.txt
nohup python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 > /tmp/lf-api.log 2>&1 &
for i in $(seq 1 60); do
if curl -sf http://127.0.0.1:8000/health > /dev/null; then echo "API up"; exit 0; fi
sleep 2
done
echo "API did not start"; cat /tmp/lf-api.log; exit 1

- name: Run the end-to-end suite
run: |
cd dashboard
npx playwright test --reporter=list
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ __pycache__/
*.sqlite
*.db
leadforge.db
# SQLite WAL sidecar files (created when journal_mode=WAL is enabled)
*.db-wal
*.db-shm
*.log

# Node & Next.js
Expand Down
157 changes: 130 additions & 27 deletions README.md

Large diffs are not rendered by default.

876 changes: 876 additions & 0 deletions ROADMAP.md

Large diffs are not rendered by default.

Loading
Loading