Skip to content

Commit 0869453

Browse files
ofershapcursoragent
andcommitted
fix: adoption score consistency uses period days, realistic mock model costs
Consistency metric now divides by the actual time period instead of max active days across users — gives accurate adoption tiers. Mock data updated with realistic per-request costs and model selection that respects cost tiers. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 37d2bca commit 0869453

4 files changed

Lines changed: 35 additions & 30 deletions

File tree

FEATURES.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@
2929
### Members Table
3030

3131
- Sortable by: spend, activity, requests, lines, $/req, context, name
32-
- Filterable by badge type
32+
- Filterable by badge type via `Badges ▾` dropdown with collapsible sections and per-badge user counts
33+
- Active badge filter shown as chip with ✕ dismiss next to table header
3334
- Columns: rank, name, email, spend, requests, lines, $/req, model, profile badges, ranks
3435

35-
### Badges (per user, max 2)
36+
### Badges (per user, all shown)
3637

3738
| Category | Badges |
3839
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39-
| Usage | Power User, Deep Thinker, Low Usage |
40+
| Usage | Power User, Deep Thinker, Balanced, Low Usage |
4041
| Spend | Cost Efficient, Premium Model, Over Budget |
4142
| Context | Long Sessions, Short Sessions |
4243
| Adoption | AI-Native (80%+), High Adoption (55%+), Moderate (30%+), Low Adoption (10%+), Manual Coder (<10%) — based on composite score (accept rate + engagement + consistency) |

data/mock.db

-104 KB
Binary file not shown.

scripts/generate-mock-db.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const STORY_MEMBERS: StoryMember[] = [
7575
userId: "usr_marcus001",
7676
storyRole: "expensive_model",
7777
activityLevel: "high",
78-
primaryModel: "claude-4.5-opus-high",
78+
primaryModel: "claude-4.6-opus-high",
7979
clientVersion: "2.5.12",
8080
group: "Backend > API",
8181
},
@@ -199,24 +199,27 @@ const MODELS = [
199199
"gpt-5.3-codex",
200200
"claude-4.5-sonnet-thinking",
201201
"claude-4.6-opus-high-thinking-fast",
202+
"claude-4.6-opus-max-thinking",
202203
];
203204

204-
const MODEL_WEIGHTS = [20, 15, 8, 12, 10, 10, 5, 5, 5, 5, 3, 2, 0];
205+
const MODEL_WEIGHTS = [20, 15, 4, 12, 10, 10, 5, 5, 5, 5, 3, 2, 0, 4];
205206

206207
const MODEL_COST_PER_REQ: Record<string, number> = {
207-
"claude-4.5-sonnet": 0.18,
208-
"claude-4.6-opus-high": 1.03,
209-
"claude-4.6-opus-max": 1.26,
210-
"claude-4.5-haiku": 0.05,
211-
"claude-4.6-opus-high-thinking": 2.8,
212-
"gpt-5.2": 0.45,
213-
"claude-4.5-opus-high-thinking": 0.42,
214-
"gpt-5.2-codex": 0.65,
215-
"gemini-3-pro-preview": 0.35,
216-
"gemini-3-flash-preview": 0.12,
217-
"gpt-5.3-codex": 0.85,
218-
"claude-4.5-sonnet-thinking": 0.55,
208+
"claude-4.5-sonnet": 0.06,
209+
"claude-4.6-opus-high": 0.38,
210+
"claude-4.6-opus-max": 4.5,
211+
"claude-4.5-haiku": 0.015,
212+
"claude-4.6-opus-high-thinking": 0.8,
213+
"gpt-5.2": 0.18,
214+
"claude-4.5-opus-high": 0.35,
215+
"claude-4.5-opus-high-thinking": 0.3,
216+
"gpt-5.2-codex": 0.22,
217+
"gemini-3-pro-preview": 0.12,
218+
"gemini-3-flash-preview": 0.03,
219+
"gpt-5.3-codex": 0.32,
220+
"claude-4.5-sonnet-thinking": 0.18,
219221
"claude-4.6-opus-high-thinking-fast": 14.55,
222+
"claude-4.6-opus-max-thinking": 7.8,
220223
};
221224

222225
const GROUPS = [
@@ -1414,18 +1417,19 @@ function generateNormalDay(user: StoryMember, date: string, isWeekend: boolean):
14141417
const agentReqs = baseReqs;
14151418
const chatReqs = rand(0, Math.floor(agentReqs * 0.2));
14161419
const composerReqs = rand(0, Math.floor(agentReqs * 0.1));
1417-
const model = seededRandom() < 0.8 ? user.primaryModel : weightedPick(MODELS, MODEL_WEIGHTS);
1420+
let model = user.primaryModel;
1421+
if (seededRandom() > 0.85) {
1422+
const primaryCost = MODEL_COST_PER_REQ[user.primaryModel] ?? 0.5;
1423+
const candidates = MODELS.filter((m) => (MODEL_COST_PER_REQ[m] ?? 0.5) <= primaryCost * 3);
1424+
const candidateWeights = candidates.map((m) => MODEL_WEIGHTS[MODELS.indexOf(m)] ?? 1);
1425+
model = weightedPick(candidates, candidateWeights);
1426+
}
14181427
const costPerReq = MODEL_COST_PER_REQ[model] ?? 0.5;
14191428

1420-
const events = generateEvents(
1421-
user,
1422-
date,
1423-
Math.min(agentReqs, rand(5, 25)),
1424-
model,
1425-
costPerReq,
1426-
false,
1427-
);
1428-
const spendCents = Math.round(events.reduce((s, e) => s + e.totalCents, 0));
1429+
const eventCount = Math.min(agentReqs, rand(5, 25));
1430+
const events = generateEvents(user, date, eventCount, model, costPerReq, false);
1431+
const jitter = 0.85 + seededRandom() * 0.3;
1432+
const spendCents = Math.round(costPerReq * 100 * agentReqs * jitter);
14291433

14301434
const linesAdded = agentReqs * rand(3, 15);
14311435
const linesDeleted = Math.floor(linesAdded * (0.2 + seededRandom() * 0.3));

src/lib/db.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ export function getFullDashboard(days: number = 7): FullDashboard {
12181218
cycleEnd,
12191219
cycleDays,
12201220
dailyTeamActivity,
1221-
rankedUsers: assignBadges(rankedUsers).map((u, i) => ({ ...u, rank: i + 1 })),
1221+
rankedUsers: assignBadges(rankedUsers, days).map((u, i) => ({ ...u, rank: i + 1 })),
12221222
};
12231223

12241224
return { days, stats, modelCosts, teamDailySpend, dailySpendBreakdown };
@@ -1247,6 +1247,7 @@ function assignBadges(
12471247
spend_rank: number;
12481248
activity_rank: number;
12491249
}>,
1250+
periodDays: number = 7,
12501251
): Array<
12511252
(typeof users)[number] & {
12521253
usage_badge: UsageBadge | null;
@@ -1255,7 +1256,6 @@ function assignBadges(
12551256
adoption_badge: AdoptionBadge | null;
12561257
}
12571258
> {
1258-
const maxActiveDays = Math.max(...users.map((u) => u.active_days), 1);
12591259
const activeUsersForIntensity = users.filter((u) => u.agent_requests >= 10 && u.active_days > 0);
12601260
const intensities = activeUsersForIntensity
12611261
.map((u) => u.agent_requests / u.active_days)
@@ -1331,7 +1331,7 @@ function assignBadges(
13311331
const acceptRate = u.total_applies > 0 ? u.total_accepts / u.total_applies : 0;
13321332
const intensity = u.agent_requests / u.active_days;
13331333
const intensityNorm = Math.min(intensity / p90Intensity, 1);
1334-
const consistency = u.active_days / maxActiveDays;
1334+
const consistency = periodDays > 0 ? u.active_days / periodDays : 0;
13351335
const score = acceptRate * 40 + intensityNorm * 40 + consistency * 20;
13361336
if (score >= 80) adoption_badge = "ai-native";
13371337
else if (score >= 55) adoption_badge = "high-adoption";

0 commit comments

Comments
 (0)