From 8723bb942e37762ab0d75f3c6a51b143cca27b97 Mon Sep 17 00:00:00 2001 From: Jason Stedwell Date: Fri, 3 Jul 2026 23:04:35 -0500 Subject: [PATCH] fix: repair cross-skill contract gaps and harden Tracker handling (v1.1.1) - deploy: create "! Rule Needed" label on first deploy (was promised in frontmatter but never implemented; rule-review depends on it) - rule-review: accept Bootstrap-mode entry (Phase: Bootstrap, no Tracker yet) - survey: create the three artifacts before invoking handoff so handoff's sanity check no longer loops - deploy: name manage_gmail_filter params explicitly (action/criteria/filter_action) - rule-review: quote label query (label:"! Rule Needed") - deploy: renumber execution-flow block to the 7 phases the body runs - survey: frame Tracker About tab as a technical reference - import + rule-review: de-duplicate rule_id before writing to Tracker - survey/import/deploy/rule-review: document XLSX Tracker Drive round-trip - all skills: use session date only in _status.md (no fabricated clock time) Co-Authored-By: Claude Opus 4.8 --- .claude-plugin/plugin.json | 2 +- skills/deploy/SKILL.md | 49 ++++++++++++++++++++++++++++++++----- skills/handoff/SKILL.md | 2 +- skills/import/SKILL.md | 15 ++++++++++-- skills/rule-review/SKILL.md | 48 +++++++++++++++++++++++++++--------- skills/survey/SKILL.md | 33 ++++++++++++++++++++----- 6 files changed, 121 insertions(+), 28 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 2a350db..be37f08 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "gmail-inbox-architect", - "version": "1.1.0", + "version": "1.1.1", "description": "A multi-skill plugin that guides non-technical users through building a systematic Gmail classification pipeline. Claude acts as the permanent architect across multiple sessions — delegating token-heavy inbox analysis to flat-rate AI tools via structured task orders.", "author": { "name": "Message Point Media" diff --git a/skills/deploy/SKILL.md b/skills/deploy/SKILL.md index f81e225..ebc622d 100644 --- a/skills/deploy/SKILL.md +++ b/skills/deploy/SKILL.md @@ -65,12 +65,13 @@ any new Apps Script code to avoid repeating past mistakes. ## Execution Flow ``` -Phase 1 → Sanity check and queue load (what's ready to deploy) +Phase 1 → Sanity check, queue load, and ensure the ! Rule Needed label exists Phase 2 → Deploy gmail_filter_safe rules (Claude executes via Gmail connection) Phase 3 → Generate and deploy apps_script_needed rules (walkthrough) Phase 4 → Acknowledge studio_candidate rules (park, no action) Phase 5 → Update Tracker (mark deployed rules as enabled=TRUE) -Phase 6 → Update _status.md and close out +Phase 6 → Update apps_script_log.md (only if an Apps Script was deployed) +Phase 7 → Update _status.md and close out ``` Phases 2, 3, and 4 are independent — run whichever apply based on what's in the queue. @@ -100,6 +101,25 @@ Present the deployment plan: > > Want to start with the sorting rules? That's the fastest part." +### Step 1.1 — Ensure the "! Rule Needed" label exists (first deploy) + +This label is how the user flags emails that slip through so `rule-review` can build +rules for them later. It is a prerequisite of the `rule-review` skill, and this is the +skill responsible for creating it on the first deploy. + +Read the label name from Bible.md if one was customized; default is `! Rule Needed`. +Call `list_gmail_labels` and check whether it already exists (a Bootstrap-mode project +may have created it during survey — in that case, do nothing). + +If it does NOT exist, ask before creating: +> "One quick housekeeping item: I'd like to add a label called '! Rule Needed'. Going +> forward, whenever an email doesn't sort the way you'd like, just tag it with that label — +> and later I can review what you tagged and build new rules. Can I create it now?" + +Wait for an explicit "yes." If approved, call `manage_gmail_label` with `action: create` +and `name: ! Rule Needed`. If declined, note it and continue — the user can create it +manually before their first rule-review session. + --- ## Phase 2 — Deploy Gmail Filter Rules @@ -134,9 +154,12 @@ Wait for answer before proceeding. ### Step 2.3 — Create filters -For each gmail_filter_safe rule, call `manage_gmail_filter` with: +For each gmail_filter_safe rule, call `manage_gmail_filter` with three parameters: +`action` (always `"create"`), `criteria` (an object), and `filter_action` (an object). +Note the parameter is named `filter_action`, NOT `actions` — do not confuse it with the +`action: "create"` selector. -**Criteria** (build from Tracker fields): +**`criteria`** (build from Tracker fields): - `from_domain`: build as `from:domain.com` — multiple domains use OR logic: `{from:domain1.com from:domain2.com}` - `to_or_cc_domain` (participant pattern): combine as: @@ -146,11 +169,15 @@ For each gmail_filter_safe rule, call `manage_gmail_filter` with: - `has_attachment`: `has:attachment` - Combined signals: join with spaces (AND logic in Gmail search) -**Actions** (from Tracker fields): +**`filter_action`** (from Tracker fields): - `addLabelIds`: the label ID from Step 2.2 - `markAsRead`: TRUE if `mark_read = TRUE` - Skip inbox (archive): set `removeLabelIds: ["INBOX"]` if `archive = TRUE` +So a typical call is: +`action: "create"`, `criteria: { from: "domain.com", subject: "statement" }`, +`filter_action: { addLabelIds: ["Label_123"], removeLabelIds: ["INBOX"] }` + Create one filter per rule. Do not batch multiple rules into one filter — they are harder to manage and delete individually. @@ -472,6 +499,16 @@ For rules that failed deployment: For Apps Script rules deployed via script: update all rules covered by the script to `enabled = TRUE` together, since they go live as a bundle when DRY_RUN is set false. +**Google Sheet Tracker:** update rows in place with `modify_sheet_values`. + +**XLSX Tracker (round-trip required):** the file lives in Drive, so editing is not +in-place. Download the current file with `get_drive_file_content` (or the Drive +download URL), invoke the `xlsx` skill to set `enabled`/`notes` on the affected rows, +then write the updated file back to the same Drive location — overwrite the existing +file (use `update_drive_file` if available; otherwise re-create it in the same folder +and confirm the old copy is replaced, never leaving two Trackers). Verify the row count +matches before and after so no rules are dropped in the round-trip. + --- ## Phase 6 — Update apps_script_log.md @@ -516,7 +553,7 @@ the history is the point. Update `_status.md`: ``` -Last Updated: [timestamp] +Last Updated: [YYYY-MM-DD — today's date from the session; do not fabricate a clock time] Last Agent: Claude (CoWork) — deploy skill Phase: [Deploy / Maintenance — depending on whether all rules are now live] Last Completed Step: [N] gmail_filter_safe rules created. [N] apps_script_needed rules diff --git a/skills/handoff/SKILL.md b/skills/handoff/SKILL.md index 276cece..68ecefe 100644 --- a/skills/handoff/SKILL.md +++ b/skills/handoff/SKILL.md @@ -449,7 +449,7 @@ After all task order folders are created in Drive, give the user clear delivery After all task orders are generated, update `_status.md`: ``` -Last Updated: [timestamp] +Last Updated: [YYYY-MM-DD — today's date from the session; do not fabricate a clock time] Last Agent: Claude (CoWork) — handoff skill Phase: Handoff in progress Last Completed Step: [N] task order batches created in Drive Task Orders folder. diff --git a/skills/import/SKILL.md b/skills/import/SKILL.md index b582750..df1503e 100644 --- a/skills/import/SKILL.md +++ b/skills/import/SKILL.md @@ -286,6 +286,12 @@ For each note: Write all approved (and parked) rules to the Tracker after the queue is complete. +**Guarantee unique rule_ids before writing.** Read the existing `rule_id` values from the +Rules sheet first. For every rule about to be written, if its ID already exists (a +subcontractor may reuse IDs across batches, or collide with a prior import), append a +numeric suffix (`-2`, `-3`, …) until unique. Never overwrite an existing row or write a +duplicate ID. + ### Tracker Column Schema ``` @@ -325,7 +331,12 @@ Always: ### Writing to XLSX Tracker -Invoke the xlsx skill to append rows to the Rules sheet. Same field rules apply. +The file lives in Drive, so this is a round-trip, not an in-place edit. Download the +current file with `get_drive_file_content`, invoke the xlsx skill to append the rows to +the Rules sheet (same field rules apply), then upload it back to the same Drive location, +replacing the existing file (`update_drive_file` if available; otherwise re-create it in +the same folder and confirm the old copy is replaced — never leave two Trackers). Verify +the row count grew by exactly the number of rules written. ### After writing: @@ -344,7 +355,7 @@ Invoke the xlsx skill to append rows to the Rules sheet. Same field rules apply. Update `_status.md`: ``` -Last Updated: [timestamp] +Last Updated: [YYYY-MM-DD — today's date from the session; do not fabricate a clock time] Last Agent: Claude (CoWork) — import skill Phase: Rule Build [or Deploy-ready if all batches are in] Last Completed Step: [N] rules from [batch name] reviewed and loaded. diff --git a/skills/rule-review/SKILL.md b/skills/rule-review/SKILL.md index e768583..fe815aa 100644 --- a/skills/rule-review/SKILL.md +++ b/skills/rule-review/SKILL.md @@ -67,12 +67,22 @@ and that requires the user to confirm it before Claude touches anything. ## Prerequisites Before doing any other work, run Phase 1 (sanity check). This skill requires: -- `_status.md` exists with Phase = "Deploy" or "Maintenance" (first deploy must be complete) +- `_status.md` exists with Phase = "Deploy", "Maintenance", or "Bootstrap" - `Bible.md` exists with taxonomy and bylaws -- Tracker exists (Google Sheet or XLSX) -- `! Rule Needed` label exists in Gmail (created automatically by the deploy skill on first deploy) +- Tracker exists (Google Sheet or XLSX) — **except in Bootstrap mode**, where no Tracker + was created yet (see Bootstrap note below) +- `! Rule Needed` label exists in Gmail (created during the first deploy, or during survey + in Bootstrap mode) -If Phase is earlier than "Deploy": +**Bootstrap-mode entry:** A project set up in Bootstrap mode (survey with < 15 labels) +never ran deploy — its `_status.md` shows `Phase: Bootstrap` and `Bootstrap Mode: TRUE`, +and it was intentionally sent here as the return path ("review my rule needed folder"). +This is valid. If there is no Tracker yet, create one on the first rule that gets approved: +confirm the Tracker format (Google Sheet vs. XLSX) with the user per the Bible's open +question, build it using the Tracker schema at the end of this skill, then proceed. + +If Phase is "Survey complete" or "Handoff in progress" (a non-bootstrap project whose +first deploy hasn't happened yet): > "It looks like your rules haven't been deployed yet. The `! Rule Needed` label gets > created as part of the first deploy — once that's done, I can review flagged emails > with you. Want to finish the deploy first?" @@ -114,11 +124,13 @@ Phase 8 → Update _status.md and brief the user Read `_status.md` from the project Drive folder. Check: -1. Phase = "Deploy" or "Maintenance" +1. Phase = "Deploy", "Maintenance", or "Bootstrap" 2. `Bible Location` has a valid Drive URL -3. `Tracker Location` has a valid Drive URL or path +3. `Tracker Location` has a valid Drive URL or path — OR is "NONE" if `Bootstrap Mode: TRUE` + (a bootstrap project has no Tracker yet; that's expected — see Prerequisites) -If Phase is earlier than "Deploy" → redirect (see Prerequisites above). +If Phase is "Survey complete" or "Handoff in progress" (deploy hasn't run and this is not +a bootstrap project) → redirect (see Prerequisites above). Read `Bible.md` — extract: - The `! Rule Needed` label name (may have been customized; default is `! Rule Needed`) @@ -135,10 +147,13 @@ that name everywhere in this skill. Do not hardcode `! Rule Needed`. Search Gmail for all messages with the `! Rule Needed` label. ``` -search_gmail_messages query: "label:! Rule Needed" +search_gmail_messages query: label:"! Rule Needed" ``` -If the label name has spaces or special characters, quote it appropriately. +Because the label name contains a space, the label value must be quoted inside the query +(`label:"! Rule Needed"`) — an unquoted `label:! Rule Needed` will not match. Quote the +label value whenever the name has spaces or special characters, and substitute the actual +label name from Bible.md if it was customized. Read message headers only: sender, sender domain, subject, date received, current labels. Do NOT read message bodies — headers are sufficient for pattern identification. @@ -342,11 +357,20 @@ complexity threshold not exceeded, user confirmed the rule. Add the rule to the Tracker with enabled=FALSE. +**First, guarantee a unique rule_id.** Read the existing `rule_id` values from the Rules +sheet before writing. If the ID you generated already exists, append a numeric suffix +(`-2`, `-3`, …) until it is unique. Two sessions can easily generate the same ID +(e.g. `GF-VENDOR-RR-FEDEX`) for different rules — never overwrite or silently collide. + For a Google Sheet Tracker: Call `append_table_rows` or `modify_sheet_values` to add the row to the Rules sheet. -For an XLSX Tracker: -Invoke the xlsx skill to append the row. +For an XLSX Tracker (round-trip required — the file lives in Drive, not locally): +Download the current file with `get_drive_file_content`, invoke the xlsx skill to append +the row to the Rules sheet, then upload it back to the same Drive location, replacing the +existing file (`update_drive_file` if available; otherwise re-create in the same folder and +confirm the old copy is replaced — never leave two Trackers). Verify the row count grew by +exactly the number of rules added. Rule ID format for rule-review-authored rules: ``` @@ -462,7 +486,7 @@ After completing: Update `_status.md`: ``` -Last Updated: [timestamp] +Last Updated: [YYYY-MM-DD — today's date from the session; do not fabricate a clock time] Last Agent: Claude (CoWork) — rule-review skill Phase: Maintenance Last Completed Step: Rule review session. [N] clusters reviewed. diff --git a/skills/survey/SKILL.md b/skills/survey/SKILL.md index 48ea0ce..1155a83 100644 --- a/skills/survey/SKILL.md +++ b/skills/survey/SKILL.md @@ -473,7 +473,7 @@ mpmedia.tv, messagepoint.tv, messagepointmedia.com, messagepoint.media, mpm.to ```markdown # Gmail Inbox Architect — Project Status -**Last Updated:** [YYYY-MM-DD HH:MM UTC] +**Last Updated:** [YYYY-MM-DD — today's date from the session; do not fabricate a clock time] **Last Agent:** Claude (CoWork) — survey skill (bootstrap mode) **Phase:** Bootstrap **Last Completed Step:** Bootstrap survey complete. Domain analysis run. Taxonomy v1 preliminary. @@ -704,7 +704,16 @@ If handoff threshold is met: > flat-rate tool'] for the heavy analysis — they can walk through the whole thing, > then bring the results back to me for review. Want me to set that up?" -Then invoke the `handoff` skill with the current project context. +**Before invoking handoff, complete Phase 5 first.** The `handoff` skill's sanity check +requires that Bible.md, the Tracker, and `_status.md` already exist in the project folder — +if you invoke it before those artifacts are created, its Phase 1 check fails and bounces +back here in a loop. So when the handoff threshold is met: +1. Skip the Step 4.2 sampling. +2. Run **Phase 5** now to create all three artifacts (write `_status.md` with + `Phase: Survey complete` and note the pending handoff). +3. Then invoke the `handoff` skill with the current project context. + +Phase 6 close-out then covers the handoff delivery instructions. **If sampling is appropriate (under 20 labels, manageable message counts):** @@ -859,7 +868,11 @@ Create two sheets: - Sheet 2 named **About** — add this text: ``` This tracker is the source of truth for all Gmail classification rules. - + + (This tab is a technical reference for the column names below — you never + need to edit it. The plain-English view of your rules is what we walk through + together in each session.) + Columns: rule_id — Unique rule identifier enabled — TRUE/FALSE: whether the rule is active @@ -880,11 +893,19 @@ Create two sheets: ``` **If XLSX path:** -Invoke the `xlsx` skill to create `Tracker.xlsx` in the project folder with: +Invoke the `xlsx` skill to create `Tracker.xlsx` locally with: - Sheet 1 named "Rules" with the header row from the schema above - Sheet 2 named "About" with the column descriptions above Do NOT pre-populate any data rows. +The `xlsx` skill writes the file to the local workspace, not to Drive. After it's created, +upload it into the project Drive folder so every session and tool can reach it: call +`create_drive_file` with `file_name: Tracker.xlsx`, the project `folder_id`, and the local +file passed via `fileUrl` (e.g. `file:///path/to/Tracker.xlsx`) with +`mime_type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`. +Record the resulting Drive URL as the Tracker Location — subsequent skills read and +round-trip the file from there. + After creating, share the link: > "Your Tracker is ready — [link]. It's empty right now, which is exactly right. > Rules will be added here as task orders come back, and you'll review each one @@ -899,7 +920,7 @@ Call `create_drive_file` in the project folder with this content: ```markdown # Gmail Inbox Architect — Project Status -**Last Updated:** [YYYY-MM-DD HH:MM UTC] +**Last Updated:** [YYYY-MM-DD — today's date from the session; do not fabricate a clock time] **Last Agent:** Claude (CoWork) — survey skill **Phase:** Survey complete **Last Completed Step:** Survey complete. Taxonomy v1 drafted. Bible.md, Tracker, and _status.md created. @@ -1014,7 +1035,7 @@ Documentation: Technical documents, engineering files, signed contracts, specs For Claude's reference when populating the status file: ``` -Last Updated: Timestamp this file was written — YYYY-MM-DD HH:MM UTC +Last Updated: Date this file was written — YYYY-MM-DD (today's session date; no fabricated clock time) Last Agent: Name/tool that last updated this file Phase: Survey / Bootstrap / Analysis / Rule Build / Deploy / Review / Maintenance Last Completed Step: Plain-English description of what just finished