Letting an AI Email the CEO, Unsupervised
2026
Internal Tooling

Letting an AI Email the CEO, Unsupervised

A weekly vendor-delivery status report that reads Jira, GitLab, Slack, Confluence and Sentry, computes every number in deterministic Python, has an LLM write the prose over it — and since August 4th sends it to the CEO with nobody reading it first.

274 Tests
None Human review
1 pre-ship Fabrications caught
40 days Hidden blocker found
PythonpytestLLM OpsJiraGitLabAgentic AutomationReportingGuardrails

The problem

A website rebuild was being delivered by an external vendor, tracked in Jira with code in GitLab. Keeping the CEO informed meant somebody manually assembling status every week: slow, subjective, and — the real issue — easy to spin. A human writing their own project's status report is not a neutral narrator.

I built a system that reads Jira, GitLab, Slack, Confluence and Sentry, computes a metrics snapshot, writes the prose over that snapshot, and emails the report to me and the CEO. Since August 4th that runs fully automatically, with no human reading the prose before it lands in the CEO's inbox.

Which is a genuinely uncomfortable thing to ship. Most of the design is about earning it.

Compute and narrate, strictly separated

The single most important decision: the LLM may not compute anything.

Plain Python calculates every number — 274 pytest tests, socket-blocked so nothing can accidentally reach a live API during a test run. The model receives a fixed brief and writes prose over it. It may not compute, round, or estimate.

Then an audit layer extracts every number from the generated prose and rejects anything not present in the brief, with a single named-problem retry.

This caught a real fabrication before it shipped: an "8-week median" with no source anywhere in the brief. Plausible, well-written, entirely invented. That single catch is the whole argument for the layer.

No API key exists anywhere

The weekly run is a scheduled cloud coding routine — the assistant is the narrator under the subscription, so there's no API key in the system at all. Slack I/O goes through the routine's connector. The only mail credential lives in a dispatch-only GitHub workflow with no schedule trigger, and tests assert both of those properties rather than trusting them.

Honest metrics by design

Outcomes

That third bullet is the point of the whole project. The bottleneck was on our side, and the manual report had never once said so.

What we learned

The live project beats the plan. The real Jira board contradicted my assumptions repeatedly — team-managed project quirks, nobody logging time, 34 issues closed as Won't Do, a 40-bug bulk-close skewing throughput. Every rule had to bend to observed data.

A prompt alone doesn't hold for autonomous runs. The first routine run committed its snapshot to a working branch instead of main — correct, green, and completely invisible to the next run. The fix was prevent / detect / recover layers, not better prompting. Autonomy fails quietly, not loudly.

Green tests are not done. A post-build audit found three defects no test caught. The worst: the three scope buckets overlapped, so the forecast blended approved and awaiting-decision work — defeating the exact design decision meant to prevent that.

Number-auditing LLM prose works, but is weakest on single digits. That's an acknowledged, documented limit rather than a hidden one.

Cost-consciousness as a first-class requirement: a cheap model for narration, trimmed briefs, and tables rendered in Python instead of spent as tokens.

How it was built

Solo, in 5 stage-gated stages: the maths validated against fixtures first, live APIs only later. At each gate I reviewed real numbers, not code. Then a defect audit after "done", and the production run handed to a scheduled cloud routine whose prompt (ROUTINE.md) lives in the repo like code.

History is git. Every weekly snapshot and the exact prose that was sent are committed, never regenerated — so "what did we tell the CEO in week 3" has one answer.