Skip to content

feat(web): record sales-demo spend before a reset discards it - #5496

Merged
iscekic merged 1 commit into
mainfrom
feat/sales-demo-spend-ledger
Aug 25, 2026
Merged

feat(web): record sales-demo spend before a reset discards it#5496
iscekic merged 1 commit into
mainfrom
feat/sales-demo-spend-ledger

Conversation

@iscekic

@iscekic iscekic commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

A sales-demo reset (#5459) deletes the org's microdollar_usage, exa_usage_log, compute_usage_charge, and credit_transactions rows, then zeroes the org balance columns. The daily cron runs that reset for every live demo, so demo cost disappeared within 24 hours and finance had no way to account for it.

This adds a separate ledger table, sales_demo_spend_ledger, and writes one row inside the reset transaction before the deletes:

  • microdollars_used comes from the org counter at reset time. The LLM path (mutateOrganizationUsage), the Exa path, and the container compute settlement all increment that counter, so one value covers all three spend sources.
  • period_start is the previous sales_demo_last_reset_at, or the org's created_at for the first reset. period_end defaults to the reset time, so each row is a closed window.
  • A reset that discards no spend writes no row. The sales_demo_spend_ledger_spend_positive check constraint enforces that at the database level.
  • The row keeps organization_id and owner_kilo_user_id only. It stores no name or email, so it adds no PII and softDeleteUser needs no change; the org row survives a reset and holds the name.
  • The FK uses ON DELETE restrict, matching compute_usage_charge, so a demo org cannot be hard-deleted out from under its spend history.

Monthly demo cost is then one query:

SELECT date_trunc('month', period_end) AS month, sum(microdollars_used)
FROM sales_demo_spend_ledger GROUP BY 1 ORDER BY 1;

Out of scope, worth a separate look: the reset leaves orphan microdollar_usage_metadata rows (that table has no FK to microdollar_usage), and it never resets exa_monthly_usage, so a demo keeps its Exa free-allowance consumption across resets within a month.

Verification

  • pnpm --filter web jest src/routers/admin/sales-demo-router.test.ts — 10 passed, including the two new cases: a dirty reset writes one row of 1,000,000 microdollars with the owner and window, and a clean reset writes none.
  • pnpm --filter web jest packages/db/src/schema.test.ts src/app/api/cron/sales-demo-reset src/lib/organizations/sales-demo — 12 passed; the schema test proves the generated migration matches the schema.
  • pnpm lint, pnpm typecheck, pnpm format — clean.
  • pnpm drizzle migrate applied 0227_exotic_scarecrow.sql to a local database.

No manual UI test. The change has no UI.

Visual Changes

N/A

Reviewer Notes

  • The insert sits before the deletes in restoreSalesDemoOrganization, inside the same transaction that holds FOR UPDATE on the org row, so a failed reset writes no ledger row.
  • No admin UI reads the table yet. Add one when finance asks for more than the SQL above.

Note for finance

Nothing is lost yet if this lands before 2026-08-26 05:00 UTC. Sales-demo orgs first existed on 2026-08-25 18:40 UTC (#5459), and the reset cron runs at 0 5 * * *, so the first spend-destroying reset has not run.

  • Each reset that runs without this change loses one day of demo spend per demo org, permanently. The reset deletes microdollar_usage, exa_usage_log, compute_usage_charge, and credit_transactions for the org, then zeroes its balance columns. No archive, no analytics sink, no per-request export holds a copy.
  • For any day already lost, treat the demo cost as unattributed COGS. The only remaining source is the provider-side invoice, which cannot be split by org.
  • From this change onward, demo spend per month is:
SELECT date_trunc('month', period_end) AS month,
       sum(microdollars_used) / 1e6 AS usd
FROM sales_demo_spend_ledger
GROUP BY 1 ORDER BY 1;
  • Per rep, join owner_kilo_user_id to kilocode_users.id. Each row is one closed window, period_start to period_end, so no window double counts.
  • A month with no row means no demo spend. A reset that discards nothing writes nothing.
  • A follow-up PR seeds demo orgs with fabricated usage so the demo looks populated. That PR excludes the seeded amount from this ledger, so a row here stays real spend only, and the meaning of these rows does not change.

The sales-demo reset deletes the org's usage and credit rows, so demo cost
left no trace after the daily cron ran. Add the `sales_demo_spend_ledger`
table and write one row per reset, taken from the org's `microdollars_used`
counter, which the LLM, Exa, and compute charge paths all increment.

A reset that discards no spend writes no row. A check constraint keeps a
zero or negative row out of the table.
@iscekic iscekic self-assigned this Aug 25, 2026
@kilo-code-bot

kilo-code-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • apps/web/src/lib/organizations/sales-demo.ts
  • apps/web/src/routers/admin/sales-demo-router.test.ts
  • packages/db/src/migrations/0227_exotic_scarecrow.sql
  • packages/db/src/migrations/meta/0227_snapshot.json
  • packages/db/src/migrations/meta/_journal.json
  • packages/db/src/schema.ts

Reviewed by grok-4.6 · Input: 123.7K · Output: 12.5K · Cached: 692.7K

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic enabled auto-merge (squash) August 25, 2026 19:46
@iscekic
iscekic merged commit 313e075 into main Aug 25, 2026
49 checks passed
@iscekic
iscekic deleted the feat/sales-demo-spend-ledger branch August 25, 2026 19:59
iscekic added a commit that referenced this pull request Aug 26, 2026
Commit 3e25b49 is content-identical to 313e075, already on main via PR #5496. Resolved the cherry-pick conflict by keeping HEAD's version of sales-demo.ts, sales-demo-router.test.ts, and schema.ts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants