Skip to content
Open
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
20 changes: 20 additions & 0 deletions revenue-recognition-deferral-guard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "revenue-recognition-deferral-guard",
"version": "1.0.0",
"description": "Synthetic deferred revenue and recognition guard for SCIBASE revenue infrastructure.",
"type": "module",
"private": true,
"scripts": {
"check": "node --check src/index.js && node --check test/index.test.js && node --check scripts/demo.js",
"test": "node --test test/index.test.js",
"demo": "node scripts/demo.js"
},
"keywords": [
"scibase",
"revenue",
"deferred-revenue",
"recognition",
"finance-controls"
],
"license": "MIT"
}
38 changes: 38 additions & 0 deletions revenue-recognition-deferral-guard/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Revenue Recognition Deferral Guard

This module is a focused SCIBASE.AI issue #20 submission for Revenue Infrastructure.

It validates synthetic finance-close packets before revenue is accepted as recognized. The guard checks annual subscription service periods, AI compute credit consumption, licensing/export delivery evidence, and allocation reconciliation so unearned amounts stay in deferred revenue until delivery is supported.

## What It Covers

- Performance obligation allocation reconciliation against invoice totals.
- Subscription revenue recognition based on elapsed service period.
- AI compute-credit revenue recognition only after consumption or expiry.
- Licensing/export revenue requiring delivery evidence before close.
- Deterministic finance remediation actions for held obligations.

## What It Does Not Do

- No processor, financial-account, tax-filing, invoice-send, refund-send, or external service calls.
- No personal billing data, cards, account numbers, payout details, or real customer records.
- No live SCIBASE revenue is read or mutated.
- No refund-liability, cancellation, priority-support, quote approval, or named-seat roster module is reimplemented.

## Reviewer Path

```bash
npm run check
npm test
npm run demo
```

Generated reviewer artifacts:

- `reports/revenue-recognition-packet.json`
- `reports/revenue-recognition-report.md`
- `reports/summary.svg`

## Claim

Use `/claim #20` in the pull request body.
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"title": "SCIBASE Revenue Recognition Deferral Guard",
"issue": "SCIBASE.AI#20",
"claim": "/claim #20",
"purpose": "Prevent revenue close packets from recognizing subscription, compute-credit, or licensing revenue before delivery evidence exists.",
"evaluation": {
"status": "block_close",
"asOf": "2026-06-04T00:00:00.000Z",
"packetId": "scibase-revenue-recognition-close-2026-06",
"digest": "8b6865927e5e430b33924f2853f0d9c0e7c005bb6002eabd6250d82d57c0ebe2",
"totals": {
"contracts": 2,
"obligations": 3,
"acceptedRecognizedCents": 0,
"allowedRecognizedCents": 908800,
"deferredCents": 2391200,
"blockers": 6,
"warnings": 0,
"heldObligations": 3
},
"blockers": [
{
"code": "premature_revenue_recognition",
"contractId": "contract:lab-annual-pro",
"obligationId": "obl:annual-subscription",
"recognizedCents": 1800000,
"allowedRecognizedCents": 759452,
"message": "Revenue is recognized before the obligation is sufficiently delivered."
},
{
"code": "premature_revenue_recognition",
"contractId": "contract:lab-annual-pro",
"obligationId": "obl:compute-credits",
"recognizedCents": 450000,
"allowedRecognizedCents": 120000,
"message": "Revenue is recognized before the obligation is sufficiently delivered."
},
{
"code": "usage_credits_recognized_before_consumption",
"contractId": "contract:lab-annual-pro",
"obligationId": "obl:compute-credits",
"consumedCents": 120000,
"recognizedCents": 450000,
"message": "Usage credit revenue exceeds consumed or expired value."
},
{
"code": "missing_recognition_evidence",
"contractId": "contract:lab-annual-pro",
"obligationId": "obl:compute-credits",
"message": "Recognized revenue lacks required delivery evidence."
},
{
"code": "premature_revenue_recognition",
"contractId": "contract:consortium-license",
"obligationId": "obl:analytics-license-export",
"recognizedCents": 900000,
"allowedRecognizedCents": 29348,
"message": "Revenue is recognized before the obligation is sufficiently delivered."
},
{
"code": "missing_recognition_evidence",
"contractId": "contract:consortium-license",
"obligationId": "obl:analytics-license-export",
"message": "Recognized revenue lacks required delivery evidence."
}
],
"warnings": [],
"actions": [],
"obligationActions": [
{
"contractId": "contract:lab-annual-pro",
"obligationId": "obl:annual-subscription",
"kind": "subscription",
"decision": "hold",
"allocatedCents": 1800000,
"recognizedCents": 1800000,
"allowedRecognizedCents": 759452,
"deferredCents": 1040548,
"reasons": [
"premature_recognition"
],
"requiredActions": [
"Move unearned revenue into deferred revenue until service is delivered."
]
},
{
"contractId": "contract:lab-annual-pro",
"obligationId": "obl:compute-credits",
"kind": "usage_credit",
"decision": "hold",
"allocatedCents": 600000,
"recognizedCents": 450000,
"allowedRecognizedCents": 120000,
"deferredCents": 480000,
"reasons": [
"premature_recognition",
"unconsumed_usage_credits",
"missing_recognition_evidence"
],
"requiredActions": [
"Move unearned revenue into deferred revenue until service is delivered.",
"Recognize compute-credit revenue only as credits are consumed or expire.",
"Attach delivery, consumption, or export evidence for the recognized amount."
]
},
{
"contractId": "contract:consortium-license",
"obligationId": "obl:analytics-license-export",
"kind": "license_export",
"decision": "hold",
"allocatedCents": 900000,
"recognizedCents": 900000,
"allowedRecognizedCents": 29348,
"deferredCents": 870652,
"reasons": [
"premature_recognition",
"missing_recognition_evidence"
],
"requiredActions": [
"Move unearned revenue into deferred revenue until service is delivered.",
"Attach delivery, consumption, or export evidence for the recognized amount."
]
}
],
"policy": {
"maxRoundingDriftCents": 2,
"requireEvidenceForRecognition": true,
"blockFutureServiceRecognition": true,
"blockUnconsumedUsageCredits": true,
"requireContractWindow": true,
"requireAllocationMatchesInvoice": true
}
},
"reviewerChecklist": [
"Invoice amount reconciles to performance obligation allocations.",
"Subscription revenue is deferred until the service period is earned.",
"Compute-credit revenue is recognized only on consumption or expiry.",
"Licensing/export revenue has delivery evidence before close.",
"Held obligations include deterministic remediation actions."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Revenue Recognition Deferral Guard Report

Issue: SCIBASE.AI#20
Claim marker: `/claim #20`
Status: `block_close`
Digest: `8b6865927e5e430b33924f2853f0d9c0e7c005bb6002eabd6250d82d57c0ebe2`

## Reviewer Checklist
- Invoice amount reconciles to performance obligation allocations.
- Subscription revenue is deferred until the service period is earned.
- Compute-credit revenue is recognized only on consumption or expiry.
- Licensing/export revenue has delivery evidence before close.
- Held obligations include deterministic remediation actions.

## Totals
- Contracts inspected: 2
- Obligations inspected: 3
- Allowed recognized revenue: $9088.00
- Deferred revenue: $23912.00
- Held obligations: 3
- Blockers: 6
- Warnings: 0

## Blockers
- premature_revenue_recognition: Revenue is recognized before the obligation is sufficiently delivered.
- premature_revenue_recognition: Revenue is recognized before the obligation is sufficiently delivered.
- usage_credits_recognized_before_consumption: Usage credit revenue exceeds consumed or expired value.
- missing_recognition_evidence: Recognized revenue lacks required delivery evidence.
- premature_revenue_recognition: Revenue is recognized before the obligation is sufficiently delivered.
- missing_recognition_evidence: Recognized revenue lacks required delivery evidence.

## Required Actions
- No finance actions required.

## Obligation Decisions
- contract:lab-annual-pro/obl:annual-subscription: hold; recognized $18000.00, allowed $7594.52, deferred $10405.48
- contract:lab-annual-pro/obl:compute-credits: hold; recognized $4500.00, allowed $1200.00, deferred $4800.00
- contract:consortium-license/obl:analytics-license-export: hold; recognized $9000.00, allowed $293.48, deferred $8706.52
18 changes: 18 additions & 0 deletions revenue-recognition-deferral-guard/reports/summary.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions revenue-recognition-deferral-guard/scripts/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

import {
buildReviewerPacket,
demoPacket,
renderMarkdownReport,
renderSvgSummary
} from "../src/index.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const moduleRoot = path.resolve(__dirname, "..");
const reportsDir = path.join(moduleRoot, "reports");
const packet = demoPacket();
const reviewerPacket = buildReviewerPacket(packet, { asOf: packet.asOf });

fs.mkdirSync(reportsDir, { recursive: true });
fs.writeFileSync(path.join(reportsDir, "revenue-recognition-packet.json"), `${JSON.stringify(reviewerPacket, null, 2)}\n`);
fs.writeFileSync(path.join(reportsDir, "revenue-recognition-report.md"), renderMarkdownReport(packet, { asOf: packet.asOf }));
fs.writeFileSync(path.join(reportsDir, "summary.svg"), renderSvgSummary(packet, { asOf: packet.asOf }));

console.log(JSON.stringify({
status: reviewerPacket.evaluation.status,
digest: reviewerPacket.evaluation.digest,
blockers: reviewerPacket.evaluation.totals.blockers,
deferredCents: reviewerPacket.evaluation.totals.deferredCents,
reportsDir
}, null, 2));
Loading