Repository navigation
feat(ai): the outreach composer, and the checks that make it trustworthy - #10
Merged
Merged
Conversation
Adds `packages/ai` — the only package in the tree that talks to a model. The
composer writes the message body for a recommendation, and everything around
it exists to make that output safe to show a reviewer.
Two properties are structural rather than requested:
1. It can only see grounded inputs. The prompt is assembled from stored
evidence, stored facts and the customer's own offering. There is no path
by which the model learns something it may not cite.
2. Its output is checked, not trusted. Eight deterministic §14.2 gates run
on every draft — grounding overlap, unsupported claims, identity
confidence, flattery, spam patterns, cross-prospect duplication,
sensitive topics, policy. A draft that fails is rewritten once, naming
the exact invented fragments; still failing, it is withheld.
Withheld is a working state, not an error. The card keeps the prospect, the
evidence and the recommended action, and the reviewer writes the message. A
bad draft next to a caveat is still a bad draft someone might approve.
The undecidable question "is this claim true?" is inverted into a decidable
one: does every specific assertion — quoted phrase, number, mid-sentence
proper noun — appear in stored evidence? That is what `checks.ts` answers,
and it is why no model output reaches a human unexamined.
Also:
- `POST /recommendations/:id/draft` composes or recomposes on demand, returns
200 with `drafted: false` and a reason when withholding, and audits it.
A user-edited draft is never discarded by a recompose.
- The approval card gains "Write draft" / "Rewrite", and states plainly why
no draft exists rather than offering a retry that cannot help.
- Prompt caching splits the stable offering/voice prefix from the
per-prospect half, so a campaign pays for the prefix once.
- The composer is optional infrastructure: without ANTHROPIC_API_KEY the
queue still runs end to end and drafting returns 503. Refusing to boot
would take the system down for a feature designed to produce nothing.
No model decides a merge, a score, or a policy outcome. 367 tests pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
packages/ai— the only package in the tree that talks to a model.What makes it safe
Two properties are structural, not requested in a prompt:
The undecidable question "is this claim true?" is inverted into a decidable one: does every specific assertion appear in stored evidence? Quoted phrases, numbers, and mid-sentence proper nouns are extracted and matched. That is what
checks.tsanswers.Withholding is a working state, not an error. The card keeps the prospect, the evidence and the recommended action; the reviewer writes the message. A bad draft next to a caveat is still a bad draft someone might approve.
Surface
POST /recommendations/:id/draft— composes or recomposes on demand. Returns200 {drafted: false, reason, unsupported}when withholding, and audits it. A user-edited draft is never discarded by a recompose.Optional by design
Without
ANTHROPIC_API_KEYthe queue still runs end to end and drafting returns 503. Refusing to boot would take the whole system down for a feature that is designed to be allowed to produce nothing.No model decides a merge, a score, or a policy outcome (PRD §1.1.8).
367 tests pass, typecheck clean.
🤖 Generated with Claude Code