From c9be343ff7294d252ea3c8ead905705a4b1c6365 Mon Sep 17 00:00:00 2001 From: Aion Date: Thu, 25 Jun 2026 21:16:08 +0200 Subject: [PATCH] Add event sponsorship revenue readiness guard --- .../README.md | 50 ++++ .../demo.js | 71 +++++ .../index.js | 265 ++++++++++++++++++ .../package.json | 13 + .../reports/demo.mp4 | Bin 0 -> 42918 bytes .../event-sponsorship-readiness-report.json | 263 +++++++++++++++++ .../event-sponsorship-readiness-report.md | 27 ++ .../event-sponsorship-readiness-summary.svg | 24 ++ .../sample-data.js | 135 +++++++++ .../test.js | 55 ++++ 10 files changed, 903 insertions(+) create mode 100644 event-sponsorship-revenue-readiness-guard/README.md create mode 100644 event-sponsorship-revenue-readiness-guard/demo.js create mode 100644 event-sponsorship-revenue-readiness-guard/index.js create mode 100644 event-sponsorship-revenue-readiness-guard/package.json create mode 100644 event-sponsorship-revenue-readiness-guard/reports/demo.mp4 create mode 100644 event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.json create mode 100644 event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.md create mode 100644 event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-summary.svg create mode 100644 event-sponsorship-revenue-readiness-guard/sample-data.js create mode 100644 event-sponsorship-revenue-readiness-guard/test.js diff --git a/event-sponsorship-revenue-readiness-guard/README.md b/event-sponsorship-revenue-readiness-guard/README.md new file mode 100644 index 00000000..9b83e7dc --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/README.md @@ -0,0 +1,50 @@ +# Event Sponsorship Revenue Readiness Guard + +This module is a self-contained revenue-infrastructure slice for event sponsorship packages. It determines whether a sponsorship packet is ready to invoice, needs finance review, or must be held before release. + +It is intentionally aligned with the event-oriented surface of this repository (`get-event-sponsorship`, event CRM, and event operations) rather than adding another generic billing ledger. + +## What It Checks + +- Sponsorship contract signature and sponsor approval +- Purchase order or finance approval readiness +- Required sponsorship deliverables and sponsor signoff +- Attendee lead guarantees and make-good risk +- Sponsor category exclusivity conflicts +- Cancellation/refund exposure windows +- Proof artifacts required for audit-ready invoicing + +## Decisions + +- `RELEASE_INVOICE`: evidence is complete enough to invoice or release revenue. +- `REVIEW_BEFORE_RELEASE`: finance or sponsorship owner should review before release. +- `HOLD_INVOICE`: material blocker exists; invoice/revenue release should not proceed. + +## Run + +```bash +npm run check +npm test +npm run demo +``` + +The demo writes: + +- `reports/event-sponsorship-readiness-report.json` +- `reports/event-sponsorship-readiness-report.md` +- `reports/event-sponsorship-readiness-summary.svg` +- `reports/demo.mp4` + +## Requirement Mapping + +| Issue #20 requirement | Module coverage | +| --- | --- | +| Subscription and institutional revenue controls | Sponsor package readiness and finance approval controls | +| Usage/value-aligned monetization | Lead guarantee and deliverable evidence checks before invoice release | +| Licensing/API analytics revenue discipline | Proof-artifact and approval gates for sponsor-facing revenue claims | +| Predictable recurring revenue | Prevents premature invoicing, refund exposure, and exclusivity conflicts | +| Secure payment integrations | Does not call payment processors; emits hold/release decisions before finance action | + +## Safety Boundary + +This module uses synthetic data only. It does not call Stripe, PayPal, banks, ERPs, CRMs, sponsor portals, payment processors, external APIs, or accounting systems. It reads no credentials and contains no real sponsor/customer data. diff --git a/event-sponsorship-revenue-readiness-guard/demo.js b/event-sponsorship-revenue-readiness-guard/demo.js new file mode 100644 index 00000000..be924bce --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/demo.js @@ -0,0 +1,71 @@ +const fs = require("fs"); +const path = require("path"); +const { evaluateSponsorshipRevenueReadiness, summarizeEvaluations } = require("./index"); +const { sponsorshipPackets } = require("./sample-data"); + +const reportsDir = path.join(__dirname, "reports"); +fs.mkdirSync(reportsDir, { recursive: true }); + +const evaluations = sponsorshipPackets.map(evaluateSponsorshipRevenueReadiness); +const summary = summarizeEvaluations(evaluations); + +fs.writeFileSync( + path.join(reportsDir, "event-sponsorship-readiness-report.json"), + JSON.stringify({ summary, evaluations }, null, 2) +); + +const markdown = [ + "# Event Sponsorship Revenue Readiness Guard - Demo Report", + "", + "Synthetic reviewer demo for SCIBASE Revenue Infrastructure issue #20.", + "", + "## Summary", + "", + `- Packets evaluated: ${summary.packet_count}`, + `- Ready to invoice: ${summary.packets_ready_to_invoice}`, + `- Needs finance review: ${summary.packets_requiring_review}`, + `- Held before invoice: ${summary.packets_on_hold}`, + "", + "## Decisions", + "", + "| Packet | Sponsor | Tier | Decision | Score | Top reasons |", + "| --- | --- | --- | --- | ---: | --- |", + ...evaluations.map((item) => { + const topReasons = item.reasons.slice(0, 3).map((reason) => reason.code).join(", ") || "none"; + return `| ${item.packet_id} | ${item.sponsor_id} | ${item.package_tier} | ${item.decision} | ${item.readiness_score} | ${topReasons} |`; + }), + "", + "## Boundary", + "", + "- Synthetic data only.", + "- No payment processors called.", + "- No real sponsor/customer data used.", + "- No external APIs used.", + "- No credentials, bank data, Stripe, PayPal, ERP, or accounting systems touched.", + "" +].join("\n"); + +fs.writeFileSync(path.join(reportsDir, "event-sponsorship-readiness-report.md"), markdown); + +const bar = (label, value, color, y) => ` + ${label} + + + ${value}`; + +const svg = ` + + Event Sponsorship Revenue Readiness + Synthetic guard demo for invoice release, finance review, and hold decisions. + ${bar("RELEASE_INVOICE", summary.decision_counts.RELEASE_INVOICE, "#16a34a", 160)} + ${bar("REVIEW_BEFORE_RELEASE", summary.decision_counts.REVIEW_BEFORE_RELEASE, "#f59e0b", 220)} + ${bar("HOLD_INVOICE", summary.decision_counts.HOLD_INVOICE, "#dc2626", 280)} + + Boundary + Synthetic data only · no payment processors · no external APIs · no credentials + Designed to make event sponsorship revenue release auditable before invoice actions. +`; + +fs.writeFileSync(path.join(reportsDir, "event-sponsorship-readiness-summary.svg"), svg.replace(/[ \t]+$/gm, "")); + +console.log(JSON.stringify({ summary, report_dir: reportsDir }, null, 2)); diff --git a/event-sponsorship-revenue-readiness-guard/index.js b/event-sponsorship-revenue-readiness-guard/index.js new file mode 100644 index 00000000..a729d264 --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/index.js @@ -0,0 +1,265 @@ +const crypto = require("crypto"); + +const DECISIONS = Object.freeze({ + RELEASE: "RELEASE_INVOICE", + REVIEW: "REVIEW_BEFORE_RELEASE", + HOLD: "HOLD_INVOICE" +}); + +const SEVERITY_WEIGHT = Object.freeze({ + info: 0, + review: 1, + hold: 3 +}); + +function stableStringify(value) { + if (Array.isArray(value)) { + return `[${value.map(stableStringify).join(",")}]`; + } + if (value && typeof value === "object") { + return `{${Object.keys(value) + .sort() + .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`) + .join(",")}}`; + } + return JSON.stringify(value); +} + +function stableHash(value) { + return crypto + .createHash("sha256") + .update(stableStringify(value)) + .digest("hex"); +} + +function reason(code, severity, message, remediation) { + return { code, severity, message, remediation }; +} + +function missingRequiredDeliverables(packet) { + return (packet.deliverables || []).filter((item) => item.required && !item.completed); +} + +function unsignedRequiredDeliverables(packet) { + return (packet.deliverables || []).filter( + (item) => item.required && item.completed && !item.sponsorApproved + ); +} + +function missingEvidence(packet) { + return (packet.evidence || []).filter((item) => !item.present); +} + +function evaluateSponsorshipRevenueReadiness(packet) { + const reasons = []; + const financeActions = []; + const contract = packet.contract || {}; + const leadGuarantee = packet.leadGuarantee || {}; + const exclusivity = packet.exclusivity || {}; + const missingDeliverables = missingRequiredDeliverables(packet); + const unsignedDeliverables = unsignedRequiredDeliverables(packet); + const evidenceGaps = missingEvidence(packet); + const delivered = Number(leadGuarantee.qualifiedDelivered || 0); + const promised = Number(leadGuarantee.promised || 0); + const minimumAcceptable = Number(leadGuarantee.minimumAcceptable || 0); + const leadShortfall = Math.max(0, minimumAcceptable - delivered); + + if (!contract.signed) { + reasons.push( + reason( + "SPONSOR_CONTRACT_UNSIGNED", + "hold", + "Sponsorship contract is not signed.", + "Collect signed sponsorship agreement before invoicing or releasing revenue." + ) + ); + financeActions.push("hold invoice until contract is signed"); + } + + if (!contract.purchaseOrderApproved) { + reasons.push( + reason( + "PURCHASE_ORDER_NOT_APPROVED", + contract.signed ? "review" : "hold", + "Purchase order or finance approval is missing.", + "Attach approved PO or finance approval before invoice release." + ) + ); + financeActions.push("request PO or finance approval evidence"); + } + + if (!contract.sponsorApproval) { + reasons.push( + reason( + "SPONSOR_APPROVAL_MISSING", + "hold", + "Sponsor has not approved the final package state.", + "Obtain sponsor signoff for package scope before recognizing revenue readiness." + ) + ); + financeActions.push("collect sponsor package approval"); + } + + if (!contract.cancellationWindowClosed || Number(contract.refundExposureUsd || 0) > 0) { + reasons.push( + reason( + "REFUND_WINDOW_OR_EXPOSURE_OPEN", + "hold", + "Cancellation or refund exposure is still open.", + "Defer invoice release or record finance review until refund exposure is cleared." + ) + ); + financeActions.push("defer release until cancellation/refund exposure closes"); + } + + if ((exclusivity.conflicts || []).length > 0) { + reasons.push( + reason( + "SPONSOR_EXCLUSIVITY_CONFLICT", + "hold", + "Sponsor category exclusivity conflicts with another sponsor.", + "Resolve category conflict or amend sponsorship package before release." + ) + ); + financeActions.push("route exclusivity conflict to sponsorship owner"); + } + + for (const deliverable of missingDeliverables) { + reasons.push( + reason( + "REQUIRED_DELIVERABLE_INCOMPLETE", + "hold", + `Required deliverable is incomplete: ${deliverable.label}.`, + "Complete the deliverable or reduce the invoiceable package scope." + ) + ); + } + + for (const deliverable of unsignedDeliverables) { + reasons.push( + reason( + "DELIVERABLE_SIGNOFF_MISSING", + "review", + `Required deliverable lacks sponsor signoff: ${deliverable.label}.`, + "Collect sponsor signoff or mark the line item for manual finance review." + ) + ); + } + + if (leadShortfall > 0) { + const severity = delivered < promised * 0.75 ? "hold" : "review"; + reasons.push( + reason( + "ATTENDEE_LEAD_GUARANTEE_SHORTFALL", + severity, + `Qualified leads delivered (${delivered}) are below the acceptable floor (${minimumAcceptable}).`, + "Deliver remaining qualified leads, apply make-good credit, or reduce invoice amount." + ) + ); + financeActions.push("calculate make-good credit or revised invoice amount"); + } + + for (const gap of evidenceGaps) { + reasons.push( + reason( + "PROOF_ARTIFACT_MISSING", + "review", + `Proof artifact is missing: ${gap.type}.`, + "Attach proof artifact before revenue packet is marked audit-ready." + ) + ); + } + + const highestWeight = reasons.reduce( + (weight, item) => Math.max(weight, SEVERITY_WEIGHT[item.severity] || 0), + 0 + ); + const decision = + highestWeight >= SEVERITY_WEIGHT.hold + ? DECISIONS.HOLD + : highestWeight >= SEVERITY_WEIGHT.review + ? DECISIONS.REVIEW + : DECISIONS.RELEASE; + + const completedRequired = (packet.deliverables || []).filter( + (item) => item.required && item.completed && item.sponsorApproved + ).length; + const totalRequired = (packet.deliverables || []).filter((item) => item.required).length; + const deliverableReadiness = totalRequired === 0 ? 100 : Math.round((completedRequired / totalRequired) * 100); + const leadReadiness = + minimumAcceptable === 0 ? 100 : Math.min(100, Math.round((delivered / minimumAcceptable) * 100)); + const evidenceReadiness = + (packet.evidence || []).length === 0 + ? 0 + : Math.round((((packet.evidence || []).length - evidenceGaps.length) / (packet.evidence || []).length) * 100); + + const readinessScore = Math.max( + 0, + Math.min( + 100, + Math.round( + deliverableReadiness * 0.35 + + leadReadiness * 0.25 + + evidenceReadiness * 0.2 + + (contract.signed ? 10 : 0) + + (contract.purchaseOrderApproved ? 10 : 0) + ) + ) + ); + + return { + schema_version: "event_sponsorship_revenue_readiness_guard_v1", + packet_id: packet.id, + event_id: packet.eventId, + sponsor_id: packet.sponsorId, + package_tier: packet.packageTier, + invoice_amount_usd: packet.invoiceAmountUsd, + decision, + readiness_score: readinessScore, + reason_count: reasons.length, + reasons, + finance_actions: [...new Set(financeActions)], + metrics: { + deliverable_readiness_percent: deliverableReadiness, + lead_readiness_percent: leadReadiness, + evidence_readiness_percent: evidenceReadiness, + lead_shortfall: leadShortfall, + exclusivity_conflict_count: (exclusivity.conflicts || []).length, + refund_exposure_usd: Number(contract.refundExposureUsd || 0) + }, + audit_packet: { + synthetic_data_only: true, + external_apis_used: false, + payment_processors_called: false, + private_customer_data_used: false, + packet_sha256: stableHash(packet) + } + }; +} + +function summarizeEvaluations(evaluations) { + const counts = evaluations.reduce( + (acc, item) => { + acc[item.decision] = (acc[item.decision] || 0) + 1; + return acc; + }, + { [DECISIONS.RELEASE]: 0, [DECISIONS.REVIEW]: 0, [DECISIONS.HOLD]: 0 } + ); + + return { + schema_version: "event_sponsorship_revenue_readiness_summary_v1", + packet_count: evaluations.length, + decision_counts: counts, + packets_ready_to_invoice: counts[DECISIONS.RELEASE], + packets_requiring_review: counts[DECISIONS.REVIEW], + packets_on_hold: counts[DECISIONS.HOLD], + audit_note: + "Synthetic event sponsorship packets only; no payment processors, bank data, credentials, or external APIs used." + }; +} + +module.exports = { + DECISIONS, + evaluateSponsorshipRevenueReadiness, + summarizeEvaluations +}; diff --git a/event-sponsorship-revenue-readiness-guard/package.json b/event-sponsorship-revenue-readiness-guard/package.json new file mode 100644 index 00000000..68ec38b4 --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/package.json @@ -0,0 +1,13 @@ +{ + "name": "event-sponsorship-revenue-readiness-guard", + "version": "1.0.0", + "description": "Deterministic revenue readiness guard for event sponsorship invoices and release decisions.", + "private": true, + "type": "commonjs", + "scripts": { + "check": "node --check index.js && node --check sample-data.js && node --check test.js && node --check demo.js", + "test": "node test.js", + "demo": "node demo.js" + }, + "license": "MIT" +} diff --git a/event-sponsorship-revenue-readiness-guard/reports/demo.mp4 b/event-sponsorship-revenue-readiness-guard/reports/demo.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..dc4b56367c1b77698c32a8ba7d5d826c5f322f01 GIT binary patch literal 42918 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 Ks?Hcx(*XdCf5hPc literal 0 HcmV?d00001 diff --git a/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.json b/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.json new file mode 100644 index 00000000..1f4ddba6 --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.json @@ -0,0 +1,263 @@ +{ + "summary": { + "schema_version": "event_sponsorship_revenue_readiness_summary_v1", + "packet_count": 4, + "decision_counts": { + "RELEASE_INVOICE": 1, + "REVIEW_BEFORE_RELEASE": 1, + "HOLD_INVOICE": 2 + }, + "packets_ready_to_invoice": 1, + "packets_requiring_review": 1, + "packets_on_hold": 2, + "audit_note": "Synthetic event sponsorship packets only; no payment processors, bank data, credentials, or external APIs used." + }, + "evaluations": [ + { + "schema_version": "event_sponsorship_revenue_readiness_guard_v1", + "packet_id": "packet-ready-platinum", + "event_id": "deep-events-berlin-2026", + "sponsor_id": "sponsor-orion-labs", + "package_tier": "platinum", + "invoice_amount_usd": 25000, + "decision": "RELEASE_INVOICE", + "readiness_score": 100, + "reason_count": 0, + "reasons": [], + "finance_actions": [], + "metrics": { + "deliverable_readiness_percent": 100, + "lead_readiness_percent": 100, + "evidence_readiness_percent": 100, + "lead_shortfall": 0, + "exclusivity_conflict_count": 0, + "refund_exposure_usd": 0 + }, + "audit_packet": { + "synthetic_data_only": true, + "external_apis_used": false, + "payment_processors_called": false, + "private_customer_data_used": false, + "packet_sha256": "09ddd62a317d1c6e6b7e91579183eb22dd02e7df5f0e9ac9e7c518f529d68e2e" + } + }, + { + "schema_version": "event_sponsorship_revenue_readiness_guard_v1", + "packet_id": "packet-review-logo-signoff", + "event_id": "deep-events-paris-2026", + "sponsor_id": "sponsor-nova-data", + "package_tier": "gold", + "invoice_amount_usd": 14000, + "decision": "REVIEW_BEFORE_RELEASE", + "readiness_score": 82, + "reason_count": 2, + "reasons": [ + { + "code": "DELIVERABLE_SIGNOFF_MISSING", + "severity": "review", + "message": "Required deliverable lacks sponsor signoff: Homepage logo placement.", + "remediation": "Collect sponsor signoff or mark the line item for manual finance review." + }, + { + "code": "PROOF_ARTIFACT_MISSING", + "severity": "review", + "message": "Proof artifact is missing: deliverable_signoff.", + "remediation": "Attach proof artifact before revenue packet is marked audit-ready." + } + ], + "finance_actions": [], + "metrics": { + "deliverable_readiness_percent": 67, + "lead_readiness_percent": 100, + "evidence_readiness_percent": 67, + "lead_shortfall": 0, + "exclusivity_conflict_count": 0, + "refund_exposure_usd": 0 + }, + "audit_packet": { + "synthetic_data_only": true, + "external_apis_used": false, + "payment_processors_called": false, + "private_customer_data_used": false, + "packet_sha256": "c52dadd3606e19ac006fe5ae187549aab0fd0887bef26510c79569a4d1bb183a" + } + }, + { + "schema_version": "event_sponsorship_revenue_readiness_guard_v1", + "packet_id": "packet-hold-exclusivity", + "event_id": "deep-events-lisbon-2026", + "sponsor_id": "sponsor-vector-capital", + "package_tier": "platinum", + "invoice_amount_usd": 30000, + "decision": "HOLD_INVOICE", + "readiness_score": 38, + "reason_count": 8, + "reasons": [ + { + "code": "PURCHASE_ORDER_NOT_APPROVED", + "severity": "review", + "message": "Purchase order or finance approval is missing.", + "remediation": "Attach approved PO or finance approval before invoice release." + }, + { + "code": "REFUND_WINDOW_OR_EXPOSURE_OPEN", + "severity": "hold", + "message": "Cancellation or refund exposure is still open.", + "remediation": "Defer invoice release or record finance review until refund exposure is cleared." + }, + { + "code": "SPONSOR_EXCLUSIVITY_CONFLICT", + "severity": "hold", + "message": "Sponsor category exclusivity conflicts with another sponsor.", + "remediation": "Resolve category conflict or amend sponsorship package before release." + }, + { + "code": "REQUIRED_DELIVERABLE_INCOMPLETE", + "severity": "hold", + "message": "Required deliverable is incomplete: Exclusive category slot.", + "remediation": "Complete the deliverable or reduce the invoiceable package scope." + }, + { + "code": "REQUIRED_DELIVERABLE_INCOMPLETE", + "severity": "hold", + "message": "Required deliverable is incomplete: Qualified attendee lead export.", + "remediation": "Complete the deliverable or reduce the invoiceable package scope." + }, + { + "code": "ATTENDEE_LEAD_GUARANTEE_SHORTFALL", + "severity": "hold", + "message": "Qualified leads delivered (74) are below the acceptable floor (180).", + "remediation": "Deliver remaining qualified leads, apply make-good credit, or reduce invoice amount." + }, + { + "code": "PROOF_ARTIFACT_MISSING", + "severity": "review", + "message": "Proof artifact is missing: deliverable_signoff.", + "remediation": "Attach proof artifact before revenue packet is marked audit-ready." + }, + { + "code": "PROOF_ARTIFACT_MISSING", + "severity": "review", + "message": "Proof artifact is missing: lead_export_hash.", + "remediation": "Attach proof artifact before revenue packet is marked audit-ready." + } + ], + "finance_actions": [ + "request PO or finance approval evidence", + "defer release until cancellation/refund exposure closes", + "route exclusivity conflict to sponsorship owner", + "calculate make-good credit or revised invoice amount" + ], + "metrics": { + "deliverable_readiness_percent": 33, + "lead_readiness_percent": 41, + "evidence_readiness_percent": 33, + "lead_shortfall": 106, + "exclusivity_conflict_count": 1, + "refund_exposure_usd": 18000 + }, + "audit_packet": { + "synthetic_data_only": true, + "external_apis_used": false, + "payment_processors_called": false, + "private_customer_data_used": false, + "packet_sha256": "cd37765dc01d96fb70775152c728ffc617fb0b498021c429796d2b033410fbd6" + } + }, + { + "schema_version": "event_sponsorship_revenue_readiness_guard_v1", + "packet_id": "packet-hold-no-contract", + "event_id": "deep-events-remote-2026", + "sponsor_id": "sponsor-signal-harbor", + "package_tier": "silver", + "invoice_amount_usd": 6500, + "decision": "HOLD_INVOICE", + "readiness_score": 0, + "reason_count": 10, + "reasons": [ + { + "code": "SPONSOR_CONTRACT_UNSIGNED", + "severity": "hold", + "message": "Sponsorship contract is not signed.", + "remediation": "Collect signed sponsorship agreement before invoicing or releasing revenue." + }, + { + "code": "PURCHASE_ORDER_NOT_APPROVED", + "severity": "hold", + "message": "Purchase order or finance approval is missing.", + "remediation": "Attach approved PO or finance approval before invoice release." + }, + { + "code": "SPONSOR_APPROVAL_MISSING", + "severity": "hold", + "message": "Sponsor has not approved the final package state.", + "remediation": "Obtain sponsor signoff for package scope before recognizing revenue readiness." + }, + { + "code": "REFUND_WINDOW_OR_EXPOSURE_OPEN", + "severity": "hold", + "message": "Cancellation or refund exposure is still open.", + "remediation": "Defer invoice release or record finance review until refund exposure is cleared." + }, + { + "code": "REQUIRED_DELIVERABLE_INCOMPLETE", + "severity": "hold", + "message": "Required deliverable is incomplete: Sponsor page logo.", + "remediation": "Complete the deliverable or reduce the invoiceable package scope." + }, + { + "code": "REQUIRED_DELIVERABLE_INCOMPLETE", + "severity": "hold", + "message": "Required deliverable is incomplete: Qualified attendee lead export.", + "remediation": "Complete the deliverable or reduce the invoiceable package scope." + }, + { + "code": "ATTENDEE_LEAD_GUARANTEE_SHORTFALL", + "severity": "hold", + "message": "Qualified leads delivered (0) are below the acceptable floor (35).", + "remediation": "Deliver remaining qualified leads, apply make-good credit, or reduce invoice amount." + }, + { + "code": "PROOF_ARTIFACT_MISSING", + "severity": "review", + "message": "Proof artifact is missing: contract.", + "remediation": "Attach proof artifact before revenue packet is marked audit-ready." + }, + { + "code": "PROOF_ARTIFACT_MISSING", + "severity": "review", + "message": "Proof artifact is missing: deliverable_signoff.", + "remediation": "Attach proof artifact before revenue packet is marked audit-ready." + }, + { + "code": "PROOF_ARTIFACT_MISSING", + "severity": "review", + "message": "Proof artifact is missing: lead_export_hash.", + "remediation": "Attach proof artifact before revenue packet is marked audit-ready." + } + ], + "finance_actions": [ + "hold invoice until contract is signed", + "request PO or finance approval evidence", + "collect sponsor package approval", + "defer release until cancellation/refund exposure closes", + "calculate make-good credit or revised invoice amount" + ], + "metrics": { + "deliverable_readiness_percent": 0, + "lead_readiness_percent": 0, + "evidence_readiness_percent": 0, + "lead_shortfall": 35, + "exclusivity_conflict_count": 0, + "refund_exposure_usd": 6500 + }, + "audit_packet": { + "synthetic_data_only": true, + "external_apis_used": false, + "payment_processors_called": false, + "private_customer_data_used": false, + "packet_sha256": "e31e26d3b2303d27340d85ad7967cb6baa861e54e61f345975741371b987bb02" + } + } + ] +} \ No newline at end of file diff --git a/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.md b/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.md new file mode 100644 index 00000000..d436d8eb --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-report.md @@ -0,0 +1,27 @@ +# Event Sponsorship Revenue Readiness Guard - Demo Report + +Synthetic reviewer demo for SCIBASE Revenue Infrastructure issue #20. + +## Summary + +- Packets evaluated: 4 +- Ready to invoice: 1 +- Needs finance review: 1 +- Held before invoice: 2 + +## Decisions + +| Packet | Sponsor | Tier | Decision | Score | Top reasons | +| --- | --- | --- | --- | ---: | --- | +| packet-ready-platinum | sponsor-orion-labs | platinum | RELEASE_INVOICE | 100 | none | +| packet-review-logo-signoff | sponsor-nova-data | gold | REVIEW_BEFORE_RELEASE | 82 | DELIVERABLE_SIGNOFF_MISSING, PROOF_ARTIFACT_MISSING | +| packet-hold-exclusivity | sponsor-vector-capital | platinum | HOLD_INVOICE | 38 | PURCHASE_ORDER_NOT_APPROVED, REFUND_WINDOW_OR_EXPOSURE_OPEN, SPONSOR_EXCLUSIVITY_CONFLICT | +| packet-hold-no-contract | sponsor-signal-harbor | silver | HOLD_INVOICE | 0 | SPONSOR_CONTRACT_UNSIGNED, PURCHASE_ORDER_NOT_APPROVED, SPONSOR_APPROVAL_MISSING | + +## Boundary + +- Synthetic data only. +- No payment processors called. +- No real sponsor/customer data used. +- No external APIs used. +- No credentials, bank data, Stripe, PayPal, ERP, or accounting systems touched. diff --git a/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-summary.svg b/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-summary.svg new file mode 100644 index 00000000..59061f0f --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/reports/event-sponsorship-readiness-summary.svg @@ -0,0 +1,24 @@ + + + Event Sponsorship Revenue Readiness + Synthetic guard demo for invoice release, finance review, and hold decisions. + + RELEASE_INVOICE + + + 1 + + REVIEW_BEFORE_RELEASE + + + 1 + + HOLD_INVOICE + + + 2 + + Boundary + Synthetic data only · no payment processors · no external APIs · no credentials + Designed to make event sponsorship revenue release auditable before invoice actions. + \ No newline at end of file diff --git a/event-sponsorship-revenue-readiness-guard/sample-data.js b/event-sponsorship-revenue-readiness-guard/sample-data.js new file mode 100644 index 00000000..5d3a86c1 --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/sample-data.js @@ -0,0 +1,135 @@ +const sponsorshipPackets = [ + { + id: "packet-ready-platinum", + eventId: "deep-events-berlin-2026", + sponsorId: "sponsor-orion-labs", + packageTier: "platinum", + invoiceAmountUsd: 25000, + contract: { + signed: true, + sponsorApproval: true, + purchaseOrderApproved: true, + cancellationWindowClosed: true, + refundExposureUsd: 0 + }, + deliverables: [ + { id: "logo-homepage", label: "Homepage logo placement", required: true, completed: true, sponsorApproved: true }, + { id: "stage-mention", label: "Main-stage sponsor mention", required: true, completed: true, sponsorApproved: true }, + { id: "lead-export", label: "Qualified attendee lead export", required: true, completed: true, sponsorApproved: true } + ], + leadGuarantee: { + promised: 150, + qualifiedDelivered: 168, + minimumAcceptable: 140 + }, + exclusivity: { + category: "AI infrastructure", + conflicts: [] + }, + evidence: [ + { type: "contract", present: true, reference: "contract:orion-2026" }, + { type: "deliverable_signoff", present: true, reference: "signoff:orion:platinum" }, + { type: "lead_export_hash", present: true, reference: "sha256:lead-export-orion" } + ] + }, + { + id: "packet-review-logo-signoff", + eventId: "deep-events-paris-2026", + sponsorId: "sponsor-nova-data", + packageTier: "gold", + invoiceAmountUsd: 14000, + contract: { + signed: true, + sponsorApproval: true, + purchaseOrderApproved: true, + cancellationWindowClosed: true, + refundExposureUsd: 0 + }, + deliverables: [ + { id: "logo-homepage", label: "Homepage logo placement", required: true, completed: true, sponsorApproved: false }, + { id: "newsletter-slot", label: "Newsletter sponsor slot", required: true, completed: true, sponsorApproved: true }, + { id: "lead-export", label: "Qualified attendee lead export", required: true, completed: true, sponsorApproved: true } + ], + leadGuarantee: { + promised: 100, + qualifiedDelivered: 96, + minimumAcceptable: 90 + }, + exclusivity: { + category: "Research tooling", + conflicts: [] + }, + evidence: [ + { type: "contract", present: true, reference: "contract:nova-2026" }, + { type: "deliverable_signoff", present: false, reference: "" }, + { type: "lead_export_hash", present: true, reference: "sha256:lead-export-nova" } + ] + }, + { + id: "packet-hold-exclusivity", + eventId: "deep-events-lisbon-2026", + sponsorId: "sponsor-vector-capital", + packageTier: "platinum", + invoiceAmountUsd: 30000, + contract: { + signed: true, + sponsorApproval: true, + purchaseOrderApproved: false, + cancellationWindowClosed: false, + refundExposureUsd: 18000 + }, + deliverables: [ + { id: "logo-homepage", label: "Homepage logo placement", required: true, completed: true, sponsorApproved: true }, + { id: "exclusive-category", label: "Exclusive category slot", required: true, completed: false, sponsorApproved: false }, + { id: "lead-export", label: "Qualified attendee lead export", required: true, completed: false, sponsorApproved: false } + ], + leadGuarantee: { + promised: 200, + qualifiedDelivered: 74, + minimumAcceptable: 180 + }, + exclusivity: { + category: "AI infrastructure", + conflicts: ["sponsor-orion-labs"] + }, + evidence: [ + { type: "contract", present: true, reference: "contract:vector-2026" }, + { type: "deliverable_signoff", present: false, reference: "" }, + { type: "lead_export_hash", present: false, reference: "" } + ] + }, + { + id: "packet-hold-no-contract", + eventId: "deep-events-remote-2026", + sponsorId: "sponsor-signal-harbor", + packageTier: "silver", + invoiceAmountUsd: 6500, + contract: { + signed: false, + sponsorApproval: false, + purchaseOrderApproved: false, + cancellationWindowClosed: false, + refundExposureUsd: 6500 + }, + deliverables: [ + { id: "logo-page", label: "Sponsor page logo", required: true, completed: false, sponsorApproved: false }, + { id: "lead-export", label: "Qualified attendee lead export", required: true, completed: false, sponsorApproved: false } + ], + leadGuarantee: { + promised: 40, + qualifiedDelivered: 0, + minimumAcceptable: 35 + }, + exclusivity: { + category: "Event operations", + conflicts: [] + }, + evidence: [ + { type: "contract", present: false, reference: "" }, + { type: "deliverable_signoff", present: false, reference: "" }, + { type: "lead_export_hash", present: false, reference: "" } + ] + } +]; + +module.exports = { sponsorshipPackets }; diff --git a/event-sponsorship-revenue-readiness-guard/test.js b/event-sponsorship-revenue-readiness-guard/test.js new file mode 100644 index 00000000..27bd6ff4 --- /dev/null +++ b/event-sponsorship-revenue-readiness-guard/test.js @@ -0,0 +1,55 @@ +const assert = require("assert"); +const { DECISIONS, evaluateSponsorshipRevenueReadiness, summarizeEvaluations } = require("./index"); +const { sponsorshipPackets } = require("./sample-data"); + +function byId(id) { + return sponsorshipPackets.find((packet) => packet.id === id); +} + +const ready = evaluateSponsorshipRevenueReadiness(byId("packet-ready-platinum")); +assert.strictEqual(ready.decision, DECISIONS.RELEASE); +assert.strictEqual(ready.reason_count, 0); +assert.strictEqual(ready.metrics.lead_shortfall, 0); +assert.strictEqual(ready.audit_packet.payment_processors_called, false); + +const review = evaluateSponsorshipRevenueReadiness(byId("packet-review-logo-signoff")); +assert.strictEqual(review.decision, DECISIONS.REVIEW); +assert(review.reasons.some((item) => item.code === "DELIVERABLE_SIGNOFF_MISSING")); +assert(review.reasons.some((item) => item.code === "PROOF_ARTIFACT_MISSING")); +assert(review.readiness_score >= 70); + +const hold = evaluateSponsorshipRevenueReadiness(byId("packet-hold-exclusivity")); +assert.strictEqual(hold.decision, DECISIONS.HOLD); +assert(hold.reasons.some((item) => item.code === "SPONSOR_EXCLUSIVITY_CONFLICT")); +assert(hold.reasons.some((item) => item.code === "REFUND_WINDOW_OR_EXPOSURE_OPEN")); +assert(hold.reasons.some((item) => item.code === "ATTENDEE_LEAD_GUARANTEE_SHORTFALL")); +assert(hold.metrics.exclusivity_conflict_count === 1); + +const unsigned = evaluateSponsorshipRevenueReadiness(byId("packet-hold-no-contract")); +assert.strictEqual(unsigned.decision, DECISIONS.HOLD); +assert(unsigned.reasons.some((item) => item.code === "SPONSOR_CONTRACT_UNSIGNED")); +assert(unsigned.reasons.some((item) => item.severity === "hold")); + +const evaluations = sponsorshipPackets.map(evaluateSponsorshipRevenueReadiness); +const summary = summarizeEvaluations(evaluations); +assert.strictEqual(summary.packet_count, 4); +assert.strictEqual(summary.decision_counts[DECISIONS.RELEASE], 1); +assert.strictEqual(summary.decision_counts[DECISIONS.REVIEW], 1); +assert.strictEqual(summary.decision_counts[DECISIONS.HOLD], 2); + +for (const evaluation of evaluations) { + assert.strictEqual(evaluation.audit_packet.synthetic_data_only, true); + assert.strictEqual(evaluation.audit_packet.external_apis_used, false); + assert.match(evaluation.audit_packet.packet_sha256, /^[a-f0-9]{64}$/); + if (evaluation.decision !== DECISIONS.RELEASE) { + assert(evaluation.reasons.length > 0, `${evaluation.packet_id} must explain non-release decisions`); + } +} + +const tampered = JSON.parse(JSON.stringify(byId("packet-ready-platinum"))); +tampered.leadGuarantee.qualifiedDelivered = 149; +const originalHash = ready.audit_packet.packet_sha256; +const tamperedHash = evaluateSponsorshipRevenueReadiness(tampered).audit_packet.packet_sha256; +assert.notStrictEqual(tamperedHash, originalHash, "packet hash should change when nested evidence changes"); + +console.log("event-sponsorship-revenue-readiness-guard tests passed");