diff --git a/committed-revenue-drawdown-guard/README.md b/committed-revenue-drawdown-guard/README.md new file mode 100644 index 00000000..a6ae4c40 --- /dev/null +++ b/committed-revenue-drawdown-guard/README.md @@ -0,0 +1,49 @@ +# Committed Revenue Drawdown Guard + +This module adds a self-contained finance control slice for SCIBASE revenue infrastructure. It evaluates institutional minimum commitments across subscription seats, AI compute, and analytics licensing so finance and customer teams can see whether contracted revenue is being consumed, at risk, or ready for a paid amendment. + +The implementation is dependency-free and uses synthetic customer data only. It does not connect to Stripe, PayPal, bank rails, wallets, billing providers, live customer records, or external APIs. + +## What It Checks + +- Contracted annual minimums by revenue stream +- Actual consumption against the time-elapsed expectation +- Projected final consumption and overage risk +- Under-consumption that could threaten renewal or recognition confidence +- Expiring prepaid credits without a drawdown plan +- AI compute usage below the configured gross-margin floor +- Recommended actions for finance, customer success, account executives, and finance ops +- Deterministic audit digests for reviewer and month-end evidence packets + +## Usage + +Run the assertion tests: + +```bash +node committed-revenue-drawdown-guard/test.js +``` + +Run the demo: + +```bash +node committed-revenue-drawdown-guard/demo.js +``` + +The demo writes: + +- `reports/demo-output.json` with the full portfolio evaluation +- `reports/demo.svg` with a visual portfolio summary +- `reports/demo.gif` as a short visual walkthrough artifact +- `reports/demo.mp4` as the H.264 short demo video for bounty review + +## Files + +- `index.js`: revenue drawdown, amendment, credit-expiration, margin, and audit logic +- `sample-data.js`: synthetic institutional contracts and usage +- `test.js`: dependency-free regression assertions +- `demo.js`: CLI demo plus JSON and SVG report generation +- `requirements-map.md`: mapping from issue requirements to implementation evidence + +## Scope Differentiation + +This is not another generic billing ledger, usage meter, tax guard, dispute packet, SLA credit calculator, royalty settlement module, recognition close, or revenue forecast. It focuses on the contract drawdown boundary: whether prepaid or committed institutional revenue is actually being consumed in the right stream at the right pace, and whether the customer needs a drawdown plan, a paid amendment, or an expansion motion before the next finance checkpoint. diff --git a/committed-revenue-drawdown-guard/demo.js b/committed-revenue-drawdown-guard/demo.js new file mode 100644 index 00000000..4b713bf6 --- /dev/null +++ b/committed-revenue-drawdown-guard/demo.js @@ -0,0 +1,88 @@ +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const sampleContracts = require("./sample-data"); +const { evaluatePortfolio, formatUsd } = require("./index"); + +const report = evaluatePortfolio(sampleContracts, { asOf: "2026-05-20" }); +const reportsDir = path.join(__dirname, "reports"); +fs.mkdirSync(reportsDir, { recursive: true }); +fs.writeFileSync(path.join(reportsDir, "demo-output.json"), `${JSON.stringify(report, null, 2)}\n`); + +function statusColor(status) { + if (status === "amendment-review-needed") return "#b42318"; + if (status === "monitor") return "#b54708"; + return "#027a48"; +} + +function bar(width, value, max) { + return Math.max(8, Math.round((value / max) * width)); +} + +const maxCommitted = Math.max(...report.contracts.map((contract) => contract.totals.committed)); +const rows = report.contracts + .map((contract, index) => { + const y = 170 + index * 112; + const consumedWidth = bar(420, contract.totals.consumed, maxCommitted); + const projectedWidth = bar(420, contract.totals.projectedFinal, maxCommitted); + const color = statusColor(contract.status); + const topAction = contract.actions[0]?.action || "No action needed."; + return ` + + ${contract.customer} + ${contract.recommendation} - score ${contract.revenueCertaintyScore} + + + + ${formatUsd(contract.totals.consumed)} used / ${formatUsd( + contract.totals.committed, + )} committed + ${topAction} + `; + }) + .join("\n"); + +const svg = ` + Committed revenue drawdown guard demo + Portfolio status for institutional minimum commitments across subscription, compute, and analytics revenue. + + + + Committed Revenue Drawdown Guard + Institutional contract consumption, overage, credit-expiration, and amendment readiness as of ${report.asOf} + + Committed + ${formatUsd(report.portfolio.committed)} + Consumed + ${formatUsd(report.portfolio.consumed)} + At risk + ${formatUsd(report.portfolio.atRisk)} + Expansion upside + ${formatUsd(report.portfolio.expansionUpside)} + + ${rows} + Audit digest: ${report.auditDigest} + +`; + +fs.writeFileSync(path.join(reportsDir, "demo.svg"), svg); + +console.log(`Contracts evaluated: ${report.portfolio.contractCount}`); +console.log(`Committed: ${formatUsd(report.portfolio.committed)}`); +console.log(`Consumed: ${formatUsd(report.portfolio.consumed)}`); +console.log(`At risk: ${formatUsd(report.portfolio.atRisk)}`); +console.log(`Expansion upside: ${formatUsd(report.portfolio.expansionUpside)}`); +console.log(`Top action: ${report.contracts[0].actions[0].action}`); +console.log(`Digest: ${report.auditDigest}`); diff --git a/committed-revenue-drawdown-guard/index.js b/committed-revenue-drawdown-guard/index.js new file mode 100644 index 00000000..8f200191 --- /dev/null +++ b/committed-revenue-drawdown-guard/index.js @@ -0,0 +1,328 @@ +"use strict"; + +const crypto = require("crypto"); + +const STREAMS = ["subscription", "compute", "analytics"]; + +function toMoney(value) { + const amount = Number(value); + if (!Number.isFinite(amount)) { + return 0; + } + return Math.round(amount * 100) / 100; +} + +function daysBetween(start, end) { + const startMs = new Date(`${start}T00:00:00Z`).getTime(); + const endMs = new Date(`${end}T00:00:00Z`).getTime(); + if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) { + throw new Error(`Invalid contract window: ${start} -> ${end}`); + } + return Math.max(1, Math.ceil((endMs - startMs) / 86400000) + 1); +} + +function clamp(value, min, max) { + return Math.min(max, Math.max(min, value)); +} + +function formatUsd(value) { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + maximumFractionDigits: 0, + }).format(toMoney(value)); +} + +function stableJson(value) { + if (Array.isArray(value)) { + return `[${value.map(stableJson).join(",")}]`; + } + if (value && typeof value === "object") { + return `{${Object.keys(value) + .sort() + .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`) + .join(",")}}`; + } + return JSON.stringify(value); +} + +function auditDigest(value) { + return crypto.createHash("sha256").update(stableJson(value)).digest("hex"); +} + +function summarizeStream(contract, stream, elapsedRatio) { + const committed = toMoney(contract.commitments[stream] || 0); + const consumed = toMoney( + contract.usage + .filter((entry) => entry.stream === stream) + .reduce((sum, entry) => sum + Number(entry.amountUsd || 0), 0), + ); + const expectedToDate = toMoney(committed * elapsedRatio); + const projectedFinal = elapsedRatio > 0 ? toMoney(consumed / elapsedRatio) : consumed; + const remaining = toMoney(committed - consumed); + const utilization = committed > 0 ? consumed / committed : 0; + const paceRatio = expectedToDate > 0 ? consumed / expectedToDate : 1; + const projectedVariance = toMoney(projectedFinal - committed); + + let status = "on-track"; + if (committed === 0 && consumed > 0) { + status = "uncontracted-usage"; + } else if (projectedFinal > committed * 1.12) { + status = "overage-risk"; + } else if (paceRatio < 0.72 && remaining > committed * 0.25) { + status = "under-consumption-risk"; + } else if (utilization > 0.92 && projectedFinal > committed) { + status = "expansion-ready"; + } + + return { + stream, + committed, + consumed, + remaining, + expectedToDate, + projectedFinal, + projectedVariance, + utilization: Number(utilization.toFixed(4)), + paceRatio: Number(paceRatio.toFixed(4)), + status, + }; +} + +function creditRisks(contract, asOf) { + return (contract.creditPools || []) + .map((pool) => { + const expiresInDays = daysBetween(asOf, pool.expiresOn); + const remainingUsd = toMoney(pool.remainingUsd || 0); + const flagged = + remainingUsd > 0 && + expiresInDays <= (contract.thresholds.creditExpirationWindowDays || 45); + return { + id: pool.id, + stream: pool.stream, + remainingUsd, + expiresOn: pool.expiresOn, + expiresInDays, + flagged, + }; + }) + .filter((pool) => pool.flagged); +} + +function marginRisks(contract) { + const floor = contract.thresholds.marginFloorPercent || 35; + return (contract.usage || []) + .filter((entry) => Number(entry.grossMarginPercent) < floor) + .map((entry) => ({ + usageId: entry.id, + stream: entry.stream, + amountUsd: toMoney(entry.amountUsd), + grossMarginPercent: Number(entry.grossMarginPercent), + floorPercent: floor, + description: entry.description, + })); +} + +function buildActions(contract, streamSummaries, expiringCredits, lowMarginUsage) { + const actions = []; + + for (const summary of streamSummaries) { + if (summary.status === "under-consumption-risk") { + actions.push({ + owner: "customer-success", + priority: "high", + code: `${summary.stream}-drawdown-plan`, + action: `Create a ${summary.stream} adoption plan for ${contract.customer}.`, + evidence: `${formatUsd(summary.remaining)} remains against the committed ${summary.stream} pool.`, + }); + } + if (summary.status === "overage-risk") { + actions.push({ + owner: "finance", + priority: "high", + code: `${summary.stream}-amendment-review`, + action: `Prepare a contract amendment for projected ${summary.stream} overage.`, + evidence: `Projected final usage is ${formatUsd(summary.projectedFinal)} against ${formatUsd( + summary.committed, + )} committed.`, + }); + } + if (summary.status === "expansion-ready") { + actions.push({ + owner: "account-executive", + priority: "medium", + code: `${summary.stream}-expansion-motion`, + action: `Open an expansion conversation for ${summary.stream}.`, + evidence: `${Math.round(summary.utilization * 100)}% of the committed pool is already consumed.`, + }); + } + } + + for (const pool of expiringCredits) { + actions.push({ + owner: "customer-success", + priority: "medium", + code: "credit-expiration-drawdown", + action: `Schedule usage before ${pool.id} expires.`, + evidence: `${formatUsd(pool.remainingUsd)} in ${pool.stream} credits expires in ${pool.expiresInDays} days.`, + }); + } + + for (const usage of lowMarginUsage) { + actions.push({ + owner: "finance-ops", + priority: "medium", + code: "margin-floor-review", + action: `Review margin floor for ${usage.usageId}.`, + evidence: `${usage.grossMarginPercent}% gross margin is below the ${usage.floorPercent}% floor.`, + }); + } + + return actions.sort((a, b) => { + const rank = { high: 0, medium: 1, low: 2 }; + return rank[a.priority] - rank[b.priority] || a.code.localeCompare(b.code); + }); +} + +function evaluateContract(contract, options = {}) { + const asOf = options.asOf || contract.asOf || new Date().toISOString().slice(0, 10); + const thresholds = { + marginFloorPercent: 35, + creditExpirationWindowDays: 45, + ...(contract.thresholds || {}), + }; + const normalized = { ...contract, thresholds }; + const totalDays = daysBetween(contract.periodStart, contract.periodEnd); + const elapsedDays = clamp(daysBetween(contract.periodStart, asOf), 1, totalDays); + const remainingDays = Math.max(0, totalDays - elapsedDays); + const elapsedRatio = clamp(elapsedDays / totalDays, 0.01, 1); + const streamSummaries = STREAMS.map((stream) => summarizeStream(normalized, stream, elapsedRatio)); + const committedTotal = toMoney(streamSummaries.reduce((sum, item) => sum + item.committed, 0)); + const consumedTotal = toMoney(streamSummaries.reduce((sum, item) => sum + item.consumed, 0)); + const projectedTotal = toMoney(streamSummaries.reduce((sum, item) => sum + item.projectedFinal, 0)); + const expiringCredits = creditRisks(normalized, asOf); + const lowMarginUsage = marginRisks(normalized); + const actions = buildActions(normalized, streamSummaries, expiringCredits, lowMarginUsage); + const hardRisks = actions.filter((action) => action.priority === "high").length; + const mediumRisks = actions.filter((action) => action.priority === "medium").length; + const revenueCertaintyScore = clamp(100 - hardRisks * 18 - mediumRisks * 7, 0, 100); + + let recommendation = "hold-current-contract"; + if (streamSummaries.some((stream) => stream.status === "overage-risk")) { + recommendation = "prepare-paid-amendment"; + } else if (streamSummaries.some((stream) => stream.status === "under-consumption-risk")) { + recommendation = "protect-renewal-with-drawdown-plan"; + } else if (streamSummaries.some((stream) => stream.status === "expansion-ready")) { + recommendation = "start-expansion-motion"; + } + + const status = + hardRisks > 0 ? "amendment-review-needed" : mediumRisks > 0 ? "monitor" : "ready"; + + const result = { + contractId: contract.id, + customer: contract.customer, + asOf, + period: { + start: contract.periodStart, + end: contract.periodEnd, + totalDays, + elapsedDays, + remainingDays, + elapsedRatio: Number(elapsedRatio.toFixed(4)), + }, + totals: { + committed: committedTotal, + consumed: consumedTotal, + remaining: toMoney(committedTotal - consumedTotal), + projectedFinal: projectedTotal, + projectedVariance: toMoney(projectedTotal - committedTotal), + }, + streamSummaries, + expiringCredits, + lowMarginUsage, + actions, + revenueCertaintyScore, + recommendation, + status, + }; + + return { + ...result, + auditDigest: auditDigest({ + contractId: result.contractId, + asOf: result.asOf, + totals: result.totals, + streamSummaries: result.streamSummaries, + actions: result.actions.map((action) => action.code), + recommendation: result.recommendation, + }), + }; +} + +function evaluatePortfolio(contracts, options = {}) { + const contractsEvaluated = contracts.map((contract) => evaluateContract(contract, options)); + const committed = toMoney(contractsEvaluated.reduce((sum, contract) => sum + contract.totals.committed, 0)); + const consumed = toMoney(contractsEvaluated.reduce((sum, contract) => sum + contract.totals.consumed, 0)); + const projectedFinal = toMoney( + contractsEvaluated.reduce((sum, contract) => sum + contract.totals.projectedFinal, 0), + ); + const atRisk = toMoney( + contractsEvaluated + .filter((contract) => contract.status !== "ready") + .reduce((sum, contract) => sum + Math.max(0, contract.totals.remaining), 0), + ); + const expansionUpside = toMoney( + contractsEvaluated.reduce( + (sum, contract) => + sum + + contract.streamSummaries + .filter((stream) => stream.status === "overage-risk" || stream.status === "expansion-ready") + .reduce((streamSum, stream) => streamSum + Math.max(0, stream.projectedVariance), 0), + 0, + ), + ); + + const summary = { + asOf: options.asOf || contracts[0]?.asOf || new Date().toISOString().slice(0, 10), + portfolio: { + contractCount: contractsEvaluated.length, + committed, + consumed, + projectedFinal, + projectedVariance: toMoney(projectedFinal - committed), + atRisk, + expansionUpside, + averageRevenueCertaintyScore: contractsEvaluated.length + ? Math.round( + contractsEvaluated.reduce((sum, contract) => sum + contract.revenueCertaintyScore, 0) / + contractsEvaluated.length, + ) + : 0, + }, + contracts: contractsEvaluated.sort((a, b) => a.revenueCertaintyScore - b.revenueCertaintyScore), + }; + + return { + ...summary, + auditDigest: auditDigest({ + asOf: summary.asOf, + portfolio: summary.portfolio, + contracts: summary.contracts.map((contract) => ({ + contractId: contract.contractId, + status: contract.status, + recommendation: contract.recommendation, + auditDigest: contract.auditDigest, + })), + }), + }; +} + +module.exports = { + STREAMS, + auditDigest, + evaluateContract, + evaluatePortfolio, + formatUsd, + stableJson, +}; diff --git a/committed-revenue-drawdown-guard/reports/demo-output.json b/committed-revenue-drawdown-guard/reports/demo-output.json new file mode 100644 index 00000000..14b6b324 --- /dev/null +++ b/committed-revenue-drawdown-guard/reports/demo-output.json @@ -0,0 +1,320 @@ +{ + "asOf": "2026-05-20", + "portfolio": { + "contractCount": 3, + "committed": 447000, + "consumed": 186100, + "projectedFinal": 499777.15, + "projectedVariance": 52777.15, + "atRisk": 260900, + "expansionUpside": 106350.08, + "averageRevenueCertaintyScore": 55 + }, + "contracts": [ + { + "contractId": "IC-2026-NORTHBRIDGE", + "customer": "Northbridge University Consortium", + "asOf": "2026-05-20", + "period": { + "start": "2026-01-01", + "end": "2026-12-31", + "totalDays": 365, + "elapsedDays": 140, + "remainingDays": 225, + "elapsedRatio": 0.3836 + }, + "totals": { + "committed": 265000, + "consumed": 119800, + "remaining": 145200, + "projectedFinal": 312335.71, + "projectedVariance": 47335.71 + }, + "streamSummaries": [ + { + "stream": "subscription", + "committed": 120000, + "consumed": 46300, + "remaining": 73700, + "expectedToDate": 46027.4, + "projectedFinal": 120710.71, + "projectedVariance": 710.71, + "utilization": 0.3858, + "paceRatio": 1.0059, + "status": "on-track" + }, + { + "stream": "compute", + "committed": 85000, + "consumed": 61200, + "remaining": 23800, + "expectedToDate": 32602.74, + "projectedFinal": 159557.14, + "projectedVariance": 74557.14, + "utilization": 0.72, + "paceRatio": 1.8771, + "status": "overage-risk" + }, + { + "stream": "analytics", + "committed": 60000, + "consumed": 12300, + "remaining": 47700, + "expectedToDate": 23013.7, + "projectedFinal": 32067.86, + "projectedVariance": -27932.14, + "utilization": 0.205, + "paceRatio": 0.5345, + "status": "under-consumption-risk" + } + ], + "expiringCredits": [ + { + "id": "NB-COMPUTE-SPRING", + "stream": "compute", + "remainingUsd": 9200, + "expiresOn": "2026-06-21", + "expiresInDays": 33, + "flagged": true + } + ], + "lowMarginUsage": [ + { + "usageId": "NB-COMP-042", + "stream": "compute", + "amountUsd": 61200, + "grossMarginPercent": 36, + "floorPercent": 38, + "description": "Reproducibility job cluster and model inference" + } + ], + "actions": [ + { + "owner": "customer-success", + "priority": "high", + "code": "analytics-drawdown-plan", + "action": "Create a analytics adoption plan for Northbridge University Consortium.", + "evidence": "$47,700 remains against the committed analytics pool." + }, + { + "owner": "finance", + "priority": "high", + "code": "compute-amendment-review", + "action": "Prepare a contract amendment for projected compute overage.", + "evidence": "Projected final usage is $159,557 against $85,000 committed." + }, + { + "owner": "customer-success", + "priority": "medium", + "code": "credit-expiration-drawdown", + "action": "Schedule usage before NB-COMPUTE-SPRING expires.", + "evidence": "$9,200 in compute credits expires in 33 days." + }, + { + "owner": "finance-ops", + "priority": "medium", + "code": "margin-floor-review", + "action": "Review margin floor for NB-COMP-042.", + "evidence": "36% gross margin is below the 38% floor." + } + ], + "revenueCertaintyScore": 50, + "recommendation": "prepare-paid-amendment", + "status": "amendment-review-needed", + "auditDigest": "c7c69a59c92c456dc455d57ea544fc98af7e1ac84262ec2066c304dc35ca9d67" + }, + { + "contractId": "IC-2026-HELIX", + "customer": "Helix Methods Institute", + "asOf": "2026-05-20", + "period": { + "start": "2026-03-01", + "end": "2026-08-31", + "totalDays": 184, + "elapsedDays": 81, + "remainingDays": 103, + "elapsedRatio": 0.4402 + }, + "totals": { + "committed": 82000, + "consumed": 32100, + "remaining": 49900, + "projectedFinal": 72918.51, + "projectedVariance": -9081.49 + }, + "streamSummaries": [ + { + "stream": "subscription", + "committed": 28000, + "consumed": 10800, + "remaining": 17200, + "expectedToDate": 12326.09, + "projectedFinal": 24533.33, + "projectedVariance": -3466.67, + "utilization": 0.3857, + "paceRatio": 0.8762, + "status": "on-track" + }, + { + "stream": "compute", + "committed": 22000, + "consumed": 18400, + "remaining": 3600, + "expectedToDate": 9684.78, + "projectedFinal": 41797.53, + "projectedVariance": 19797.53, + "utilization": 0.8364, + "paceRatio": 1.8999, + "status": "overage-risk" + }, + { + "stream": "analytics", + "committed": 32000, + "consumed": 2900, + "remaining": 29100, + "expectedToDate": 14086.96, + "projectedFinal": 6587.65, + "projectedVariance": -25412.35, + "utilization": 0.0906, + "paceRatio": 0.2059, + "status": "under-consumption-risk" + } + ], + "expiringCredits": [ + { + "id": "HX-ANALYTICS-PILOT", + "stream": "analytics", + "remainingUsd": 14800, + "expiresOn": "2026-06-12", + "expiresInDays": 24, + "flagged": true + } + ], + "lowMarginUsage": [ + { + "usageId": "HX-COMP-007", + "stream": "compute", + "amountUsd": 18400, + "grossMarginPercent": 29, + "floorPercent": 42, + "description": "GPU-backed peer review batch jobs" + } + ], + "actions": [ + { + "owner": "customer-success", + "priority": "high", + "code": "analytics-drawdown-plan", + "action": "Create a analytics adoption plan for Helix Methods Institute.", + "evidence": "$29,100 remains against the committed analytics pool." + }, + { + "owner": "finance", + "priority": "high", + "code": "compute-amendment-review", + "action": "Prepare a contract amendment for projected compute overage.", + "evidence": "Projected final usage is $41,798 against $22,000 committed." + }, + { + "owner": "customer-success", + "priority": "medium", + "code": "credit-expiration-drawdown", + "action": "Schedule usage before HX-ANALYTICS-PILOT expires.", + "evidence": "$14,800 in analytics credits expires in 24 days." + }, + { + "owner": "finance-ops", + "priority": "medium", + "code": "margin-floor-review", + "action": "Review margin floor for HX-COMP-007.", + "evidence": "29% gross margin is below the 42% floor." + } + ], + "revenueCertaintyScore": 50, + "recommendation": "prepare-paid-amendment", + "status": "amendment-review-needed", + "auditDigest": "f5f522b60501143c015cce971c4bba68693e7ba6218ce9c593acacb95779d2fe" + }, + { + "contractId": "IC-2026-BIOMESA", + "customer": "BioMesa Translational Lab", + "asOf": "2026-05-20", + "period": { + "start": "2026-02-01", + "end": "2027-01-31", + "totalDays": 365, + "elapsedDays": 109, + "remainingDays": 256, + "elapsedRatio": 0.2986 + }, + "totals": { + "committed": 100000, + "consumed": 34200, + "remaining": 65800, + "projectedFinal": 114522.93, + "projectedVariance": 14522.93 + }, + "streamSummaries": [ + { + "stream": "subscription", + "committed": 45000, + "consumed": 16300, + "remaining": 28700, + "expectedToDate": 13438.36, + "projectedFinal": 54582.57, + "projectedVariance": 9582.57, + "utilization": 0.3622, + "paceRatio": 1.2129, + "status": "overage-risk" + }, + { + "stream": "compute", + "committed": 40000, + "consumed": 12700, + "remaining": 27300, + "expectedToDate": 11945.21, + "projectedFinal": 42527.52, + "projectedVariance": 2527.52, + "utilization": 0.3175, + "paceRatio": 1.0632, + "status": "on-track" + }, + { + "stream": "analytics", + "committed": 15000, + "consumed": 5200, + "remaining": 9800, + "expectedToDate": 4479.45, + "projectedFinal": 17412.84, + "projectedVariance": 2412.84, + "utilization": 0.3467, + "paceRatio": 1.1609, + "status": "overage-risk" + } + ], + "expiringCredits": [], + "lowMarginUsage": [], + "actions": [ + { + "owner": "finance", + "priority": "high", + "code": "analytics-amendment-review", + "action": "Prepare a contract amendment for projected analytics overage.", + "evidence": "Projected final usage is $17,413 against $15,000 committed." + }, + { + "owner": "finance", + "priority": "high", + "code": "subscription-amendment-review", + "action": "Prepare a contract amendment for projected subscription overage.", + "evidence": "Projected final usage is $54,583 against $45,000 committed." + } + ], + "revenueCertaintyScore": 64, + "recommendation": "prepare-paid-amendment", + "status": "amendment-review-needed", + "auditDigest": "f65d1e19c243fbe1e2b252f759e68d54b40bada5618800eb91bb8ca3d94d2008" + } + ], + "auditDigest": "b5f1bbc6b5248802602f04f04835e6eb6e575c0ccf1befaabdb48d70c7119156" +} diff --git a/committed-revenue-drawdown-guard/reports/demo.gif b/committed-revenue-drawdown-guard/reports/demo.gif new file mode 100644 index 00000000..c4a63d85 Binary files /dev/null and b/committed-revenue-drawdown-guard/reports/demo.gif differ diff --git a/committed-revenue-drawdown-guard/reports/demo.mp4 b/committed-revenue-drawdown-guard/reports/demo.mp4 new file mode 100644 index 00000000..1be0b6a8 Binary files /dev/null and b/committed-revenue-drawdown-guard/reports/demo.mp4 differ diff --git a/committed-revenue-drawdown-guard/reports/demo.svg b/committed-revenue-drawdown-guard/reports/demo.svg new file mode 100644 index 00000000..7988236f --- /dev/null +++ b/committed-revenue-drawdown-guard/reports/demo.svg @@ -0,0 +1,61 @@ + + Committed revenue drawdown guard demo + Portfolio status for institutional minimum commitments across subscription, compute, and analytics revenue. + + + + Committed Revenue Drawdown Guard + Institutional contract consumption, overage, credit-expiration, and amendment readiness as of 2026-05-20 + + Committed + $447,000 + Consumed + $186,100 + At risk + $260,900 + Expansion upside + $106,350 + + + + Northbridge University Consortium + prepare-paid-amendment - score 50 + + + + $119,800 used / $265,000 committed + Create a analytics adoption plan for Northbridge University Consortium. + + + + Helix Methods Institute + prepare-paid-amendment - score 50 + + + + $32,100 used / $82,000 committed + Create a analytics adoption plan for Helix Methods Institute. + + + + BioMesa Translational Lab + prepare-paid-amendment - score 64 + + + + $34,200 used / $100,000 committed + Prepare a contract amendment for projected analytics overage. + + Audit digest: b5f1bbc6b5248802602f04f04835e6eb6e575c0ccf1befaabdb48d70c7119156 + diff --git a/committed-revenue-drawdown-guard/requirements-map.md b/committed-revenue-drawdown-guard/requirements-map.md new file mode 100644 index 00000000..d070a587 --- /dev/null +++ b/committed-revenue-drawdown-guard/requirements-map.md @@ -0,0 +1,35 @@ +# Requirements Map + +Issue #20 asks for revenue infrastructure covering subscription billing, AI compute billing, licensing APIs and analytics, institutional plans, usage-based pricing, quotas, top-ups, and sustainable recurring revenue. + +## Tiered Subscription Billing + +- `commitments.subscription` models contracted subscription minimums for institutional and lab accounts. +- `summarizeStream()` compares actual subscription consumption against expected drawdown for the elapsed contract period. +- Under-consumption recommendations create customer-success drawdown actions before renewal risk appears. + +## AI Compute Billing + +- `commitments.compute` and compute usage entries model AI compute commitments and usage drawdown. +- `marginRisks()` blocks low-margin compute usage when gross margin falls below the configured finance floor. +- Overage projections create paid-amendment review actions instead of silently allowing compute burn to exceed contracted coverage. + +## Licensing APIs And Analytics + +- `commitments.analytics` evaluates analytics licensing commitments, exports, and white-label usage. +- Credit-expiration checks identify unused analytics pools before institutional reporting windows close. +- Portfolio output separates analytics consumption from subscription and compute streams. + +## Revenue Operations Evidence + +- `evaluatePortfolio()` emits committed, consumed, projected, at-risk, and expansion-upside totals. +- `auditDigest()` signs deterministic evidence from normalized contract summaries and recommended actions. +- `demo.js` writes reviewer-ready JSON and SVG reports without any live billing provider or customer data. + +## Acceptance Evidence + +- Dependency-free implementation using Node.js standard library only. +- Synthetic sample contracts only. +- Regression tests cover overage risk, under-consumption risk, credit expiration, low-margin usage, deterministic digests, and portfolio ordering. +- Demo output can be regenerated locally with `node committed-revenue-drawdown-guard/demo.js`. +- The submitted demo bundle includes both a GIF walkthrough and `reports/demo.mp4` encoded as H.264 video for the Algora short-demo requirement. diff --git a/committed-revenue-drawdown-guard/sample-data.js b/committed-revenue-drawdown-guard/sample-data.js new file mode 100644 index 00000000..81492903 --- /dev/null +++ b/committed-revenue-drawdown-guard/sample-data.js @@ -0,0 +1,138 @@ +"use strict"; + +module.exports = [ + { + id: "IC-2026-NORTHBRIDGE", + customer: "Northbridge University Consortium", + asOf: "2026-05-20", + periodStart: "2026-01-01", + periodEnd: "2026-12-31", + commitments: { + subscription: 120000, + compute: 85000, + analytics: 60000, + }, + thresholds: { + marginFloorPercent: 38, + creditExpirationWindowDays: 60, + }, + creditPools: [ + { + id: "NB-COMPUTE-SPRING", + stream: "compute", + remainingUsd: 9200, + expiresOn: "2026-06-21", + }, + ], + usage: [ + { + id: "NB-SUB-001", + stream: "subscription", + amountUsd: 46300, + grossMarginPercent: 82, + description: "Research group seats through May", + }, + { + id: "NB-COMP-042", + stream: "compute", + amountUsd: 61200, + grossMarginPercent: 36, + description: "Reproducibility job cluster and model inference", + }, + { + id: "NB-LIC-008", + stream: "analytics", + amountUsd: 12300, + grossMarginPercent: 71, + description: "Dataset reuse dashboard exports", + }, + ], + }, + { + id: "IC-2026-BIOMESA", + customer: "BioMesa Translational Lab", + asOf: "2026-05-20", + periodStart: "2026-02-01", + periodEnd: "2027-01-31", + commitments: { + subscription: 45000, + compute: 40000, + analytics: 15000, + }, + thresholds: { + marginFloorPercent: 35, + creditExpirationWindowDays: 45, + }, + creditPools: [], + usage: [ + { + id: "BM-SUB-002", + stream: "subscription", + amountUsd: 16300, + grossMarginPercent: 80, + description: "Lab workspace seats", + }, + { + id: "BM-COMP-011", + stream: "compute", + amountUsd: 12700, + grossMarginPercent: 49, + description: "Notebook reruns and summarization jobs", + }, + { + id: "BM-LIC-003", + stream: "analytics", + amountUsd: 5200, + grossMarginPercent: 72, + description: "Citation network API package", + }, + ], + }, + { + id: "IC-2026-HELIX", + customer: "Helix Methods Institute", + asOf: "2026-05-20", + periodStart: "2026-03-01", + periodEnd: "2026-08-31", + commitments: { + subscription: 28000, + compute: 22000, + analytics: 32000, + }, + thresholds: { + marginFloorPercent: 42, + creditExpirationWindowDays: 50, + }, + creditPools: [ + { + id: "HX-ANALYTICS-PILOT", + stream: "analytics", + remainingUsd: 14800, + expiresOn: "2026-06-12", + }, + ], + usage: [ + { + id: "HX-SUB-001", + stream: "subscription", + amountUsd: 10800, + grossMarginPercent: 79, + description: "Institutional pilot seats", + }, + { + id: "HX-COMP-007", + stream: "compute", + amountUsd: 18400, + grossMarginPercent: 29, + description: "GPU-backed peer review batch jobs", + }, + { + id: "HX-LIC-002", + stream: "analytics", + amountUsd: 2900, + grossMarginPercent: 69, + description: "White-labeled reproducibility trend export", + }, + ], + }, +]; diff --git a/committed-revenue-drawdown-guard/test.js b/committed-revenue-drawdown-guard/test.js new file mode 100644 index 00000000..c1596eed --- /dev/null +++ b/committed-revenue-drawdown-guard/test.js @@ -0,0 +1,35 @@ +"use strict"; + +const assert = require("assert"); +const sampleContracts = require("./sample-data"); +const { evaluateContract, evaluatePortfolio } = require("./index"); + +function byStream(contract, stream) { + return contract.streamSummaries.find((summary) => summary.stream === stream); +} + +const northbridge = evaluateContract(sampleContracts[0], { asOf: "2026-05-20" }); +assert.strictEqual(northbridge.contractId, "IC-2026-NORTHBRIDGE"); +assert.strictEqual(byStream(northbridge, "compute").status, "overage-risk"); +assert.strictEqual(byStream(northbridge, "analytics").status, "under-consumption-risk"); +assert(northbridge.actions.some((action) => action.code === "compute-amendment-review")); +assert(northbridge.actions.some((action) => action.code === "analytics-drawdown-plan")); + +const helix = evaluateContract(sampleContracts[2], { asOf: "2026-05-20" }); +assert.strictEqual(byStream(helix, "analytics").status, "under-consumption-risk"); +assert.strictEqual(helix.expiringCredits.length, 1); +assert.strictEqual(helix.lowMarginUsage.length, 1); +assert(helix.revenueCertaintyScore < 70); + +const stableA = evaluateContract(sampleContracts[1], { asOf: "2026-05-20" }).auditDigest; +const stableB = evaluateContract(sampleContracts[1], { asOf: "2026-05-20" }).auditDigest; +assert.strictEqual(stableA, stableB); + +const portfolio = evaluatePortfolio(sampleContracts, { asOf: "2026-05-20" }); +assert.strictEqual(portfolio.portfolio.contractCount, 3); +assert(portfolio.portfolio.committed > portfolio.portfolio.consumed); +assert(portfolio.portfolio.atRisk > 0); +assert(portfolio.contracts[0].revenueCertaintyScore <= portfolio.contracts[1].revenueCertaintyScore); +assert.strictEqual(portfolio.auditDigest.length, 64); + +console.log("committed-revenue-drawdown-guard tests passed");