Skip to content
Merged
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
13 changes: 10 additions & 3 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3606,6 +3606,13 @@ async function upsertProductUsageDailyRollup(env: Env, day: string, generatedAt:
return record;
}

// Bounded enum dimensions (surface / outcome / eventName) are consumed by exact-name lookups
// (e.g. the weekly value report's sumEvent over byEvent), so they must be stored complete:
// frequency-truncating a bounded exact-lookup dimension silently zeroes any value below the
// top-N cut on a high-diversity day. Only the genuinely-unbounded repo/command/tool/route
// dimensions keep a display top-N.
const FULL_DIMENSION_LIMIT = Number.MAX_SAFE_INTEGER;

function buildProductUsageDailyRollupRecord(args: {
day: string;
generatedAt: string;
Expand Down Expand Up @@ -3633,9 +3640,9 @@ function buildProductUsageDailyRollupRecord(args: {
maxEventCapacity: PRODUCT_USAGE_ROLLUP_EVENT_SCAN_LIMIT,
firstEventAt: args.events[0]?.occurredAt ?? null,
lastEventAt: args.events.at(-1)?.occurredAt ?? null,
bySurface: countProductUsageDimensions(args.events.map((event) => event.surface)).map(({ key, count }) => ({ surface: normalizeProductUsageSurface(key), count })),
byOutcome: countProductUsageDimensions(args.events.map((event) => event.outcome)).map(({ key, count }) => ({ outcome: normalizeProductUsageOutcome(key), count })),
byEvent: countProductUsageDimensions(args.events.map((event) => event.eventName)).map(({ key, count }) => ({ eventName: key, count })),
bySurface: countProductUsageDimensions(args.events.map((event) => event.surface), FULL_DIMENSION_LIMIT).map(({ key, count }) => ({ surface: normalizeProductUsageSurface(key), count })),
byOutcome: countProductUsageDimensions(args.events.map((event) => event.outcome), FULL_DIMENSION_LIMIT).map(({ key, count }) => ({ outcome: normalizeProductUsageOutcome(key), count })),
byEvent: countProductUsageDimensions(args.events.map((event) => event.eventName), FULL_DIMENSION_LIMIT).map(({ key, count }) => ({ eventName: key, count })),
byRepo: countProductUsageDimensions(args.events.map((event) => event.repoFullName)),
byCommand: countProductUsageDimensions(args.events.map((event) => productUsageMetadataString(event, "command"))),
byTool: countProductUsageDimensions(args.events.map((event) => productUsageMetadataString(event, "toolName"))),
Expand Down
18 changes: 18 additions & 0 deletions test/unit/product-usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,24 @@ describe("product usage events", () => {
await expect(getProductUsageRollupStatus(env, { nowIso: "2026-05-31T00:40:00.000Z" })).resolves.toMatchObject({ status: "ready", warnings: [] });
});

it("retains low-frequency bounded events in byEvent on a high-diversity day (no top-20 truncation)", async () => {
const env = createTestEnv();
const day = "2026-05-30";
// 20 distinct filler events, each recorded twice (count 2), occupy the highest-frequency slots.
for (let i = 0; i < 20; i++) {
const eventName = `filler_event_${String(i).padStart(2, "0")}`;
await recordProductUsageEvent(env, { surface: "control_panel", eventName, actor: "user", outcome: "success", occurredAt: `${day}T00:00:00.000Z` });
await recordProductUsageEvent(env, { surface: "control_panel", eventName, actor: "user", outcome: "success", occurredAt: `${day}T01:00:00.000Z` });
}
// A low-frequency flagship event the weekly report looks up by exact name (count 1, ranks 21st).
await recordProductUsageEvent(env, { surface: "control_panel", eventName: "agent_pr_packet_completed", actor: "user", outcome: "success", occurredAt: `${day}T05:00:00.000Z` });

const run = await rollupProductUsageDaily(env, { day, nowIso: "2026-05-31T00:10:00.000Z" });
// Without the fix, byEvent was frequency-truncated to the top 20 and dropped this event.
expect(run.rollups[0]?.byEvent).toEqual(expect.arrayContaining([{ eventName: "agent_pr_packet_completed", count: 1 }]));
expect((run.rollups[0]?.byEvent ?? []).length).toBe(21);
});

it("builds empty role and retention rollups for days without product usage", async () => {
const env = createTestEnv({ PRODUCT_USAGE_HASH_SALT: "fixed-test-salt" });
const result = await rollupProductUsageDaily(env, { day: "2026-06-01", nowIso: "2026-06-02T00:00:00.000Z" });
Expand Down