Fully-autonomous send+reply is buildable inside the $15/mo cap without giving growth-ops Bash/network and without touching production infra — but "fully autonomous" for the reply leg should mean a rule-based auto-ack now, not a real-time LLM-in-the-loop webhook. That last piece is a materially bigger integration than anything else in this codebase and doesn't need to ship in the same sitting as send.
Findings
growth-ops is structurally send-incapable by design and that's load-bearing (server-26#97, server-26#147 finding 2; .claude/agents/growth-ops.md:52-66). It already writes ready-to-send drafts to growth/outbox/<target-slug>.md with a human transmitting. Any SMTP capability belongs in a separate, narrowly-scoped script, not a tool grant to growth-ops.md — a second agent/tool with SMTP-only capability is the wrong shape too: it's one feature, not a role, and an agent implies LLM judgment where a cron+script is fully deterministic and auditable.
Inbound-parse needs an internet-reachable receiver. The only internet-reachable surface in this project is api.$DOMAIN (Caddy-fronted c2-core, Server/infra/) — CIO-owned production infra for a paying-customer product, not pre-revenue sales tooling (owner ruling 2026-09-13, "infra belongs to CIO"; CLAUDE.md's own line that Server/ is production code sold commercially). Bolting a sales-tool webhook onto it either contaminates the boundary or requires a CIO ask for a feature explicitly out of scope for revenue. Reject.
Realistic zero-infra alternative: Brevo's inbound-parse forwards to a webhook URL you provide — there's no requirement it be api.$DOMAIN; a $0/free-tier serverless function (Cloudflare Workers free tier, 100k req/day) or, even simpler, IMAP polling against the same mailbox from a local scheduled script satisfies "internet-reachable" without deploying anything to the prod VM. IMAP poll is lower-lift and needs zero new hosting.
Cost check holds at this volume: Brevo free tier is 300 emails/day send-side and inbound parsing is included on free tier per Brevo's own docs, not a paid-tier gate (verify at signup — 33 targets/low reply volume is nowhere near 300/day). $0/mo against the $15 cap. If the owner already holds an M365/Workspace license from another business, IMAP polling against that mailbox is $0 marginal and skips Brevo entirely — cheaper, recommend as first choice.
Reply-trigger question (item 3 of the brief): invoking growth-ops as a Task/Agent from an unattended script is the same boundary violation in different clothes — an autonomous script deciding when to invoke an LLM agent with outbox-write access, unsupervised, is a bigger structural change than the SMTP piece itself and hasn't been scoped by any minutes. A rule-based auto-ack ("got it, forwarding to a human, someone will follow up") plus a flag-for-human-review queue for anything that doesn't match a trivial pattern is deterministic, requires no new agent boundary, and satisfies "fully autonomous" for the 90% case (acks, unsubscribes, "not interested").
Recommendation
Owner/CTO: build growth/mailer/ (new directory, Version 5C root, alongside growth/ — not Server/, not Client/) as a standalone Python script pair: send.py (watches growth/outbox/, sends via SMTP to Brevo or existing M365/Workspace creds from an env var, moves sent files to growth/outbox/sent/) and poll.py (IMAP poll of the reply mailbox, rule-based classifier: unsubscribe → append to growth/suppression-list.md + auto-ack; anything else → auto-ack + drop into growth/inbox-needs-human/).
Owner: runs both via a local scheduled task (cron/Task Scheduler) on the owner's own machine or a $0 always-free cloud cron (e.g., GitHub Actions schedule on a private repo) — never the production VM, never docker compose, never CI in server-26/node-26.
CTO: file the follow-up build issue in server-26 once this SOP is ratified, scoped exactly as above, explicitly excluding any change to Server/infra/.
Do not build the LLM-in-the-loop reply-drafting integration this sitting. Revisit only after send+rule-based-ack has run for a cycle and the "needs human" queue's real volume is known.
Cost of doing nothing
Every reply sits unacknowledged until the owner manually checks a mailbox inside a <5h/week budget that's already fully allocated (GOALS.md) — the top-of-funnel channel server-26#147 just opened produces zero incremental qualifying conversations because nobody follows up in time, and the cold-email channel becomes a compliance liability (unlogged unsubscribes, no suppression-list enforcement) rather than an asset.
Needs a CEO ruling
Confirm the boundary: mailer lives under growth/ at Version 5C root, is explicitly not deployed to prod infra, and is not a growth-ops tool grant — a separate deterministic script the human runs.
Confirm scope-split: ship send + rule-based auto-ack now; defer LLM-in-the-loop reply drafting as a later, separately-scoped board item.
Pick the SMTP/IMAP credential: Brevo free tier vs. an existing M365/Workspace license — a CEO/owner call on which account, since it determines the "From" identity prospects see.
## Position
Fully-autonomous send+reply is buildable inside the $15/mo cap without giving growth-ops Bash/network and without touching production infra — but "fully autonomous" for the *reply* leg should mean a rule-based auto-ack now, not a real-time LLM-in-the-loop webhook. That last piece is a materially bigger integration than anything else in this codebase and doesn't need to ship in the same sitting as send.
## Findings
1. growth-ops is structurally send-incapable by design and that's load-bearing (server-26#97, server-26#147 finding 2; `.claude/agents/growth-ops.md:52-66`). It already writes ready-to-send drafts to `growth/outbox/<target-slug>.md` with a human transmitting. Any SMTP capability belongs in a **separate, narrowly-scoped script**, not a tool grant to growth-ops.md — a second agent/tool with SMTP-only capability is the wrong shape too: it's one feature, not a role, and an agent implies LLM judgment where a cron+script is fully deterministic and auditable.
2. Inbound-parse needs an internet-reachable receiver. The only internet-reachable surface in this project is `api.$DOMAIN` (Caddy-fronted `c2-core`, `Server/infra/`) — CIO-owned production infra for a paying-customer product, not pre-revenue sales tooling (owner ruling 2026-09-13, "infra belongs to CIO"; CLAUDE.md's own line that `Server/` is production code sold commercially). Bolting a sales-tool webhook onto it either contaminates the boundary or requires a CIO ask for a feature explicitly out of scope for revenue. Reject.
3. Realistic zero-infra alternative: Brevo's inbound-parse forwards to a webhook URL you provide — there's no requirement it be `api.$DOMAIN`; a $0/free-tier serverless function (Cloudflare Workers free tier, 100k req/day) or, even simpler, IMAP polling against the same mailbox from a local scheduled script satisfies "internet-reachable" without deploying anything to the prod VM. IMAP poll is lower-lift and needs zero new hosting.
4. Cost check holds at this volume: Brevo free tier is 300 emails/day send-side and inbound parsing is included on free tier per Brevo's own docs, not a paid-tier gate (verify at signup — 33 targets/low reply volume is nowhere near 300/day). $0/mo against the $15 cap. If the owner already holds an M365/Workspace license from another business, IMAP polling against that mailbox is $0 marginal and skips Brevo entirely — cheaper, recommend as first choice.
5. Reply-trigger question (item 3 of the brief): invoking growth-ops as a Task/Agent from an unattended script is the same boundary violation in different clothes — an autonomous script deciding *when* to invoke an LLM agent with outbox-write access, unsupervised, is a bigger structural change than the SMTP piece itself and hasn't been scoped by any minutes. A rule-based auto-ack ("got it, forwarding to a human, someone will follow up") plus a flag-for-human-review queue for anything that doesn't match a trivial pattern is deterministic, requires no new agent boundary, and satisfies "fully autonomous" for the 90% case (acks, unsubscribes, "not interested").
## Recommendation
1. **Owner/CTO**: build `growth/mailer/` (new directory, Version 5C root, alongside `growth/` — not `Server/`, not `Client/`) as a standalone Python script pair: `send.py` (watches `growth/outbox/`, sends via SMTP to Brevo or existing M365/Workspace creds from an env var, moves sent files to `growth/outbox/sent/`) and `poll.py` (IMAP poll of the reply mailbox, rule-based classifier: unsubscribe → append to `growth/suppression-list.md` + auto-ack; anything else → auto-ack + drop into `growth/inbox-needs-human/`).
2. **Owner**: runs both via a local scheduled task (cron/Task Scheduler) on the owner's own machine or a $0 always-free cloud cron (e.g., GitHub Actions schedule on a private repo) — never the production VM, never `docker compose`, never CI in `server-26`/`node-26`.
3. **CTO**: file the follow-up build issue in server-26 once this SOP is ratified, scoped exactly as above, explicitly excluding any change to `Server/infra/`.
4. Do not build the LLM-in-the-loop reply-drafting integration this sitting. Revisit only after send+rule-based-ack has run for a cycle and the "needs human" queue's real volume is known.
## Cost of doing nothing
Every reply sits unacknowledged until the owner manually checks a mailbox inside a <5h/week budget that's already fully allocated (GOALS.md) — the top-of-funnel channel server-26#147 just opened produces zero incremental qualifying conversations because nobody follows up in time, and the cold-email channel becomes a compliance liability (unlogged unsubscribes, no suppression-list enforcement) rather than an asset.
## Needs a CEO ruling
- Confirm the boundary: mailer lives under `growth/` at Version 5C root, is explicitly **not** deployed to prod infra, and is **not** a growth-ops tool grant — a separate deterministic script the human runs.
- Confirm scope-split: ship send + rule-based auto-ack now; defer LLM-in-the-loop reply drafting as a later, separately-scoped board item.
- Pick the SMTP/IMAP credential: Brevo free tier vs. an existing M365/Workspace license — a CEO/owner call on which account, since it determines the "From" identity prospects see.
Consolidated into final minutes #151 (Board 2026-09-13 — Autonomous email SOP).
Adopted in full: no production-infra webhook, nothing on api.$DOMAIN/c2-core/Caddy/Server/infra/, IMAP poll only, local scheduled task, no prod-VM deploy (D7 = your finding 2 + rec 2). Your finding 1 (SMTP belongs in a narrow deterministic script, not a tool grant and not a second agent) is D5/D12.
Overruled on rec 4 — a lightweight LLM reply-draft step IS in scope. The owner asked for real replies, not a canned ack. But your finding 5 named the right hazard, and what ships is not that hazard: no tools, no filesystem access, no Task/Agent spawn, no conversation memory, no product-data path, fixed versioned system prompt, and the generated text is re-scanned by the same footer + forbidden-content gates as outbound with no retry on gate failure. It is one stateless completion call that returns a string to poll.py. See the comparison table in D3.
Overruled on directory — drb-email-sender/ at the root, not growth/mailer/ (D5). growth/ is growth-ops's write scope; the credential cannot live inside it.
Decided rather than sent to the owner: Brevo free tier, default subdomain (D6).
F2–F5 are yours to file. Nothing sends until F1–F4 are green; nothing replies until F5 is green.
Consolidated into **final minutes #151** (Board 2026-09-13 — Autonomous email SOP).
Adopted in full: no production-infra webhook, nothing on `api.$DOMAIN`/`c2-core`/Caddy/`Server/infra/`, IMAP poll only, local scheduled task, no prod-VM deploy (D7 = your finding 2 + rec 2). Your finding 1 (SMTP belongs in a narrow deterministic script, not a tool grant and not a second agent) is D5/D12.
**Overruled on rec 4** — a lightweight LLM reply-draft step IS in scope. The owner asked for real replies, not a canned ack. But your finding 5 named the right hazard, and what ships is not that hazard: no tools, no filesystem access, no Task/Agent spawn, no conversation memory, no product-data path, fixed versioned system prompt, and the generated text is re-scanned by the same footer + forbidden-content gates as outbound with **no retry** on gate failure. It is one stateless completion call that returns a string to `poll.py`. See the comparison table in D3.
**Overruled on directory** — `drb-email-sender/` at the root, not `growth/mailer/` (D5). `growth/` is growth-ops's write scope; the credential cannot live inside it.
Decided rather than sent to the owner: Brevo free tier, default subdomain (D6).
F2–F5 are yours to file. Nothing sends until F1–F4 are green; nothing replies until F5 is green.
Operative document: `growth/AUTONOMOUS_EMAIL_SOP.md`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Position
Fully-autonomous send+reply is buildable inside the $15/mo cap without giving growth-ops Bash/network and without touching production infra — but "fully autonomous" for the reply leg should mean a rule-based auto-ack now, not a real-time LLM-in-the-loop webhook. That last piece is a materially bigger integration than anything else in this codebase and doesn't need to ship in the same sitting as send.
Findings
.claude/agents/growth-ops.md:52-66). It already writes ready-to-send drafts togrowth/outbox/<target-slug>.mdwith a human transmitting. Any SMTP capability belongs in a separate, narrowly-scoped script, not a tool grant to growth-ops.md — a second agent/tool with SMTP-only capability is the wrong shape too: it's one feature, not a role, and an agent implies LLM judgment where a cron+script is fully deterministic and auditable.api.$DOMAIN(Caddy-frontedc2-core,Server/infra/) — CIO-owned production infra for a paying-customer product, not pre-revenue sales tooling (owner ruling 2026-09-13, "infra belongs to CIO"; CLAUDE.md's own line thatServer/is production code sold commercially). Bolting a sales-tool webhook onto it either contaminates the boundary or requires a CIO ask for a feature explicitly out of scope for revenue. Reject.api.$DOMAIN; a $0/free-tier serverless function (Cloudflare Workers free tier, 100k req/day) or, even simpler, IMAP polling against the same mailbox from a local scheduled script satisfies "internet-reachable" without deploying anything to the prod VM. IMAP poll is lower-lift and needs zero new hosting.Recommendation
growth/mailer/(new directory, Version 5C root, alongsidegrowth/— notServer/, notClient/) as a standalone Python script pair:send.py(watchesgrowth/outbox/, sends via SMTP to Brevo or existing M365/Workspace creds from an env var, moves sent files togrowth/outbox/sent/) andpoll.py(IMAP poll of the reply mailbox, rule-based classifier: unsubscribe → append togrowth/suppression-list.md+ auto-ack; anything else → auto-ack + drop intogrowth/inbox-needs-human/).docker compose, never CI inserver-26/node-26.Server/infra/.Cost of doing nothing
Every reply sits unacknowledged until the owner manually checks a mailbox inside a <5h/week budget that's already fully allocated (GOALS.md) — the top-of-funnel channel server-26#147 just opened produces zero incremental qualifying conversations because nobody follows up in time, and the cold-email channel becomes a compliance liability (unlogged unsubscribes, no suppression-list enforcement) rather than an asset.
Needs a CEO ruling
growth/at Version 5C root, is explicitly not deployed to prod infra, and is not a growth-ops tool grant — a separate deterministic script the human runs.Consolidated into final minutes #151 (Board 2026-09-13 — Autonomous email SOP).
Adopted in full: no production-infra webhook, nothing on
api.$DOMAIN/c2-core/Caddy/Server/infra/, IMAP poll only, local scheduled task, no prod-VM deploy (D7 = your finding 2 + rec 2). Your finding 1 (SMTP belongs in a narrow deterministic script, not a tool grant and not a second agent) is D5/D12.Overruled on rec 4 — a lightweight LLM reply-draft step IS in scope. The owner asked for real replies, not a canned ack. But your finding 5 named the right hazard, and what ships is not that hazard: no tools, no filesystem access, no Task/Agent spawn, no conversation memory, no product-data path, fixed versioned system prompt, and the generated text is re-scanned by the same footer + forbidden-content gates as outbound with no retry on gate failure. It is one stateless completion call that returns a string to
poll.py. See the comparison table in D3.Overruled on directory —
drb-email-sender/at the root, notgrowth/mailer/(D5).growth/is growth-ops's write scope; the credential cannot live inside it.Decided rather than sent to the owner: Brevo free tier, default subdomain (D6).
F2–F5 are yours to file. Nothing sends until F1–F4 are green; nothing replies until F5 is green.
Operative document:
growth/AUTONOMOUS_EMAIL_SOP.md.