Restrict comms_by_user/email_counts_by_user to matched-investor email

Both NL-query intents counted/listed a user's ENTIRE captured sent corpus
(internal, vendor, personal mail) rather than only email to a matched investor
— they were missing the `EXISTS email_investor_links` gate that recent_emails
and the Communications panel's query_email_activity use. Their own docstrings
said "investor emails", so the behavior was wrong, not just loose.

Add the matched-only gate to both, mirroring query_email_activity. The runner
test now seeds an unmatched sent email and asserts it is excluded (without the
fix comms_by_user returns 3 not 2, this_week 2 not 1) — the prior fixture
linked every email, so the leak went uncaught.

Also documents the matched-only rule in the nl-query guide, and refreshes the
AGENTS.md Current state (v93 deployed; this fix pending a v94 s9pk since the
intents run on the box, not the bot).
This commit is contained in:
Keysat
2026-06-18 20:24:52 -05:00
parent f7b03ee109
commit 2d43bad6fc
4 changed files with 38 additions and 10 deletions
+11
View File
@@ -49,6 +49,17 @@ axis is the **`graveyard` flag** (exclude `graveyard = 1` for "live"). Other tab
to avoid importing the `__main__` server module — helpers take a `conn`, never import server).
Keep the two in sync; the soft-delete test guards the copy.
## Email/comms intents are MATCHED-ONLY
The email-touching intents (`recent_emails`, `comms_by_user`, `email_counts_by_user`,
`investor_last_contact`) surface only **investor-linked** email — an `email_investor_links` row
must exist — exactly like the Communications panel's `query_email_activity`. Captured
internal/vendor/personal mail is never counted or listed. The gate is
`EXISTS (SELECT 1 FROM email_investor_links l WHERE l.email_id = e.id)`. **`comms_by_user` /
`email_counts_by_user` originally omitted this** and counted the user's *entire* sent corpus —
fixed; the runner test now seeds an unmatched sent email to guard it. Add this gate to any new
email intent.
## Endpoint, caps, audit
- `POST /api/query/nl` (`require_bot_or_admin`, read-only) — body `{question}` (local translate)