From 81ca16f9b516112ae17727a09b8b36538f9db21d Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 11 Jul 2026 01:56:03 -0500 Subject: fix(triage-intake): guard personal-gmail sweeps against the Gmail 100-cap The google-docs MCP listMessages tool caps at maxResults=100 and exposes no pageToken, so an unread pile over 100 truncates silently and every later anchored sweep skips the tail below the cap. That's how a 300+ backlog built up unseen by 2026-07-08. Two Scan-section rules close it: when a scan returns exactly 100, walk the tail in date slices and dedupe by id, and never report resultSizeEstimate as a count. A cheap backlog-residue probe each sweep (before:, maxResults=5) surfaces pre-anchor unread loudly, so an anchored "no changes" can't mask a window the scan never saw. personal-gmail is the only gmail-family plugin here, so it's the only file that changed. --- .ai/workflows/triage-intake.personal-gmail.org | 15 +++++++++++++++ .../.ai/workflows/triage-intake.personal-gmail.org | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/.ai/workflows/triage-intake.personal-gmail.org b/.ai/workflows/triage-intake.personal-gmail.org index a7af333..7d1ab4d 100644 --- a/.ai/workflows/triage-intake.personal-gmail.org +++ b/.ai/workflows/triage-intake.personal-gmail.org @@ -25,6 +25,21 @@ mcp__google-docs-personal__listMessages q="is:unread in:inbox after:= (day resolution), repeat until a page returns fewer than 100, dedupe by message id across slices (the day-resolution boundary overlaps). +- *Never report =resultSizeEstimate= as a count.* It's unreliable — observed stuck at "201" across three different queries whose real union exceeded 300. + +*** Backlog-residue check (every sweep — cheap, mandatory) + +The anchored scan is blind to anything unread from *before* the anchor. After it, run one probe for pre-anchor residue: + +#+begin_src text +mcp__google-docs-personal__listMessages q="is:unread in:inbox before:" maxResults=5 +#+end_src + +If it returns any messages, surface one loud line in the sweep summary: "Backlog: unread predating the anchor exists (N+ shown; date-slice to inventory)" and offer a backlog sweep. Never fold the residue into a quiet sweep — an anchored "no changes" claim is only true for the window the scan saw. (Added 2026-07-08 after ~300 pre-anchor unread accumulated unseen; the probe returns actual messages, so it works where the estimate lies.) + ** Classify Bias: *trash-leaning* — personal Gmail is high noise volume. diff --git a/claude-templates/.ai/workflows/triage-intake.personal-gmail.org b/claude-templates/.ai/workflows/triage-intake.personal-gmail.org index a7af333..7d1ab4d 100644 --- a/claude-templates/.ai/workflows/triage-intake.personal-gmail.org +++ b/claude-templates/.ai/workflows/triage-intake.personal-gmail.org @@ -25,6 +25,21 @@ mcp__google-docs-personal__listMessages q="is:unread in:inbox after:= (day resolution), repeat until a page returns fewer than 100, dedupe by message id across slices (the day-resolution boundary overlaps). +- *Never report =resultSizeEstimate= as a count.* It's unreliable — observed stuck at "201" across three different queries whose real union exceeded 300. + +*** Backlog-residue check (every sweep — cheap, mandatory) + +The anchored scan is blind to anything unread from *before* the anchor. After it, run one probe for pre-anchor residue: + +#+begin_src text +mcp__google-docs-personal__listMessages q="is:unread in:inbox before:" maxResults=5 +#+end_src + +If it returns any messages, surface one loud line in the sweep summary: "Backlog: unread predating the anchor exists (N+ shown; date-slice to inventory)" and offer a backlog sweep. Never fold the residue into a quiet sweep — an anchored "no changes" claim is only true for the window the scan saw. (Added 2026-07-08 after ~300 pre-anchor unread accumulated unseen; the probe returns actual messages, so it works where the estimate lies.) + ** Classify Bias: *trash-leaning* — personal Gmail is high noise volume. -- cgit v1.2.3