diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-27 10:48:12 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-27 10:48:12 -0500 |
| commit | 6c1ea8bbc3775fe7c481f41b4f30e0c0110a9339 (patch) | |
| tree | d8607d3c7abaaa901678c89ad4e45cbc1eaca739 /inbox/PROCESSED-2026-07-08-1124-from-work-triage-intake.personal-gmail.org | |
| parent | f2609d9f9ad33486bef43211d753ba53e1e24181 (diff) | |
| download | rulesets-6c1ea8bbc3775fe7c481f41b4f30e0c0110a9339.tar.gz rulesets-6c1ea8bbc3775fe7c481f41b4f30e0c0110a9339.zip | |
feat: add peer-reasoning contract and fix two silent probe defects
I added a Collaborative Peer Reasoning section to the interaction rules. It governs how an interpretation forms before any rule about presenting choices: infer first and clarify only at material forks, test a conclusion against its strongest alternative, let a correction update the downstream model instead of just the wording. The file's framing line widened to match.
Two probes were failing silently. The startup KB nudge looked up the best-practices node by grepping file content for its slug. A roam node's slug lives in its filename, so the lookup always returned empty. The nudge pointed at nothing in every project and every session, for as long as it shipped. It matches the filename now, through find rather than a glob so zsh doesn't abort on no match.
The browser rule told agents to open URLs with a form ending in &>/dev/null &. That discards the "Opening in existing browser session." line confirming the tab opened. Warm and cold start now split: foreground and read the confirmation when Chrome is already up, detach only when it isn't. The confirmation is on stdout, verified rather than assumed.
I filed two tasks from handoffs. The sentry triage split needs a work-vs-personal classification mechanism before its wording can move, because the current rule excludes by category and category can't express that split. The publish-lock design is approved but carries three open gaps. The load-bearing one is a lock held across an unbounded human approval pause.
I also swept the old processed handoffs out of inbox/. History keeps them.
Diffstat (limited to 'inbox/PROCESSED-2026-07-08-1124-from-work-triage-intake.personal-gmail.org')
| -rw-r--r-- | inbox/PROCESSED-2026-07-08-1124-from-work-triage-intake.personal-gmail.org | 68 |
1 files changed, 0 insertions, 68 deletions
diff --git a/inbox/PROCESSED-2026-07-08-1124-from-work-triage-intake.personal-gmail.org b/inbox/PROCESSED-2026-07-08-1124-from-work-triage-intake.personal-gmail.org deleted file mode 100644 index ca81f5d..0000000 --- a/inbox/PROCESSED-2026-07-08-1124-from-work-triage-intake.personal-gmail.org +++ /dev/null @@ -1,68 +0,0 @@ -#+TITLE: Triage Intake — Personal Gmail Source -#+AUTHOR: Craig Jennings & Claude -#+DATE: 2026-05-26 - -# Source plugin for the triage-intake engine. See triage-intake.org for the -# contract and the Phase A-D orchestration. This file declares ONE source. - -* Source: personal-gmail -:PROPERTIES: -:ORDER: 20 -:ENABLED: mcp google-docs-personal present -:ANCHOR: epoch -:SUBAGENT_OVER: 50 -:END: - -** Scan - -Personal Gmail unread in the inbox since the anchor: - -#+begin_src text -mcp__google-docs-personal__listMessages q="is:unread in:inbox after:<anchor-epoch>" maxResults=100 -#+end_src - -⚠ *Express the cutoff as the literal UNIX epoch* — =after:1778856990=, not =after:YYYY/MM/DD=. Gmail's =after:YYYY/MM/DD= operator only supports day resolution; the =YYYY/MM/DD HH:MM:SS= form is NOT valid syntax — Gmail parses the space as a term separator, treats =HH:MM:SS= as a search term that never matches, and returns 0 results, silently masking unread mail. The engine supplies =<anchor-epoch>= because this source declares =ANCHOR: epoch=. - -⚠ *Do NOT add =-category:promotions -category:social=.* That filter masked 67 promo+social messages across two runs (2026-05-04, 2026-05-06), both needing a follow-up sweep. Pull the full unfiltered set; the trash-leaning bias in Classify handles promotions and social directly. - -⚠ *The MCP caps at =maxResults=100= and exposes NO =pageToken= parameter.* The response carries a =nextPageToken=, but the tool can't consume it, so a pile over 100 is silently truncated — the tail below the cap never gets classified, and every later anchored sweep skips it (it predates the new anchor). This is exactly how a 300+ backlog accumulated invisibly by 2026-07-08. Two consequences: - -- *Never treat a 100-row result as complete.* When a scan returns exactly 100, walk the tail in *date slices*: re-query with =before:<oldest-full-day-seen>= (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:<anchor-YYYY/MM/DD>" 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. - -- *Noise-trash:* newsletters, Substacks, retail/SaaS marketing, social digests, redundant aggregator digests (Notion/Miro daily), wrong-recipient mail, past-event calendar artifacts. -- *Noise-keep:* receipts, order confirmations, statements — low value but worth the audit trail. -- *FYI:* substantive personal mail with no action owed. -- *Action:* an explicit ask, a reply owed, a time-sensitive personal matter. - -** Render - -#+begin_example -**Personal Gmail — N unread.** <one-line classification summary> -- Action: <items, if any, with thread links> -- FYI: <items, if any> -- Noise: N trash candidates, M keep -#+end_example - -Omit the block if zero unread. - -** Actions - -- trash :: =mcp__google-docs-personal__trashMessage= id=<message-id> (recoverable from Gmail Trash for 30 days) -- mark-read :: =mcp__google-docs-personal__modifyMessageLabels= id=<message-id> removeLabelIds=["UNREAD"] -- star+read :: =mcp__google-docs-personal__modifyMessageLabels= id=<message-id> addLabelIds=["STARRED"] removeLabelIds=["UNREAD"] -- attach-fetch:: =.ai/scripts/gmail-fetch-attachments.py --profile personal --message-id <message-id> --output-dir <PATH>= |
