diff --git a/.use-case-library/catalog.json b/.use-case-library/catalog.json index 1763197..121953a 100644 --- a/.use-case-library/catalog.json +++ b/.use-case-library/catalog.json @@ -15,12 +15,13 @@ "import-from-ai", "brand-audit", "paid-strategy-audit", - "creative-deep-dive" + "creative-deep-dive", + "usage-efficiency-audit" ], "excluded": [ { "slug": "ugc-creator-programme", - "reason": "Extracted to a directory; still excluded — SQLite-backed, like the other stateful apps. Promote when ready." + "reason": "Extracted to a directory; still excluded \u2014 SQLite-backed, like the other stateful apps. Promote when ready." }, { "slug": "corpus-search", @@ -72,15 +73,15 @@ }, { "slug": "review-library", - "reason": "Install-eval data shows 1/7 installs pass (14% pass, 14% value rate) — hits the hide-recommendation threshold (≥5 installs, <30% value). Hide while we investigate and rework. See /agent/brain/rax/team/kyra/use-case-migration-plan.md (bucket 2)." + "reason": "Install-eval data shows 1/7 installs pass (14% pass, 14% value rate) \u2014 hits the hide-recommendation threshold (\u22655 installs, <30% value). Hide while we investigate and rework. See /agent/brain/rax/team/kyra/use-case-migration-plan.md (bucket 2)." }, { "slug": "weekly-performance-deck", - "reason": "install-config.json auto-runs app-create + app-build + reminder-add at install — three v2-schema violations. 1/1 install in the eval window failed. Hide until bucket 3 of the v2 migration ships a clean customize-weekly-performance-deck flow." + "reason": "install-config.json auto-runs app-create + app-build + reminder-add at install \u2014 three v2-schema violations. 1/1 install in the eval window failed. Hide until bucket 3 of the v2 migration ships a clean customize-weekly-performance-deck flow." }, { "slug": "building-integrations", - "reason": "Being removed from the use case library entirely — moving into runneth-volume as part of the agent's standing capability. Hide preemptively." + "reason": "Being removed from the use case library entirely \u2014 moving into runneth-volume as part of the agent's standing capability. Hide preemptively." }, { "slug": "performance-bundle", diff --git a/usage-efficiency-audit/README.md b/usage-efficiency-audit/README.md new file mode 100644 index 0000000..ed3cfd4 --- /dev/null +++ b/usage-efficiency-audit/README.md @@ -0,0 +1,25 @@ +# usage-efficiency-audit + +Watcher and audit. Daily check on whether a workspace has crossed 80% of its included Runneth plan for the current billing cycle. When it has, runs a personalized efficiency audit on the team's actual conversation history, generates an openable HTML page with concrete recommendations, and posts a friend-voice heads-up to Slack with the link. Fires once per cycle. + +## What it does + +- Reads `/agent/brain/usage-efficiency/.json` for config +- Computes the current billing cycle window from the saved anniversary day +- Pulls cycle-to-date cost from the local Postgres conversation store +- Compares to the configured threshold (80% of the included plan by default) +- If the threshold is crossed and the cycle hasn't been notified yet, runs pattern detection on the team's real conversations, builds an openable HTML page, and posts to Slack +- Marks the cycle as notified so it stays quiet until the next cycle + +## Customer-facing copy guardrails + +The customer never sees dollar figures, internal pricing tiers, or scarcity language. The audit speaks in percentage of plan used, days until reset, and (when appropriate) "the next tier 3x's your usage." + +## Files + +- `SKILL.md` — the main audit + watcher skill +- `setup-usage-efficiency-audit/SKILL.md` — one-time setup and reconfigure +- `install-config.json` — install schema for the use case library +- `marketing.md` — library detail page copy +- `use-case.json` — library manifest +- `post-install-intro.md` — first-message shown after install completes diff --git a/usage-efficiency-audit/SKILL.md b/usage-efficiency-audit/SKILL.md new file mode 100755 index 0000000..cd5c3d9 --- /dev/null +++ b/usage-efficiency-audit/SKILL.md @@ -0,0 +1,324 @@ +--- +name: usage-efficiency-audit +description: | + Watcher + audit. Daily check on whether a workspace has crossed 80% of its included Runneth usage for the current billing cycle. When it has, runs a personalized efficiency audit on the org's actual conversation history, generates an openable HTML page with concrete recommendations, and posts a friendly heads-up to Slack with the link. Frames as a friend helping the team get more out of every conversation. Only fires once per cycle. Triggers on the daily watcher reminder, on "run usage-efficiency-audit", "check usage", "audit my usage", "am I close to my limit", or as a follow-up from setup. + +triggers: + phrases: + - "run usage-efficiency-audit" + - "check usage" + - "audit my usage" + - "usage efficiency audit" + - "am I close to my limit" + - "how am I tracking on usage" + intent: "User wants to evaluate current cycle usage and/or produce the efficiency audit" + excludes: + - "set up usage-efficiency-audit" + - "reconfigure usage-efficiency-audit" +--- + +# Usage Efficiency Audit + +A friend-voice efficiency audit that fires once per billing cycle when a customer crosses 80% of their included Runneth usage. The audit is personalized to the team's actual conversation history and the customer never sees dollar amounts or internal pricing. + +## Core principles + +- **Friend voice, not scarcity.** Frame every recommendation as "here's how to get more out of each conversation," never "you're running out." +- **No dollars, ever.** The customer sees percentage of plan used, days until reset, and "3x your usage" if the upgrade nudge fires. They never see $100, $300, $500, or any cost figure. The audit body must not include cost numbers anywhere. +- **Real evidence.** Every recommendation cites the specific conversations it came from. Generic best-practice tips are weaker than "I noticed you re-explained your brand voice in three separate conversations." +- **One ping per cycle.** Once notified for a cycle, stay quiet until the next cycle starts. + +--- + +## Phase 1 — Load config and compute current cycle + +### 1a. Read config + +Read `/agent/brain/usage-efficiency/.json`. If the file is missing, halt and invoke `setup-usage-efficiency-audit`: + +> The usage audit isn't configured yet for this workspace. Let me set it up first. + +Extract: `organizationId`, `billingAnniversaryDay`, `slackChannelId`, `slackUserTags`, `includedLimitUsd`, `thresholdPercent`, `upgradeMultiplier`, `lastNotifiedCycleStart`. + +### 1b. Compute the current cycle window + +Today's date is the reference. The cycle starts on the most recent occurrence of `billingAnniversaryDay`. If the anniversary day does not exist in a given month (e.g. day 31 in April, or day 29 in non-leap February), clamp to the last day of that month. This matches how Stripe schedules monthly billing. + +```python +from datetime import date, timedelta +import calendar + +def safe_anniversary(year, month, anchor_day): + last_day = calendar.monthrange(year, month)[1] + return date(year, month, min(anchor_day, last_day)) + +today = date.today() +anchor_day = config["billingAnniversaryDay"] # 1-31 + +# Effective anchor for this month (clamped if month is short) +this_month_anchor = safe_anniversary(today.year, today.month, anchor_day) + +if today >= this_month_anchor: + cycle_start = this_month_anchor +else: + # previous month + if today.month == 1: + prev_year, prev_month = today.year - 1, 12 + else: + prev_year, prev_month = today.year, today.month - 1 + cycle_start = safe_anniversary(prev_year, prev_month, anchor_day) + +# Next anniversary (cycle end is the day before it) +if cycle_start.month == 12: + next_year, next_month = cycle_start.year + 1, 1 +else: + next_year, next_month = cycle_start.year, cycle_start.month + 1 +next_anniversary = safe_anniversary(next_year, next_month, anchor_day) + +reset_date = next_anniversary +cycle_end = reset_date - timedelta(days=1) +days_until_reset = (reset_date - today).days +``` + +Store as `CYCLE_START`, `CYCLE_END`, `RESET_DATE`, `DAYS_UNTIL_RESET`. + +--- + +## Phase 2 — Pull cycle-to-date usage from local Postgres + +The customer sandbox's local Postgres has every message with cost. We query that, not Motion's internal BQ. + +```sql +SELECT + COALESCE(SUM(m.total_cost_usd), 0) AS cost_to_date, + COUNT(DISTINCT c.id) AS conversation_count, + COUNT(m.id) AS message_count, + COUNT(DISTINCT c.user_email) AS active_users +FROM agent_message m +JOIN agent_conversation c ON c.id = m.conversation_id +WHERE c.organization_id = $1 + AND m.created_date >= $2 -- CYCLE_START + AND m.created_date < $3; -- RESET_DATE +``` + +Use `secret run --env NEON_DATABASE_URL=NEON_DATABASE_URL -- psql "$NEON_DATABASE_URL" ...` to execute. + +Compute: +``` +percent_used = (cost_to_date / includedLimitUsd) * 100 +``` + +Round to nearest whole percent for customer display. + +--- + +## Phase 3 — Decide whether to fire + +Two gates: + +**Gate 1 — Threshold.** If `percent_used < thresholdPercent`, exit silently. Log to a daily run log only. + +**Gate 2 — Already notified this cycle.** If `lastNotifiedCycleStart == CYCLE_START.isoformat()`, exit silently — we already told them this cycle. + +If both gates pass, continue to Phase 4. + +**Manual invocation override.** When the user invokes the skill directly with phrases like "audit my usage" or "check usage," skip both gates and run the full audit regardless. They asked for it explicitly. Don't update `lastNotifiedCycleStart` in this case — manual runs don't count as the cycle notification. + +--- + +## Phase 4 — Pull conversation content for pattern detection + +Pull every conversation from the cycle window, with messages. + +```sql +SELECT + c.id, c.title, c.user_email, c.created_date, + m.sequence_number, m.role, m.parts->0->>'text' AS text, m.created_date AS message_date +FROM agent_conversation c +JOIN agent_message m ON m.conversation_id = c.id +WHERE c.organization_id = $1 + AND c.created_date >= $2 + AND c.created_date < $3 +ORDER BY c.created_date, m.sequence_number; +``` + +Group by conversation. Each conversation becomes a record with id, title, user_email, created_date, and an ordered list of `{role, text}` turns. + +Cap total content sent into pattern detection at roughly 200K tokens — sample the most recent conversations first, then by user diversity (ensure each active user is represented). + +--- + +## Phase 5 — Pattern detection + +Use Claude (via `ANTHROPIC_API_KEY`) to scan the conversations and surface efficiency patterns. The output is structured JSON. + +### 5a. The detection prompt + +Send Claude: +- The full conversation corpus from Phase 4 +- A list of skills available in this workspace (from `ls /agent/.agents/skills/`) +- A list of routines configured (from `reminder list`) +- A list of durable knowledge files (from `/agent/INDEX.md` if present) + +Ask it to return JSON with detected patterns in these categories: + +```json +{ + "patterns": [ + { + "category": "repeated_context", + "title": "You re-introduced your brand voice across multiple conversations", + "evidence": [ + {"conversation_id": "...", "title": "...", "snippet": "..."}, + {"conversation_id": "...", "title": "...", "snippet": "..."} + ], + "estimated_impact": "high|medium|low", + "estimated_messages_saved": 6, + "recommendation": "One-paragraph concrete suggestion in friend voice. No cost figures." + } + ], + "wins": [ + "Specific thing the team is doing well, in one sentence" + ] +} +``` + +Categories to look for: +- `repeated_context` — same context block re-pasted across 3+ conversations +- `repeating_workflow` — same kind of task asked repeatedly (could be a routine) +- `skill_mismatch` — task matched an available skill but the skill wasn't invoked +- `over_scoped_thread` — a conversation ran very long because the ask was vague +- `re_derived_knowledge` — the team re-solved a problem already documented in their brain +- `wins` — things the team is already doing well (always include 2-3 here) + +### 5b. Parsing and ranking + +Rank patterns by `estimated_impact` (high > medium > low), break ties by `estimated_messages_saved`. Take the top 5 patterns for the artifact. Always include 2-3 wins regardless of count. + +If Claude returns fewer than 3 patterns or the corpus is thin (< 20 conversations in cycle), fall back to a lighter audit: skip the personalized patterns section, lead with the reset notice and one or two generic best-practice nudges chosen from a small built-in list (saving brand context, creating routines for repeated work, using skills for matching tasks). + +--- + +## Phase 6 — Build the HTML artifact as an app + +The artifact is an openable page. Same pattern as competitor-intel. + +**App name:** `usage-audit-{WORKSPACE_SLUG}` + +### 6a. Page structure + +Read `/runneth/references/html-generation--design-system.md` for the design system. + +Sections (top to bottom): +1. **Header.** "Getting more out of Runneth" — workspace name and reset date. +2. **At-a-glance.** Percent used (no dollar figure), days until reset, number of conversations this cycle, number of active teammates. +3. **TL;DR.** Top 3 highest-leverage changes as one-liners with anchor links to the detail below. +4. **The patterns.** One section per detected pattern: title, what I noticed, evidence (links to actual conversations in the customer's app), what to try instead. Friend voice throughout. +5. **What you're already doing well.** The wins from Phase 5b. +6. **If your volume just runs bigger than this plan.** One sentence: "If these changes don't cover the gap, the next tier 3x's your usage." No dollar figures. Show this section only if usage trajectory suggests they'd still run over after applying the recommendations (rough heuristic: if percent_used at current pace would exceed 110% by cycle end, include the section; otherwise omit). + +### 6b. Conversation link format + +Each evidence link goes to the customer's own app conversation URL: + +``` +https://projects.motionapp.com/organization/{organizationId}/{workspaceId}/chat/{conversationId} +``` + +Pull `workspaceId` from config or from `motion workspaces`. + +### 6c. Build and verify + +```bash +app list # check if already exists +# if exists: overwrite source, rebuild +# if not: app create usage-audit-{WORKSPACE_SLUG} +app build usage-audit-{WORKSPACE_SLUG} +app verify usage-audit-{WORKSPACE_SLUG} +``` + +Capture the verified public URL as `APP_URL`. Build the URL from `$SPAWNETH_HOST` plus the verified route, never `build.runneth.com`. + +--- + +## Phase 7 — Post to Slack + +Parent message in `slackChannelId`. Friend voice. No dollars. Include the tag string if `slackUserTags` is non-empty. + +``` +{tag string} Hey team, quick note. You've used about {percent_used}% of your Runneth limit this cycle, and you've got {days_until_reset} days until it resets on {reset_date_human}. + +I went through how I've been showing up for your team this month and put together a short read on a few places where you could be getting more out of each conversation. Most of these are small habit shifts, not big workflow changes. + +Have a look here: {APP_URL} +``` + +Where: +- `percent_used` is the rounded whole percent +- `days_until_reset` is the integer day count +- `reset_date_human` is natural language: "June 14th" +- `APP_URL` is the verified app open URL from Phase 6c + +**Rules:** +- No threaded follow-up. The app link is the full audit. +- Apply the pre-post check: read the channel for any audit posted today, skip if duplicate. +- Send via `slack send --channel {slackChannelId} --text "..."`. + +If `slackChannelId` is missing or Runneth was kicked from the channel, post the parent message as a visible reply in the current conversation and flag the Slack delivery failure in the agent log. + +--- + +## Phase 8 — Mark the cycle notified + +Update the config file: + +```json +{ + ... + "lastNotifiedCycleStart": "", + "lastNotificationDeliveredAt": "", + "lastNotificationAppUrl": "" +} +``` + +This is what keeps Phase 3 Gate 2 firing for the rest of the cycle. Don't update this for manual user-invoked runs. + +--- + +## Error handling + +| Condition | Response | +|-----------|----------| +| Config file missing | Invoke `setup-usage-efficiency-audit` and retry | +| Postgres unreachable | Log and exit silently. Watcher will retry tomorrow. | +| Cost data is all zeros for the cycle | Log "no usage this cycle" and exit. Don't post. | +| Conversation corpus is thin (< 20 conversations) | Use the lighter audit fallback in Phase 5b | +| Claude API errors | Retry once with smaller corpus; if still failing, post the reset notice only with a generic nudge, log the failure | +| App build fails | Post the parent Slack message with the percentage and reset date only, note that the detailed audit couldn't be built today, log the failure | +| Slack channel not joined | Post in current conversation, log the failure, do NOT mark cycle as notified (so the next day's run retries) | +| `lastNotifiedCycleStart` matches current `CYCLE_START` and the run is automated | Exit silently | + +--- + +## Customer-facing copy guardrails + +Every piece of copy that could reach the customer must pass these checks before sending: +1. Contains no dollar figure, cost number, or pricing language. +2. Contains no "you're running out" or scarcity framing. +3. Contains no internal terminology (no "tokens," "model calls," "agent_cost_usd"). +4. Reads like a teammate, not an alert. + +If any line fails, rewrite before sending. + +--- + +## Self-test (for manual invocation) + +When invoked manually for testing, the skill should: +1. Print the computed cycle window +2. Print the cost-to-date and percent used +3. Print whether the gates would normally fire it +4. If passed `--dry-run`, build the artifact but skip Slack delivery +5. Return the app URL even on dry-run so the tester can inspect the page + +These outputs are agent-facing, not customer-facing — fine to include numbers. diff --git a/usage-efficiency-audit/install-config.json b/usage-efficiency-audit/install-config.json new file mode 100644 index 0000000..7247a8a --- /dev/null +++ b/usage-efficiency-audit/install-config.json @@ -0,0 +1,72 @@ +{ + "schema": "1.0", + "id": "usage-efficiency-audit", + "version": "1.0.0", + "description": "Watcher + audit. Daily check on whether a workspace has crossed 80% of its included Runneth plan for the current billing cycle. When it has, runs a personalized efficiency audit on the team's actual conversation history, generates an openable HTML page with concrete recommendations, and posts a friend-voice heads-up to Slack with the link. Fires once per cycle.", + "requires": { + "runtime": "any Runneth sandbox", + "preinstalled": [], + "recommended": ["slack-connect"] + }, + "depends": [], + "customize": [ + { + "token": "WORKSPACE_ID", + "description": "The Motion workspace ID for this sandbox. Found in workspace settings or resolved via motion workspace-goal.", + "required": true, + "fallback": null + }, + { + "token": "WORKSPACE_NAME", + "description": "Human-readable workspace or brand name. Used to namespace brain files and derive the workspace slug.", + "required": true, + "fallback": null + }, + { + "token": "WORKSPACE_SLUG", + "description": "URL-safe version of the workspace name: lowercase, hyphens for spaces. Derived at install time from WORKSPACE_NAME. Used as the file name under /agent/brain/usage-efficiency/.", + "required": true, + "fallback": null + } + ], + "installs": [ + { + "from": "SKILL.md", + "to": "/agent/.agents/skills/usage-efficiency-audit/SKILL.md", + "on_exists": "overwrite", + "purpose": "Core usage efficiency audit skill. Runs daily on schedule and on explicit trigger." + }, + { + "from": "setup-usage-efficiency-audit/SKILL.md", + "to": "/agent/.agents/skills/setup-usage-efficiency-audit/SKILL.md", + "on_exists": "overwrite", + "purpose": "Setup and reconfiguration skill. Runs on first install. Re-invokable any time." + } + ], + "post_install": [ + { + "action": "skill-invoke", + "skill": "setup-usage-efficiency-audit", + "name": "Configure the audit", + "description": "Walks through picking the Slack channel for the heads-up, optional teammates to tag, and the team's billing anniversary day. Schedules the daily watcher.", + "manual": false + }, + { + "action": "show-intro", + "name": "Introduce the new capability", + "file": "post-install-intro.md", + "description": "Read post-install-intro.md from this use case folder. After all prior install and post-install steps complete, surface the file's sections to the user in chat as the closing message of the install turn. Resolve any {{TOKEN}} placeholders using the same customize map applied during install." + } + ], + "creates_at_runtime": [ + "/agent/brain/usage-efficiency/{{WORKSPACE_SLUG}}.json" + ], + "changelog": [ + { + "version": "1.0.0", + "date": "2026-06-02", + "type": "major", + "notes": "Initial release. Daily watcher checks cycle-to-date Runneth usage against the team's plan. When the cycle crosses 80%, the audit runs personalized pattern detection across the team's real conversations, builds an openable HTML page with the highest-leverage habit shifts, and posts a friend-voice note to Slack. Fires once per cycle. Setup skill captures Slack channel, optional user tags, and billing anniversary day." + } + ] +} diff --git a/usage-efficiency-audit/marketing.md b/usage-efficiency-audit/marketing.md new file mode 100644 index 0000000..0cd2353 --- /dev/null +++ b/usage-efficiency-audit/marketing.md @@ -0,0 +1,22 @@ +--- +hero_headline: "When the team hits 80% of plan, get a read on how to work faster." +hero_subhead: "Runneth watches usage quietly across the cycle, and when the team passes 80% it sends a friendly Slack note plus an openable page with the few habit shifts that would unlock the most. Personalized to how the team actually uses Runneth, not generic best practices." +install_time: "~2 minutes" +requires: "Slack connected, and your team's monthly billing cycle date" +status: "experimental" +--- + +## Super powers this unlocks + +- Catch the team before they bump into the plan limit, with enough runway to change something. +- See exactly which conversations could have been shorter, batched, or saved as durable context. +- Spot repeating workflows that would be cheaper as a routine the team can trigger any time. +- Find tasks the team is doing the long way when a skill would have done them in one turn. + +## How it works + +Pick the Slack channel where the team should get the heads-up, confirm the team's billing anniversary, and Runneth takes it from there. It checks usage once a day quietly. When the team crosses 80% of the cycle's plan, Runneth runs a personalized read on the actual conversations from that cycle, builds a short openable page with the 3 to 5 highest-leverage habit shifts, and posts the link to Slack. Only once per cycle, so the team is never nagged. The voice throughout is a teammate helping the team get more value, not an alert system warning about a limit. + +## A real example + +Maya's team blew past 80% on a Thursday with 11 days left in the cycle. The Slack note showed up in #motion-marketing right after stand-up: hey team, here's where you could be getting more out of each conversation. The page called out three things. The brand voice and customer pain points had been re-pasted into six different conversations that month, would be one save away from never having to retype. Three teammates had asked for hooks in three separate threads when one batched ask would have done it in half the messages. And a competitor research workflow the team ran every Monday morning could be a routine that triggers itself. Maya forwarded the page to the team. They saved the brand context, set up the routine, and finished the cycle at 94% instead of overshooting. diff --git a/usage-efficiency-audit/post-install-intro.md b/usage-efficiency-audit/post-install-intro.md new file mode 100644 index 0000000..f9de16a --- /dev/null +++ b/usage-efficiency-audit/post-install-intro.md @@ -0,0 +1,15 @@ +# Usage efficiency audit + +## What just opened up +The team now has a quiet watcher on Runneth usage. Every day Runneth checks where the team is in the cycle. The moment usage crosses 80%, a Slack note lands in the configured channel with a friendly read on a few habit shifts that would unlock more out of each conversation. The voice throughout is a teammate helping the team get more value, never an alert about a limit. It only fires once per cycle, so the team is never nagged. + +## Try this now +1. **Force a preview right now**: `Run the usage audit on what we've used so far this cycle.` + _The team gets back:_ a personalized page based on real conversations from the current cycle. Treat this as a sneak peek of what would land at 80% later. +2. **Change where the heads-up posts**: `Reconfigure the usage audit.` + _The team gets back:_ a chance to update the Slack channel, who gets tagged, or the billing anniversary day. +3. **Check where the team is right now**: `How are we tracking on usage this cycle?` + _The team gets back:_ percentage of plan used and days until reset, no audit attached. + +## Compounds with +- **bootcamp:** New teammates getting onboarded learn good Runneth habits early, so the team rarely needs the audit in the first place. diff --git a/usage-efficiency-audit/setup-usage-efficiency-audit/SKILL.md b/usage-efficiency-audit/setup-usage-efficiency-audit/SKILL.md new file mode 100755 index 0000000..07cfe98 --- /dev/null +++ b/usage-efficiency-audit/setup-usage-efficiency-audit/SKILL.md @@ -0,0 +1,144 @@ +--- +name: setup-usage-efficiency-audit +description: | + One-time setup for usage-efficiency-audit. Captures the Slack channel for delivery, optional user tags, and the org's billing anniversary day so the watcher knows when each monthly cycle starts. Runs automatically after install and can be re-invoked any time with "set up usage-efficiency-audit", "reconfigure usage-efficiency-audit", or "personalize usage-efficiency-audit". + +triggers: + phrases: + - "set up usage-efficiency-audit" + - "configure usage-efficiency-audit" + - "reconfigure usage-efficiency-audit" + - "personalize usage-efficiency-audit" + - "set up the usage audit" + intent: "User wants to configure or re-configure usage-efficiency-audit for their workspace" + excludes: + - "run usage-efficiency-audit" + - "what's my usage" +--- + +# Setup — usage-efficiency-audit + +Three personalization points. One question at a time. Write each answer immediately after it's given. Confirm at the end. + +--- + +## Step 1 — Resolve workspace and org + +Default to the workspace from Motion context. If the user named a different workspace in the same turn, use that instead. + +Derive a readable slug: lowercase, hyphens for spaces, strip special chars. Store as `WORKSPACE_SLUG`. + +Capture the `organizationId` from `motion workspaces` if not already in context. Store as `ORG_ID`. + +Open with one sentence: + +> Setting up the usage efficiency audit for ``. Three quick questions and we're done. + +--- + +## Step 2 — Slack channel (required) + +> Which Slack channel should I post the audit to when usage hits 80%? Drop a channel ID (`C0XXX`) or name (`#channel-name`), I'll resolve the name. + +**Rules:** +- DMs are not a delivery target. If the user names a DM or person, explain channels-only and re-ask. +- Validate the channel ID against Slack membership. If Runneth is not in the channel, surface that and ask the user to invite it before proceeding. +- Single channel only for v1. If they name multiple, take the first and note the others as future config. + +**Write to** `slackChannelId` in the config file. + +--- + +## Step 3 — User tags (optional) + +> Anyone I should tag in the post? Optional — drop one or more Slack user IDs or @-handles, or say "skip". + +Accept zero, one, or many. Tags get inserted at the start of the parent message. + +**Write to** `slackUserTags` (array, can be empty). + +--- + +## Step 4 — Billing anniversary day (required) + +> What day of the month does this customer's Runneth billing cycle reset? Just the day number, 1 through 31. + +**Rules:** +- Accept 1 through 31. +- For 29, 30, and 31, the audit automatically falls back to the last day of any month that doesn't have that day (e.g. February falls back to the 28th or 29th in leap years). This is how Stripe handles it, so the cycle stays aligned with their real billing. +- If they don't know, suggest checking Stripe or HubSpot for the subscription start date — the day-of-month from that is the answer. + +**Write to** `billingAnniversaryDay` (integer 1-31). + +--- + +## Step 5 — Write config + +Write to `/agent/brain/usage-efficiency/.json`: + +```json +{ + "workspaceSlug": "", + "organizationId": "", + "billingAnniversaryDay": 14, + "slackChannelId": "C0XXXXXX", + "slackUserTags": ["U0YYYYYY"], + "includedLimitUsd": 100.00, + "thresholdPercent": 80, + "upgradeMultiplier": 3, + "lastNotifiedCycleStart": null, + "configuredAt": "" +} +``` + +**Notes for the agent writing this config:** +- `includedLimitUsd`, `thresholdPercent`, and `upgradeMultiplier` are internal-only constants. They live here so they're easy to change later, but they are never surfaced in customer-facing copy. The customer only ever sees percentage of plan used and "3x your usage." +- `lastNotifiedCycleStart` stays null until the first notification fires. + +--- + +## Step 6 — Schedule the watcher + +Create a daily reminder that runs the main audit skill. The skill itself decides whether to act on any given day. + +```bash +reminder add \ + --title "Run usage-efficiency-audit watcher" \ + --schedule "every day at 09:00" \ + --instruction "Run the usage-efficiency-audit skill for workspace . The skill will check whether the org has crossed the threshold and only deliver an audit if it has." +``` + +Capture the reminder short ID. Save it to the config file as `watcherReminderId` so reconfigure can find and update it. + +--- + +## Step 7 — Confirm + +> Locked in for ``: +> - Audit posts to: `` +> - Tagging: `` +> - Billing cycle resets on day `` of each month +> - Watcher scheduled daily at 09:00 `` +> +> You can re-run "set up usage-efficiency-audit" any time to change these. Want me to do a dry-run now so you can see what the post would look like? + +--- + +## Re-invocation behavior + +When invoked on an already-configured workspace: +1. Read existing config and show the current values. +2. Ask which fields the user wants to change. Update only those. +3. If the channel or schedule changed, update the reminder. +4. Preserve `lastNotifiedCycleStart` across reconfigures so a mid-cycle change doesn't cause a re-notify. + +--- + +## Error handling + +| Condition | Response | +|-----------|----------| +| Slack channel not joined by Runneth | Ask user to invite Runneth, retry | +| Workspace can't be resolved | Ask user to name the workspace | +| Reminder system unavailable | Save config anyway, note that scheduling failed and the watcher needs to be added manually | +| Existing config corrupt | Show the user, ask whether to start fresh | diff --git a/usage-efficiency-audit/use-case.json b/usage-efficiency-audit/use-case.json new file mode 100644 index 0000000..06bdb77 --- /dev/null +++ b/usage-efficiency-audit/use-case.json @@ -0,0 +1,8 @@ +{ + "slug": "usage-efficiency-audit", + "display_title": "Get More Out of Every Conversation", + "pitch": "When usage hits 80% of plan, Runneth sends a friendly read on how the team could be working faster.", + "status": "experimental", + "category": "agent-foundations", + "github_path": "usage-efficiency-audit" +} diff --git a/use-case-library-site/frontend/src/Illustration.tsx b/use-case-library-site/frontend/src/Illustration.tsx index 73abcd0..802796e 100644 --- a/use-case-library-site/frontend/src/Illustration.tsx +++ b/use-case-library-site/frontend/src/Illustration.tsx @@ -4,6 +4,7 @@ import { accentIconColor, cardAccent } from "./theme"; import apiConnectionFilled from "./icons/api-connection-filled.svg?raw"; import arrowRotateClockwiseFilled from "./icons/arrow-rotate-clockwise-filled.svg?raw"; +import barsRisingFilled from "./icons/bars-rising-filled.svg?raw"; import brainFilled from "./icons/brain-filled.svg?raw"; import checkCircleFilled from "./icons/check-circle-filled.svg?raw"; import cloudDownloadFilled from "./icons/cloud-download-filled.svg?raw"; @@ -53,6 +54,7 @@ const ICON_BY_SLUG: Record = { "performance-bundle": zapFilled, "weekly-performance-deck": slidesWideFilled, "health-alerts": squareChecklistBellFilled, + "usage-efficiency-audit": barsRisingFilled, }; const iconWrapStyle = (tint: string): CSSProperties => ({ diff --git a/use-case-library-site/frontend/src/icons/bars-rising-filled.svg b/use-case-library-site/frontend/src/icons/bars-rising-filled.svg new file mode 100644 index 0000000..6688d2e --- /dev/null +++ b/use-case-library-site/frontend/src/icons/bars-rising-filled.svg @@ -0,0 +1,3 @@ + + +