camomail · Harvey port

Initiation playbook progress. Updated 2026-10-16 10:05 UTC.

49%overall
39/79tasks complete
4/10phases complete
4files with uncommitted changes
Now working on
Phase 05: The Prospecting Pipeline as Background Jobs — Port email finding and verification (integrations/email_finder.py, integrations/quota.py) to apps/api/src/sales/pipeline/email/:
Workflow running Phase 05 tasks in order, then a phase review.

Phases

01 Rebrand to camomail and Clear the Starter Scaffolding Complete 8/8
  1. 1. Take an inventory before changing anything, and save it to /Users/system/www/camomail/.maestro/playbooks/Initiation/Working/phase-01-inventory.md:
  2. 2. Remove every feature fence marker while KEEPING the code inside the fences (all features stay enabled in camomail).
  3. 3. Delete the scaffolding leftovers and stale docs:
  4. 4. Rename npm packages and imports from @orbit/* to @camomail/*:
  5. 5. Rebrand user-facing text and assets from Orbit to camomail:
  6. 6. Rewrite CLAUDE.md for camomail:
  7. 7. Add a placeholder "Today" page so the rebranded app shows where the sales agent will live:
  8. 8. Verify the rebranded prototype end to end and fix anything that breaks:
02 Per-Workspace Integrations Framework Complete 11/11
  1. 1. Study the existing patterns before writing code, and write .maestro/playbooks/Initiation/Working/phase-02-notes.md covering:
    Notes written to Working/phase-02-notes.md. Watch out: routes use :slug (not :workspaceId), the web app is apps/web-tanstack, there is no migrations folder / integration-test config / testcontainers / nodemailer / imapflow yet, Harvey has no OpenRou…
  2. 2. Add a credential encryption service:
    Done: apps/api/src/kernel/secret-box.ts (SecretBox port, AesGcmSecretBox, UnconfiguredSecretBox, SecretKeyring, readSecretKeyring, redact), exposed as secretBox on AppContainer and FeatureCore; keys are on AppConfig.encryptionKeys. Not…
  3. 3. Add the integrations data model:
    Done: workspaceIntegrations table in schema.ts, integration: "intg" prefix, and the two permissions (Integrations group in PERMISSION_GROUPS; ADMIN gets view+manage, MEMBER gets view, OWNER gets both via ALL_WORKSPACE_PERMISSIONS). Notes for later…
  4. 4. Build the integrations domain, application and infrastructure layers in apps/api/src/integrations/, following the conventions of the workspaces context:
    Done: apps/api/src/integrations/ (feature.ts, domain/{integration,events,repositories}.ts, infrastructure/drizzle-integration.repository.ts, application/{list,connect,test,disconnect}-integration(s).service.ts, connection-test.ts, `integration-c…
  5. 5. Implement the provider registry, apps/api/src/integrations/providers/, with one file per provider.
    Done: one file per provider in providers/ plus http.ts (shared readJson, httpFailure, pick, and form-tolerant Zod helpers requiredSecret/optionalSecret/optionalText/port/formBoolean); PROVIDERS lists all 12 in display order. Added `nod…
  6. 6. Implement the Gmail OAuth connect flow, using the workspace's own client credentials:
    Done: integrations/application/gmail-oauth-state.ts (GmailOAuthStateCodec), application/gmail-oauth.service.ts (GmailOAuthService.authorize/verify/complete), infrastructure/gmail-client.ts (GmailClient, GmailClientFactory), and `interfaces/htt…
  7. 7. Add the HTTP controller apps/api/src/interfaces/http/controllers/integrations.controller.ts, mounted under the workspace routes:
    Done: routes added to the existing integrations Hono in integrations.controller.ts (already mounted at /v1/workspaces). Notes for later tasks:
  8. 8. Build the Integrations settings UI in apps/web-tanstack/src/pages/workspace-settings/integrations-page.tsx, reusing SettingsSection and @camomail/ui components (Card, Badge, Dialog/Sheet, Input, Switch, Select, …
    Done: pages/workspace-settings/integrations-page.tsx (one SettingsSection per category, cards in a 2-column grid; ConfigureIntegrationDialog renders IntegrationDTO.fields 1:1: text/email/number/password inputs, Switch for boolean, Select for sel…
  9. 9. Write tests for the integrations context in apps/api/src/__tests__/ (follow the existing unit and integration test conventions):
    Done: apps/api/vitest.integration.config.ts (the missing config: src/**/*.integration.test.ts, one file at a time, 120 s hook timeout), src/__tests__/support/postgres.ts (startTestDatabase(): a postgres:17-alpine Testcontainer, or `TEST_DATABASE_U…
  10. 10. Human step done:
  11. 11. Run npm run typecheck, npm run lint, npm run test --workspace @camomail/api and npm run test:integration --workspace @camomail/api, and fix all failures.
    Done. All checks are green:
03 Workspace Agent Configuration (Persona, Product, ICP, Limits, Prompts) Complete 8/8
  1. 1. Map Harvey's configuration to the new model before writing code.
  2. 2. Define shared Zod schemas and types in packages/shared/src/sales-config.ts (export from the package index):
  3. 3. Add persistence:
  4. 4. Create the apps/api/src/sales/ bounded context (the home for the whole sales domain from now on) with a config/ sub-area:
  5. 5. Add HTTP endpoints in interfaces/http/controllers/agent-settings.controller.ts:
  6. 6. Build the settings pages in apps/web-tanstack, following the existing pages/workspace-settings/ patterns and the $section route:
  7. 7. Write tests:
  8. 8. Run npm run typecheck, npm run lint and both API test suites, and fix failures.
04 Sales Data Model in Postgres Complete 8/8
  1. 1. Analyse the Harvey schema in depth and write .maestro/playbooks/Initiation/Working/phase-04-schema-map.md.
    See Working/phase-04-schema-map.md ([[phase-04-schema-map]]). Where it differs from the wording of the tasks below, follow the map. Its "Findings" section explains why.
  2. 2. Add the sales tables to apps/api/src/db/drizzle/schema.ts according to the schema map, each with workspace_id FK (cascade), a created_at/updated_at pair, and composite indexes that start with workspace_id:
    All 15 tables are in the // ── Sales data model section of schema.ts and follow the schema map exactly: camelCase columns, timestamptz(3), composite (workspaceId, xId) FKs, append-only ledgers without updatedAt. Migration: `apps/api/drizzle/migrat…
  3. 3. Build domain entities and repository interfaces in apps/api/src/sales/domain/:
    The domain lives in apps/api/src/sales/domain/ (unit tests: sales-domain.test.ts, 22 cases).
  4. 4. Implement Drizzle repositories in apps/api/src/sales/infrastructure/ (one drizzle-*.repository.ts per aggregate), including the query methods the pipeline and dashboard need:
    All 15 repositories + SalesStatsQuery service implemented in apps/api/src/sales/infrastructure/:
  5. 5. Add read endpoints in interfaces/http/controllers/sales.controller.ts with new permissions workspace.sales.view / workspace.sales.manage (member gets view, admin/owner get manage):
    Routes live in sales.controller.ts, mounted under /v1/workspaces.
  6. 6. Add a dev seed command npm run seed:sales --workspace @camomail/api (apps/api/scripts/seed-sales.ts) that fills a chosen workspace with realistic fake companies, prospects, signals, drafts in each status, and a sh…
    Run npm run seed:sales --workspace @camomail/api -- --slug <slug> [--reset]. Without --slug it lists the workspaces, and it refuses NODE_ENV=production.
  7. 7. Write tests:
    All suites pass: 324 unit tests and 52 integration tests.
  8. 8. Run npm run typecheck, npm run lint, both test suites and the seed command, and fix failures.
    Everything is green, and the seeded data comes back over HTTP.
05 The Prospecting Pipeline as Background Jobs In progress 4/10
  1. 1. Read the job infrastructure (apps/api/src/jobs/application/*.ts, jobs/infrastructure/graphile-*.ts, jobs/feature.ts) and Harvey's pipeline.py and main.py.
    See Working/phase-05-pipeline-plan.md ([[phase-05-pipeline-plan]]). Where it differs from the wording of the tasks below, follow the plan. Its "Findings" and "Deviations" sections explain why.
  2. 2. Build the OpenRouter brain in apps/api/src/sales/brain/:
    apps/api/src/sales/brain/, wired as services.brain in sales/feature.ts (documented in CLAUDE.md).
  3. 3. Port discovery and profiling (collectors/discover.py, collectors/profile.py, integrations/tech_detect.py) to apps/api/src/sales/pipeline/discover/ and .../profile/:
    apps/api/src/sales/pipeline/ (discover/, profile/, http/, stage.ts) and apps/api/src/sales/signals/, wired in sales/feature.ts as services.discoverStage, services.profileStage and services.signalCatalog (documented in CLAUDE.md).
  4. 4. Port the scout (agents/scout.py, signals.py) to apps/api/src/sales/pipeline/scout/:
    apps/api/src/sales/pipeline/scout/, wired as services.scoutStage in sales/feature.ts (documented in CLAUDE.md). The profiler and the scout now share one WebPageFetcher, and feature.ts builds the Brain once.
  5. 6. Port the writer and outbox (agents/writer.py, agents/analyst.py if it feeds the writer) to apps/api/src/sales/pipeline/writer/:
  6. 7. Port the sender (agents/sender.py, integrations/mail_provider.py, gmail.py, smtp_mail.py, instantly.py) to apps/api/src/sales/pipeline/sender/:
  7. 8. Wire the scheduler and orchestration in apps/api/src/sales/pipeline/:
  8. 9. Write tests for the pipeline with fake fetch and fake mail providers:
  9. 10. Run npm run typecheck, npm run lint and both API test suites, and fix every failure.
06 Reply Handling and Meeting Booking Not started 0/7
  1. 1. Read harvey/harvey/agents/handler.py, integrations/calendar.py, prompts/handler.md, skills/objection_handling.md and the reply-related StateManager methods.
  2. 2. Implement inbox readers in apps/api/src/sales/inbox/:
  3. 3. Implement reply classification and suppression:
  4. 4. Port the handler response flow:
  5. 5. Add endpoints:
  6. 6. Write tests:
  7. 7. Run npm run typecheck, npm run lint and both API test suites, and fix failures.
07 Trainer — Learn the Product from the Workspace's Website Not started 0/6
  1. 1. Read harvey/harvey/trainer.py and the trainer-related parts of harvey/harvey/setup.py, and write .maestro/playbooks/Initiation/Working/phase-07-trainer-plan.md covering the crawl strategy, FALLBACK_PATHS, page…
  2. 2. Implement the crawler in apps/api/src/sales/trainer/:
  3. 3. Implement the training job and service:
  4. 4. Build the trainer UI in apps/web-tanstack:
  5. 5. Write tests:
  6. 6. Run npm run typecheck, npm run lint and both API test suites, and fix failures.
08 Opt-In LinkedIn Prospecting Not started 0/6
  1. 1. Read harvey/harvey/integrations/linkedin.py and the LinkedIn branches in agents/sender.py, agents/writer.py and pipeline.py, and write .maestro/playbooks/Initiation/Working/phase-08-linkedin-plan.md covering…
  2. 2. Add the opt-in gate:
  3. 3. Implement the LinkedIn automation adapter in apps/api/src/sales/linkedin/:
  4. 4. Wire LinkedIn into the pipeline:
  5. 5. Write tests with a fake LinkedInClient:
  6. 6. Run npm run typecheck, npm run lint and both API test suites, and fix failures.
09 The Sales Dashboard Not started 0/8
  1. 1. Read Harvey's dashboard (dashboard.py routes and web/app.js views) and the camomail frontend conventions (apps/web-tanstack/src/routes/d/$workspaceSlug/, components/sidebar, components/workspace-page.tsx, `l…
  2. 2. Add API client functions and query keys for every sales endpoint from Phases 04–08, plus realtime handlers in lib/db/realtime.ts and lib/stores/workspace-stores.ts that invalidate or patch queries on sales.* eve…
  3. 3. Build the Today page (replacing the Phase 01 placeholder at /d/$workspaceSlug/today):
  4. 4. Build the Outbox page /d/$workspaceSlug/outbox:
  5. 5. Build the Companies and Contacts pages:
  6. 6. Build the Signals, Campaigns, Conversations and Discover pages:
  7. 7. Add the sidebar navigation (Today, Outbox with a pending-count badge, Companies, Contacts, Signals, Campaigns, Conversations, Discover) above Settings in components/sidebar, following existing item patterns; update …
  8. 8. Verify the dashboard:
10 Hardening, Parity Check and Retiring Harvey Not started 0/7
  1. 1. Run a parity audit and write docs/architecture/harvey-parity.md with YAML front matter (`type:
  2. 2. Implement every gap marked Ported-but-missing in the parity report (for example CSV export from export.py as GET /sales/companies/export and /prospects/export endpoints with a UI button, or usage reporting from …
  3. 3. Harden tenancy and data lifecycle:
  4. 4. Harden operations:
  5. 5. Write an end-to-end integration test apps/api/src/__tests__/sales-e2e.integration.test.ts that, with fake providers, creates a workspace, connects fake integrations, applies trainer suggestions, runs the heartbeat, …
  6. 6. Run the full verification:
  7. 7. Retire Harvey:

Recent commits