Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .use-case-library/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
25 changes: 25 additions & 0 deletions usage-efficiency-audit/README.md
Original file line number Diff line number Diff line change
@@ -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/<workspace-slug>.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
324 changes: 324 additions & 0 deletions usage-efficiency-audit/SKILL.md
Original file line number Diff line number Diff line change
@@ -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/<WORKSPACE_SLUG>.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": "<CYCLE_START.isoformat()>",
"lastNotificationDeliveredAt": "<ISO now>",
"lastNotificationAppUrl": "<APP_URL>"
}
```

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.
Loading
Loading