Skip to content

Commit 7aff553

Browse files
committed
Add revenue renewal true-up guard
1 parent abb4798 commit 7aff553

10 files changed

Lines changed: 917 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# deepevents.ai
22
deepevents.ai main codebase
3+
4+
## Revenue renewal true-up
5+
6+
`revenue-renewal-true-up/` adds a local revenue operations module for renewal timing, seat true-ups, payment holds, churn risk, and renewal packet evidence.

revenue-renewal-true-up/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Revenue Renewal True-Up
2+
3+
This module adds a self-contained revenue operations slice for issue #20. It focuses on renewals and seat true-ups after an institution is already using SCIBASE.
4+
5+
It does not handle live payments or account credentials. All data is synthetic.
6+
7+
## What It Covers
8+
9+
- renewal dates, notice windows, and late renewal risk
10+
- contracted seats vs active seats with annual true-up estimates
11+
- compute credit overage estimates
12+
- payment, purchase order, security review, and DPA blockers
13+
- churn and expansion signals for customer success teams
14+
- signed webhook events and audit digest evidence
15+
- admin dashboard metrics and a renewal packet report
16+
17+
## Run Locally
18+
19+
```bash
20+
npm run check
21+
npm test
22+
npm run demo
23+
```
24+
25+
## Files
26+
27+
- `src/revenue-renewal-true-up.js` contains the evaluator.
28+
- `data/sample-renewal-input.json` contains synthetic accounts and plans.
29+
- `test/revenue-renewal-true-up.test.js` covers decisions, metrics, events, and digest stability.
30+
- `scripts/demo.js` prints a reviewer-friendly renewal report.
31+
- `docs/requirement-map.md` maps the slice to issue #20.
32+
- `docs/demo.svg` and `docs/demo.gif` show the dashboard flow.
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
{
2+
"generatedAt": "2026-05-16T09:30:00Z",
3+
"currency": "USD",
4+
"plans": [
5+
{
6+
"id": "institutional-research",
7+
"name": "Institutional Research",
8+
"seatUnitPriceCents": 18000,
9+
"includedComputeCents": 120000,
10+
"annualDiscountRate": 0.12,
11+
"volumeDiscountRate": 0.07,
12+
"volumeSeatThreshold": 100
13+
},
14+
{
15+
"id": "lab-growth",
16+
"name": "Lab Growth",
17+
"seatUnitPriceCents": 9600,
18+
"includedComputeCents": 42000,
19+
"annualDiscountRate": 0.08,
20+
"volumeDiscountRate": 0.03,
21+
"volumeSeatThreshold": 40
22+
}
23+
],
24+
"accounts": [
25+
{
26+
"id": "acct-northstar",
27+
"name": "Northstar University Library",
28+
"segment": "institution",
29+
"contract": {
30+
"planId": "institutional-research",
31+
"renewalDate": "2026-06-12",
32+
"term": "annual",
33+
"contractedSeats": 80,
34+
"baseAnnualCents": 1440000,
35+
"autoRenew": true,
36+
"noticeDays": 30,
37+
"purchaseOrderRequired": true,
38+
"dataProcessingAddendumExpires": "2026-09-01",
39+
"slaTier": "institutional"
40+
},
41+
"billing": {
42+
"paymentRail": "institutional_invoice",
43+
"openInvoices": [
44+
{
45+
"id": "inv-1021",
46+
"status": "past_due",
47+
"ageDays": 38,
48+
"amountCents": 220000
49+
}
50+
]
51+
},
52+
"usage": {
53+
"activeSeats": 96,
54+
"provisionedSeats": 108,
55+
"monthlyActiveSeats": [101, 99, 98, 96],
56+
"computeCreditsUsedCents": 146000,
57+
"licensedApiCalls": 98000,
58+
"adminsActive": 5
59+
},
60+
"support": {
61+
"openCriticalTickets": 1,
62+
"slaBreaches": 2,
63+
"avgFirstResponseHours": 9.5
64+
},
65+
"commercial": {
66+
"customerSuccessOwner": "Maya Chen",
67+
"healthScore": 64,
68+
"lastExecutiveReview": "2026-02-01",
69+
"expansionSignals": ["new genomics center onboarding"]
70+
},
71+
"requirements": {
72+
"securityReviewDue": "2026-05-25",
73+
"purchaseOrder": ""
74+
}
75+
},
76+
{
77+
"id": "acct-riverlab",
78+
"name": "River Lab Consortium",
79+
"segment": "consortium",
80+
"contract": {
81+
"planId": "institutional-research",
82+
"renewalDate": "2026-07-01",
83+
"term": "annual",
84+
"contractedSeats": 120,
85+
"baseAnnualCents": 2160000,
86+
"autoRenew": false,
87+
"noticeDays": 45,
88+
"purchaseOrderRequired": true,
89+
"dataProcessingAddendumExpires": "2026-12-20",
90+
"slaTier": "institutional"
91+
},
92+
"billing": {
93+
"paymentRail": "institutional_invoice",
94+
"openInvoices": []
95+
},
96+
"usage": {
97+
"activeSeats": 148,
98+
"provisionedSeats": 151,
99+
"monthlyActiveSeats": [121, 131, 140, 148],
100+
"computeCreditsUsedCents": 110000,
101+
"licensedApiCalls": 164000,
102+
"adminsActive": 9
103+
},
104+
"support": {
105+
"openCriticalTickets": 0,
106+
"slaBreaches": 0,
107+
"avgFirstResponseHours": 2.7
108+
},
109+
"commercial": {
110+
"customerSuccessOwner": "Andre Park",
111+
"healthScore": 91,
112+
"lastExecutiveReview": "2026-04-18",
113+
"expansionSignals": ["three member labs requested private projects"]
114+
},
115+
"requirements": {
116+
"securityReviewDue": "2026-06-12",
117+
"purchaseOrder": "PO-RLC-2026-077"
118+
}
119+
},
120+
{
121+
"id": "acct-hillview",
122+
"name": "Hillview Methods Lab",
123+
"segment": "lab",
124+
"contract": {
125+
"planId": "lab-growth",
126+
"renewalDate": "2026-06-04",
127+
"term": "annual",
128+
"contractedSeats": 34,
129+
"baseAnnualCents": 326400,
130+
"autoRenew": false,
131+
"noticeDays": 30,
132+
"purchaseOrderRequired": false,
133+
"dataProcessingAddendumExpires": "2026-05-30",
134+
"slaTier": "standard"
135+
},
136+
"billing": {
137+
"paymentRail": "card",
138+
"openInvoices": [
139+
{
140+
"id": "inv-2098",
141+
"status": "open",
142+
"ageDays": 6,
143+
"amountCents": 31200
144+
}
145+
]
146+
},
147+
"usage": {
148+
"activeSeats": 19,
149+
"provisionedSeats": 21,
150+
"monthlyActiveSeats": [33, 29, 24, 19],
151+
"computeCreditsUsedCents": 26000,
152+
"licensedApiCalls": 9000,
153+
"adminsActive": 1
154+
},
155+
"support": {
156+
"openCriticalTickets": 0,
157+
"slaBreaches": 1,
158+
"avgFirstResponseHours": 7.2
159+
},
160+
"commercial": {
161+
"customerSuccessOwner": "Nora Singh",
162+
"healthScore": 51,
163+
"lastExecutiveReview": "2025-12-10",
164+
"expansionSignals": []
165+
},
166+
"requirements": {
167+
"securityReviewDue": "2026-06-20",
168+
"purchaseOrder": null
169+
}
170+
},
171+
{
172+
"id": "acct-clearwater",
173+
"name": "Clearwater Research Office",
174+
"segment": "institution",
175+
"contract": {
176+
"planId": "institutional-research",
177+
"renewalDate": "2026-08-20",
178+
"term": "annual",
179+
"contractedSeats": 60,
180+
"baseAnnualCents": 1080000,
181+
"autoRenew": true,
182+
"noticeDays": 60,
183+
"purchaseOrderRequired": true,
184+
"dataProcessingAddendumExpires": "2027-01-15",
185+
"slaTier": "institutional"
186+
},
187+
"billing": {
188+
"paymentRail": "institutional_invoice",
189+
"openInvoices": []
190+
},
191+
"usage": {
192+
"activeSeats": 61,
193+
"provisionedSeats": 64,
194+
"monthlyActiveSeats": [58, 60, 61, 61],
195+
"computeCreditsUsedCents": 118000,
196+
"licensedApiCalls": 74000,
197+
"adminsActive": 4
198+
},
199+
"support": {
200+
"openCriticalTickets": 0,
201+
"slaBreaches": 0,
202+
"avgFirstResponseHours": 3.5
203+
},
204+
"commercial": {
205+
"customerSuccessOwner": "Andre Park",
206+
"healthScore": 82,
207+
"lastExecutiveReview": "2026-04-02",
208+
"expansionSignals": []
209+
},
210+
"requirements": {
211+
"securityReviewDue": "2026-07-24",
212+
"purchaseOrder": "PO-CRO-4881"
213+
}
214+
}
215+
]
216+
}
37.3 KB
Loading
Lines changed: 46 additions & 0 deletions
Loading
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Requirement Map
2+
3+
This slice maps to issue #20, Revenue Infrastructure.
4+
5+
| Issue area | Module coverage |
6+
| --- | --- |
7+
| Tiered subscription billing | Evaluates plan, seat price, annual discount, volume threshold, renewal date, and contracted seats. |
8+
| Institutional licenses | Models institutional and consortium renewal packets with purchase order and DPA checks. |
9+
| Volume discounts and auto-scaling | Computes active seat true-ups against contracted seats and volume discount rules. |
10+
| Institutional invoicing | Flags past due and open invoice exposure before renewal booking. |
11+
| AI compute billing | Adds included compute credit limits and renewal-period overage estimates. |
12+
| Admin dashboard | Produces blocked, review, expansion, true-up, churn exposure, and forecast metrics. |
13+
| Webhooks and integrations | Emits deterministic signed renewal events for downstream CRM and billing systems. |
14+
| Auditability | Builds account digests and a manifest digest for reviewer evidence. |
15+
16+
## Distinct Scope
17+
18+
Existing submissions cover billing ledgers, metering, entitlements, procurement, licensing gates, revenue recognition, tax controls, and compute margin. This module focuses on renewal execution after usage has already happened: who is due, what needs a true-up, which renewals are blocked, and where churn or expansion needs action.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "revenue-renewal-true-up",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module",
6+
"description": "Renewal, seat true-up, and churn risk controls for SCIBASE revenue infrastructure.",
7+
"scripts": {
8+
"check": "node --check src/revenue-renewal-true-up.js && node --check scripts/demo.js && node --check test/revenue-renewal-true-up.test.js",
9+
"test": "node --test test/revenue-renewal-true-up.test.js",
10+
"demo": "node scripts/demo.js"
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { readFile } from "node:fs/promises";
2+
import { fileURLToPath } from "node:url";
3+
import { dirname, join } from "node:path";
4+
import { evaluateRenewalPortfolio, renderRenewalReport } from "../src/revenue-renewal-true-up.js";
5+
6+
const currentDir = dirname(fileURLToPath(import.meta.url));
7+
const dataPath = join(currentDir, "../data/sample-renewal-input.json");
8+
const input = JSON.parse(await readFile(dataPath, "utf8"));
9+
const result = evaluateRenewalPortfolio(input);
10+
11+
console.log(renderRenewalReport(result));

0 commit comments

Comments
 (0)