| name | customer-project-watcher |
| description | Set up a recurring background watcher for an Orderful onboarding customer that polls Slack, Gmail, the Orderful org, and Salesforce on a cadence and posts a concise digest back to the current chat whenever new activity is found. Use this whenever a user wants to stay on top of a specific customer's onboarding project without having to ask for updates — triggers include 'watch [customer] for updates', 'set up a watcher for [customer]', 'poll [customer] every hour', 'keep an eye on [customer]', 'background monitor for [customer]', 'don't let me miss anything on [customer]', or 'alert me when something changes on [customer]'. The skill resolves the customer's identifiers across systems, gathers the project stakeholder list, composes a self-contained polling prompt, and schedules it via the scheduled-tasks MCP. Designed for proactive project management — silent on quiet hours, vocal when there's signal. |
Customer Project Watcher
What this skill does
Creates a recurring scheduled task that monitors an Orderful onboarding customer's project across Slack, Gmail, the Orderful platform, and Salesforce — and posts a grouped digest back to the originating chat whenever new activity is detected. Silent on quiet hours by design.
The output is a scheduled task, not a one-shot run. Once created, it runs autonomously on the cadence the user specifies, posting updates here as they come.
When this fires
Use this whenever the user wants ongoing visibility into a specific customer's onboarding without having to ask for updates. Typical triggers:
- "Watch [customer] for any updates"
- "Set up a watcher / poller / monitor for [customer]"
- "Poll Slack and email for [customer] every hour"
- "Keep an eye on [customer]'s onboarding"
- "Don't let me miss anything on [customer]"
- "Background monitor for [customer]"
Don't fire this for one-shot status checks ("what's new on [customer]?") — those should run the polling logic inline once. This skill is specifically for setting up a recurring task.
Inputs to gather
Before creating the task, resolve:
-
Customer name — required. If missing, ask before doing anything else.
-
Cadence — how often the watcher should run. Common options: hourly (0 * * * *), every 4 hours (0 */4 * * *), daily at 8 AM (0 8 * * *), weekdays at 9 AM (0 9 * * 1-5). If the user doesn't specify, suggest hourly as the default and confirm.
-
Customer identifiers — pull in parallel:
- Salesforce Account ID —
soqlQuery SELECT Id, Name, OwnerId, Type FROM Account WHERE Name LIKE '%<customer>%'. Account must be Type = 'Customer'. Stop if not found.
- Orderful org ID + ISA —
search_organizations by name. Capture organizationId, organizationIsaId.
- Project Slack channel —
slack_search_channels for the customer's MTS or onboarding channel (usually #<customer>-mts-project or #<customer>-onboarding). Capture the channel ID.
- Customer email domain — pull from the Account Website field or any Contact email. This drives the Gmail filter.
-
Stakeholders — resolve emails and Slack user IDs:
- Customer-side primary contact — pull from SFDC Contacts on the Account.
- Customer-side additional contacts — pull all Contacts on the Account.
- Orderful AE — Account Owner from SFDC.
- Orderful AM — usually a different person than the AE; ask if not obvious from the Account or recent Slack activity.
- Orderful technical driver — the CEA or onboarding engineer driving this project.
- Anyone else the user names — SI contacts, integration partners, etc.
For each Orderful person, run slack_search_users to get their @orderful.com email and Slack user ID.
Don't invent emails. If a stakeholder's email isn't in any system, leave them off and note this to the user.
Composing the polling prompt
The prompt that gets stored on the scheduled task must be fully self-contained. Future runs have no memory of this session. Bake in:
- Customer name + all identifiers (SFDC Account ID, Orderful org ID, ISA, project Slack channel ID, customer email domain)
- Full stakeholder roster with names + emails
- All five source checks (see below)
- Digest format with explicit silence-on-quiet rule
- Noise-vs-signal filtering rules
Sources to check (run in parallel each run)
-
Project Slack channel — slack_read_channel on the project channel ID, limit ~20. Filter to messages with timestamps in the past <cadence> window that are NOT from the originating user themselves and NOT from known bots (e.g., Jarvis).
-
Global Slack — slack_search_public_and_private for keywords matching the customer (name, domain, primary contact names) excluding the project channel and excluding the originating user. Window: past <cadence>.
-
Gmail — search_threads filtering by:
from:<customer-domain> OR to:<customer-domain> OR mentions of customer name in subject
newer_than:<cadence> (e.g., newer_than:1h)
Pull each thread's most recent message. Skip threads where the only new message is from the originating user.
-
Orderful platform — get_account_context on the org ID (captures partnerships + comm channels + EDI accounts) and list_trade_requests on the org ID. Compare against prior state (the prompt can describe expected baseline state so the agent knows what counts as "new"). Surface:
- New trade requests (created in window)
- Trade request status changes (PENDING → ACCEPTED, etc.)
- Partnership step changes (Setup → Testing → GoLive)
- New communication channels or EDI accounts
-
Salesforce — two SOQL queries (use TODAY or explicit datetime — do not use LAST_N_HOURS, it's not valid SOQL):
SELECT Id, Name, StageName, Amount, LastModifiedDate FROM Opportunity WHERE AccountId = '<id>' AND LastModifiedDate = TODAY
SELECT Id, Name, Email, Title, CreatedDate FROM Contact WHERE AccountId = '<id>' AND CreatedDate = TODAY
Digest format
<Customer> watcher — <YYYY-MM-DD HH:MM local time>
🟢 New since last check:
**<Source emoji + label>:** <one-line summary>
- <Detail bullet 1>
- <Detail bullet 2 with link>
**<Next source>:** <summary>
- ...
Group by source. Bold the source headline. Use bullets for specifics. Always include permalinks where available (Slack message URLs, Gmail thread URLs, SFDC record URLs).
When relevant, end with suggested next moves — 1–3 concrete actions the user might take based on what was found. Don't pad; only include if there's a clear next step.
Silence on quiet hours
If none of the five sources turned up genuine new activity, post nothing. No "no updates," no "all clear." Silence is the signal that nothing's changed.
Track open asks → flag when replies close them
If recent outbound emails to the customer had a numbered ask list (e.g. the onboarding-outreach-email skill structures them this way), the polling prompt should track which asks are still open. When a customer or stakeholder replies:
- Identify any explicit answers in the reply ("we're new to EDI" → closes the EDI-systems ask; "I'm looping in our NetSuite team" → closes the team-loop-in ask; an ISA ID or vendor number → closes the partner-identifier ask).
- Surface in the digest both the new activity AND a "now closed" / "still outstanding" status against the open ask list.
- If all asks are closed, say so explicitly — that's a signal the project is unblocked and the next configuration step can start.
When composing the polling prompt for create_scheduled_task, include the current open-ask list inline so future runs have it as context. If the open-ask list changes (new outbound goes out, or asks close), update the scheduled task via update_scheduled_task rather than letting the prompt go stale.
What counts as "new" vs. noise
Surface:
- Any Slack message in the project channel from anyone other than the originating user or known bots
- Any Slack message anywhere mentioning the customer / domain / primary contacts from anyone other than the originating user
- Any Gmail thread to/from the customer domain or naming the customer in subject, where the latest message is NOT from the originating user
- Any new partnership, trade request, or partnership step change in Orderful
- Any new SFDC Contact or Opportunity stage/amount change
Skip (noise):
- The originating user's own posts and emails
- Automated bot messages (e.g., Slack apps,
Jarvis user IDs)
- Duplicate hits across sources (mention once with cross-references)
When in doubt, surface it. A slightly noisy digest is better than a missed signal.
Creating the scheduled task
Call mcp__scheduled-tasks__create_scheduled_task with:
taskId — kebab-case, e.g. <customer-slug>-watcher (e.g., day-dream-watcher, acme-corp-watcher)
description — one-line summary, e.g. "Hourly watcher for onboarding — surfaces new Slack messages, emails, Salesforce changes, or Orderful partnership activity."
cronExpression — the agreed cadence
notifyOnCompletion: true — critical; this routes each run's output back to the originating chat
prompt — the full self-contained polling prompt described above
After creating, tell the user:
- The task is live, with the cadence and next run time.
- Recommend they click "Run now" the first time to pre-approve the tool grants (Slack, Gmail, Salesforce, Orderful MCP). Once granted, future runs won't pause on permission prompts.
- How to pause/edit/kill the task (Scheduled section in the Cowork sidebar).
Multi-customer watchers
If the user wants to track multiple customers, create one task per customer rather than a multi-customer mega-task. Reasons:
- Easier to pause/kill one customer's watcher independently
- Each digest is focused (skimmable in 10 seconds vs. 30)
- Less risk of one customer's noise drowning another's signal
If they want a single weekly portfolio summary across multiple customers, that's a different ask — point them at weekly-customer-progress-update (one customer at a time) or suggest building a separate portfolio-roll-up skill.
Anti-patterns to avoid
- Don't use
LAST_N_HOURS in SOQL. It's not a valid SOQL date literal. Use TODAY, YESTERDAY, LAST_N_DAYS:n, or explicit ISO datetime comparisons.
- Don't post empty digests. "Nothing new" messages are noise that train the user to tune the watcher out.
- Don't hardcode emails or IDs you didn't verify. If you couldn't resolve a stakeholder's email or Slack ID, omit them and note the gap to the user.
- **Don'