fix(triage): demote awaiting_reply → fyi for confident group/service senders (#210)#389
Merged
Merged
Conversation
…senders (#210) The #218 sender-kind projection is now activated in prod, but its consumer is only a soft prompt nudge — a read-only re-categorize pass showed the classifier still tags demoted senders (e.g. messages-noreply@linkedin.com, group 0.99) as `awaiting_reply` whenever the body reads action-y. `awaiting_reply` from a confident group/service sender is definitionally wrong (you do not write back to a distribution list or a noreply/bot address), so add a deterministic post-classification floor that demotes it to `fyi`. Scoped to `awaiting_reply` only — the zero-bury-risk case (#210 "demote, never bury"). `action_needed`/`urgent` are left to the model + prompt: a group/service CAN legitimately assign the user an action, so demoting those needs a body-level "directly assigns / @-mentions the user" carve-out (ADR-0066), not this floor. - `applySenderKindDemotionFloor` (pure), applied in `classifyEmail` BEFORE the secret override floor so an escalation always wins; tags model id `+kindfloor`. - Decision trace records `senderKindDemotedCategory` alongside the existing person-treatment breadcrumb. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Why
The #218 sender-kind user-model projection is now activated in prod (both mailboxes, v2). But its triage consumer is only a soft prompt nudge — it injects a
senderKindhint + a rubric sentence, it does not move the category deterministically.A read-only re-categorize pass (
dry-run-triage-recategorize-committed.js) over recent triaged threads, with the projection live, confirmed the gap: confidentgroup/servicesenders still land in demanding lanes whenever the body reads action-y. The clearest miss:fyi → awaiting_reply|messages-noreply@linkedin.com("still waiting for your response") — group 0.99, the exact Product: triage over-tags attention — significance is absolute & gates todos not categories #210 LinkedIn case.Net demanding-lane movement was ~flat. The signal resolves correctly; it just doesn't change the tag.
What
A deterministic post-classification floor: when a confident
group/servicesender's category isawaiting_reply, demote it tofyi. You do not owe a reply to a distribution list or anoreply/bot address, soawaiting_replyfrom one is definitionally wrong.Scoped to
awaiting_replyonly — the zero-bury-risk case (#210 "demote, never bury":fyistays visible).action_needed/urgentare deliberately left to the model + prompt, because a group/service can legitimately assign the user an action (a ClickUp task actually assigned to them, a real security/payment alert); demoting those safely needs a body-level "directly assigns / @-mentions the user" carve-out — that's the ADR-0066 significance-weighted-category work, not this floor.applySenderKindDemotionFloor(pure) inclassify.ts, applied inclassifyEmailbefore the secret override floor so an escalation always has the final word. Tags the model id+kindfloor.senderKindDemotedCategorybreadcrumb added to the decision trace (senderExtractionEvent), distinct from the existing person-treatment flag.senderKindis non-null only for a confident group/service (resolveSenderKindgates kind ∈ {group,service} AND confidence ≥ 0.8), so presence is the gate.Tests
applySenderKindDemotionFloor(group/service demote; action_needed/urgent/null untouched).classifyEmailorchestration test (awaiting_reply + group senderKind →fyi,+kindfloor,audit.senderKindDemoted).Rollout / verification
Takes effect after deploy. To verify: re-run the read-only re-categorize pass and confirm the LinkedIn/
awaiting_replycases now showawaiting_reply → fyi. Related: #218 (activated projection), #353 (todo graveyard), #354 (alarm gate — still needs its own deterministic gate; this floor doesn't touchaction_needed).🤖 Generated with Claude Code