Skip to content

Draft the message instead of warning about it, and make the approvals queue usable - #37

Merged
ralyodio merged 2 commits into
mainfrom
feat/approvals-filters-and-log
Aug 16, 2026
Merged

ralyodio merged 2 commits into
mainfrom
feat/approvals-filters-and-log

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Answers "why are there still no AI-generated drafts here?" — there were two separate causes, and neither was the composer being broken.

Nothing ever retried a draft

Drafting happens once, when the recommendation is created. If the composer was briefly unavailable or declined that one time, the lead became permanently undraftable — and the send sweep then found it on every tick, logged no drafted message, and moved on.

Production has 288 workflow events. Every one of them is that warning. 98 of them are for a single prospect (Christian Heide), accumulated while nothing ever attempted the thing being warned about.

Autopilot now takes a model and writes the missing message before reporting. Without a model, the old report-and-skip behaviour is unchanged, and a composer that legitimately declines still produces a no-send — but now says why.

The queue was one undifferentiated list

Production holds 73 refresh_research cards — internal actions that have no message by definition and never will — against one email actually waiting for a decision. All 73 render as cards with nothing written on them, so the page reads as "the composer is broken" when it is really "you are looking at the wrong 73 rows".

GET /recommendations now takes filter=ready|needs_draft|research|all and returns counts for all four. The page defaults to ready — outbound work with a message already written — and an empty tab links to wherever the work actually is.

needs_draft is the distinction that matters: a blank research card and an undrafted email look identical on screen, and only one of them is a problem.

Verification

  • 800 tests pass (795 before, +5 filter tests, +2 draft-retry tests over the branch point); format and typecheck clean.
  • AS all was a real bug caught by the suite — all is reserved in SQLite.

Note on the log

You asked for a log to check periodically. One already exists and is live: /today renders the workflow event feed (server-rendered, then live over SSE). It just happens to be showing 288 copies of the same warning right now, which this PR removes the cause of.

🤖 Generated with Claude Code

ralyodio and others added 2 commits August 16, 2026 18:27
…e usable

Two problems that together made the approvals page look broken.

**Nothing ever retried a draft.** Drafting happens once, when the
recommendation is created. If the composer was unavailable or declined that
one time, the lead was permanently undraftable — and the send sweep found
it on every tick, logged "no drafted message" and moved on. Production has
288 workflow events, all of them that warning, 98 of them for one prospect,
while nothing attempted the thing being warned about. Autopilot now takes a
model and writes the missing message before reporting; without a model the
old report-and-skip behaviour is unchanged.

**The queue was one undifferentiated list.** Production holds 73
`refresh_research` cards — internal actions that have no message by
definition and never will — against a single email actually waiting for a
decision. All 73 render as cards with nothing written on them, so the page
reads as "the composer is broken" when it is really "you are looking at the
wrong 73 rows".

`GET /recommendations` takes `filter=ready|needs_draft|research|all` and
returns counts for all four. The page defaults to `ready` — outbound work
with a message already written — and an empty tab links to where the work
actually is. `needs_draft` is the distinction that matters: a blank
research card and an undrafted email look identical on screen and only one
of them is a problem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Changing fetchApprovals to return counts alongside the cards broke Today,
which also calls it and treated the response as an array. The root
typecheck does not cover apps/web — Next type-checks during its own build —
so this only surfaced in CI's image job. Verified here by running the web
build rather than trusting the root check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ralyodio
ralyodio merged commit 2011ce7 into main Aug 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant