diff --git a/challenge-recusal-guard/demo.js b/challenge-recusal-guard/demo.js
new file mode 100644
index 00000000..24d81c0a
--- /dev/null
+++ b/challenge-recusal-guard/demo.js
@@ -0,0 +1,49 @@
+const fs = require("fs");
+const path = require("path");
+const { assignments } = require("./sample-data");
+const { evaluateAssignments } = require("./index");
+
+const outDir = path.join(__dirname, "reports");
+fs.mkdirSync(outDir, { recursive: true });
+
+const report = evaluateAssignments(assignments);
+fs.writeFileSync(path.join(outDir, "recusal-report.json"), JSON.stringify(report, null, 2));
+
+const rows = report.reports
+ .map((entry) => `| ${entry.challengeId} | ${entry.evaluatorId} | ${entry.decision} | ${entry.findings.length} |`)
+ .join("\n");
+
+fs.writeFileSync(
+ path.join(outDir, "recusal-report.md"),
+ [
+ "# Challenge Conflict-of-Interest Recusal Guard",
+ "",
+ "| Challenge | Evaluator | Decision | Findings |",
+ "| --- | --- | --- | ---: |",
+ rows,
+ "",
+ `Payout holds: ${report.payoutHolds}`,
+ `Evaluator recusals: ${report.recusals}`,
+ `Disclosure gates: ${report.disclosures}`
+ ].join("\n")
+);
+
+fs.writeFileSync(
+ path.join(outDir, "summary.svg"),
+ ``
+);
+
+console.log(JSON.stringify({
+ totalAssignments: report.totalAssignments,
+ payoutHolds: report.payoutHolds,
+ recusals: report.recusals,
+ disclosures: report.disclosures
+}, null, 2));
diff --git a/challenge-recusal-guard/index.js b/challenge-recusal-guard/index.js
new file mode 100644
index 00000000..46487137
--- /dev/null
+++ b/challenge-recusal-guard/index.js
@@ -0,0 +1,106 @@
+function evaluateAssignment(assignment) {
+ const findings = [];
+
+ if (assignment.recentCoauthorshipMonths !== null && assignment.recentCoauthorshipMonths <= 24) {
+ findings.push({
+ type: "recent-coauthorship",
+ severity: "high",
+ detail: `Coauthorship ${assignment.recentCoauthorshipMonths} months ago requires recusal or independent review.`
+ });
+ }
+
+ if (assignment.sameInstitution) {
+ findings.push({
+ type: "same-institution",
+ severity: "medium",
+ detail: "Evaluator and team share an institution; require disclosure and secondary reviewer."
+ });
+ }
+
+ if (assignment.sponsorEmployment && assignment.evaluatorRole !== "sponsor-observer") {
+ findings.push({
+ type: "sponsor-employment",
+ severity: "high",
+ detail: "Sponsor employee cannot be the sole scoring reviewer for payout decisions."
+ });
+ }
+
+ if (assignment.financialInterestUsd >= 10000) {
+ findings.push({
+ type: "material-financial-interest",
+ severity: "critical",
+ detail: "Material financial interest requires payout decision hold and alternate reviewer."
+ });
+ }
+
+ if (assignment.competingTeamMembership) {
+ findings.push({
+ type: "competing-team-membership",
+ severity: "critical",
+ detail: "Evaluator belongs to a competing team and must be removed from scoring."
+ });
+ }
+
+ if (assignment.anonymityRequired && assignment.identitySharedWithSponsor) {
+ findings.push({
+ type: "anonymity-breach",
+ severity: "high",
+ detail: "Anonymous challenge identity was shared with sponsor before scoring lock."
+ });
+ }
+
+ const highestSeverity = severityRank(findings);
+ return {
+ challengeId: assignment.challengeId,
+ evaluatorId: assignment.evaluatorId,
+ teamId: assignment.teamId,
+ decision: decisionFor(highestSeverity),
+ highestSeverity,
+ findings,
+ remediation: remediationFor(findings)
+ };
+}
+
+function severityRank(findings) {
+ const order = ["clear", "low", "medium", "high", "critical"];
+ return findings.reduce((highest, finding) => (
+ order.indexOf(finding.severity) > order.indexOf(highest) ? finding.severity : highest
+ ), "clear");
+}
+
+function decisionFor(severity) {
+ if (severity === "critical") return "hold-payout-decision";
+ if (severity === "high") return "recuse-evaluator";
+ if (severity === "medium") return "require-disclosure";
+ return "allow-scoring";
+}
+
+function remediationFor(findings) {
+ if (findings.length === 0) return ["Record no-conflict attestation before scoring lock."];
+ return findings.map((finding) => {
+ const actions = {
+ "recent-coauthorship": "Assign an independent reviewer and record coauthorship disclosure.",
+ "same-institution": "Add a secondary external reviewer before sponsor-visible scoring.",
+ "sponsor-employment": "Limit sponsor employee to observer notes or require independent scoring.",
+ "material-financial-interest": "Hold payout decision until an alternate evaluator signs off.",
+ "competing-team-membership": "Remove evaluator from the panel and quarantine prior scores.",
+ "anonymity-breach": "Freeze sponsor access, rotate reviewer packet, and log breach review."
+ };
+ return actions[finding.type] || "Route to bounty administrator.";
+ });
+}
+
+function evaluateAssignments(assignments) {
+ const reports = assignments.map(evaluateAssignment);
+ return {
+ generatedAt: "2026-05-29T00:00:00.000Z",
+ totalAssignments: reports.length,
+ payoutHolds: reports.filter((report) => report.decision === "hold-payout-decision").length,
+ recusals: reports.filter((report) => report.decision === "recuse-evaluator").length,
+ disclosures: reports.filter((report) => report.decision === "require-disclosure").length,
+ allowed: reports.filter((report) => report.decision === "allow-scoring").length,
+ reports
+ };
+}
+
+module.exports = { decisionFor, evaluateAssignment, evaluateAssignments, severityRank };
diff --git a/challenge-recusal-guard/readme.md b/challenge-recusal-guard/readme.md
new file mode 100644
index 00000000..12b5167a
--- /dev/null
+++ b/challenge-recusal-guard/readme.md
@@ -0,0 +1,28 @@
+# Challenge Conflict-of-Interest Recusal Guard
+
+This module is a focused Scientific Bounty System slice for checking evaluator, sponsor, and institutional conflicts before challenge scoring or payout decisions.
+
+It is intentionally separate from payout routing, team split ledgers, arbitration, review integrity, reviewer workload, challenge fairness, deadline fairness, evidence freeze, benchmark leakage, submission quarantine, package security, license risk, sponsor data-room access, and award transparency modules.
+
+## What it checks
+
+- Recent coauthorship between evaluator and submitter team
+- Shared institutional affiliation requiring disclosure
+- Sponsor employee involvement in payout scoring
+- Material financial interest
+- Competing-team evaluator membership
+- Anonymous challenge identity exposure before scoring lock
+
+## Run
+
+```bash
+node challenge-recusal-guard/test.js
+node challenge-recusal-guard/demo.js
+node challenge-recusal-guard/render-video.js
+```
+
+Outputs are written to `challenge-recusal-guard/reports/`.
+
+## Safety
+
+All data is synthetic. The module does not call payment providers, sponsors, reviewers, bounty platforms, private submissions, identity systems, credentials, or production SCIBASE services.
diff --git a/challenge-recusal-guard/render-video.js b/challenge-recusal-guard/render-video.js
new file mode 100644
index 00000000..8adcfa3c
--- /dev/null
+++ b/challenge-recusal-guard/render-video.js
@@ -0,0 +1,38 @@
+const { execFileSync } = require("child_process");
+const fs = require("fs");
+const path = require("path");
+
+const outDir = path.join(__dirname, "reports");
+fs.mkdirSync(outDir, { recursive: true });
+
+const ppm = path.join(outDir, "demo-frame.ppm");
+const mp4 = path.join(outDir, "demo.mp4");
+const width = 960;
+const height = 540;
+let body = "";
+
+for (let y = 0; y < height; y += 1) {
+ for (let x = 0; x < width; x += 1) {
+ const panel = x > 90 && x < 870 && y > 80 && y < 460;
+ const holdBar = x > 160 && x < 460 && y > 210 && y < 252;
+ const recuseBar = x > 160 && x < 310 && y > 302 && y < 344;
+ const r = panel ? (holdBar ? 196 : recuseBar ? 228 : 28) : 10;
+ const g = panel ? (holdBar ? 63 : recuseBar ? 138 : 32) : 12;
+ const b = panel ? (holdBar ? 77 : recuseBar ? 42 : 46) : 18;
+ body += String.fromCharCode(r, g, b);
+ }
+}
+
+fs.writeFileSync(ppm, `P6\n${width} ${height}\n255\n${body}`, "binary");
+execFileSync("ffmpeg", [
+ "-y",
+ "-loop", "1",
+ "-framerate", "24",
+ "-i", ppm,
+ "-t", "5",
+ "-vf", "format=yuv420p",
+ "-movflags", "+faststart",
+ mp4
+], { stdio: "inherit" });
+
+console.log(mp4);
diff --git a/challenge-recusal-guard/reports/demo.mp4 b/challenge-recusal-guard/reports/demo.mp4
new file mode 100644
index 00000000..f9c3a96e
Binary files /dev/null and b/challenge-recusal-guard/reports/demo.mp4 differ
diff --git a/challenge-recusal-guard/reports/recusal-report.json b/challenge-recusal-guard/reports/recusal-report.json
new file mode 100644
index 00000000..0de6f614
--- /dev/null
+++ b/challenge-recusal-guard/reports/recusal-report.json
@@ -0,0 +1,90 @@
+{
+ "generatedAt": "2026-05-29T00:00:00.000Z",
+ "totalAssignments": 4,
+ "payoutHolds": 2,
+ "recusals": 1,
+ "disclosures": 1,
+ "allowed": 0,
+ "reports": [
+ {
+ "challengeId": "rna-biomarker-prize",
+ "evaluatorId": "reviewer-17",
+ "teamId": "team-alpha",
+ "decision": "recuse-evaluator",
+ "highestSeverity": "high",
+ "findings": [
+ {
+ "type": "recent-coauthorship",
+ "severity": "high",
+ "detail": "Coauthorship 8 months ago requires recusal or independent review."
+ }
+ ],
+ "remediation": [
+ "Assign an independent reviewer and record coauthorship disclosure."
+ ]
+ },
+ {
+ "challengeId": "materials-catalyst-model",
+ "evaluatorId": "sponsor-scientist-4",
+ "teamId": "team-boron",
+ "decision": "hold-payout-decision",
+ "highestSeverity": "critical",
+ "findings": [
+ {
+ "type": "sponsor-employment",
+ "severity": "high",
+ "detail": "Sponsor employee cannot be the sole scoring reviewer for payout decisions."
+ },
+ {
+ "type": "material-financial-interest",
+ "severity": "critical",
+ "detail": "Material financial interest requires payout decision hold and alternate reviewer."
+ }
+ ],
+ "remediation": [
+ "Limit sponsor employee to observer notes or require independent scoring.",
+ "Hold payout decision until an alternate evaluator signs off."
+ ]
+ },
+ {
+ "challengeId": "climate-forecasting-open",
+ "evaluatorId": "faculty-panel-2",
+ "teamId": "team-delta",
+ "decision": "require-disclosure",
+ "highestSeverity": "medium",
+ "findings": [
+ {
+ "type": "same-institution",
+ "severity": "medium",
+ "detail": "Evaluator and team share an institution; require disclosure and secondary reviewer."
+ }
+ ],
+ "remediation": [
+ "Add a secondary external reviewer before sponsor-visible scoring."
+ ]
+ },
+ {
+ "challengeId": "quantum-noise-reduction",
+ "evaluatorId": "solver-advisor-9",
+ "teamId": "team-qubit",
+ "decision": "hold-payout-decision",
+ "highestSeverity": "critical",
+ "findings": [
+ {
+ "type": "competing-team-membership",
+ "severity": "critical",
+ "detail": "Evaluator belongs to a competing team and must be removed from scoring."
+ },
+ {
+ "type": "anonymity-breach",
+ "severity": "high",
+ "detail": "Anonymous challenge identity was shared with sponsor before scoring lock."
+ }
+ ],
+ "remediation": [
+ "Remove evaluator from the panel and quarantine prior scores.",
+ "Freeze sponsor access, rotate reviewer packet, and log breach review."
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/challenge-recusal-guard/reports/recusal-report.md b/challenge-recusal-guard/reports/recusal-report.md
new file mode 100644
index 00000000..2ba8f687
--- /dev/null
+++ b/challenge-recusal-guard/reports/recusal-report.md
@@ -0,0 +1,12 @@
+# Challenge Conflict-of-Interest Recusal Guard
+
+| Challenge | Evaluator | Decision | Findings |
+| --- | --- | --- | ---: |
+| rna-biomarker-prize | reviewer-17 | recuse-evaluator | 1 |
+| materials-catalyst-model | sponsor-scientist-4 | hold-payout-decision | 2 |
+| climate-forecasting-open | faculty-panel-2 | require-disclosure | 1 |
+| quantum-noise-reduction | solver-advisor-9 | hold-payout-decision | 2 |
+
+Payout holds: 2
+Evaluator recusals: 1
+Disclosure gates: 1
\ No newline at end of file
diff --git a/challenge-recusal-guard/reports/summary.svg b/challenge-recusal-guard/reports/summary.svg
new file mode 100644
index 00000000..a65068a3
--- /dev/null
+++ b/challenge-recusal-guard/reports/summary.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/challenge-recusal-guard/sample-data.js b/challenge-recusal-guard/sample-data.js
new file mode 100644
index 00000000..1b0218e9
--- /dev/null
+++ b/challenge-recusal-guard/sample-data.js
@@ -0,0 +1,60 @@
+const assignments = [
+ {
+ challengeId: "rna-biomarker-prize",
+ evaluatorId: "reviewer-17",
+ evaluatorRole: "external-reviewer",
+ teamId: "team-alpha",
+ sponsorId: "pharma-sponsor-a",
+ sameInstitution: false,
+ recentCoauthorshipMonths: 8,
+ sponsorEmployment: false,
+ financialInterestUsd: 0,
+ competingTeamMembership: false,
+ anonymityRequired: true,
+ identitySharedWithSponsor: false
+ },
+ {
+ challengeId: "materials-catalyst-model",
+ evaluatorId: "sponsor-scientist-4",
+ evaluatorRole: "sponsor-reviewer",
+ teamId: "team-boron",
+ sponsorId: "materials-sponsor-b",
+ sameInstitution: false,
+ recentCoauthorshipMonths: null,
+ sponsorEmployment: true,
+ financialInterestUsd: 25000,
+ competingTeamMembership: false,
+ anonymityRequired: false,
+ identitySharedWithSponsor: true
+ },
+ {
+ challengeId: "climate-forecasting-open",
+ evaluatorId: "faculty-panel-2",
+ evaluatorRole: "institutional-reviewer",
+ teamId: "team-delta",
+ sponsorId: "climate-nonprofit-c",
+ sameInstitution: true,
+ recentCoauthorshipMonths: 42,
+ sponsorEmployment: false,
+ financialInterestUsd: 0,
+ competingTeamMembership: false,
+ anonymityRequired: false,
+ identitySharedWithSponsor: false
+ },
+ {
+ challengeId: "quantum-noise-reduction",
+ evaluatorId: "solver-advisor-9",
+ evaluatorRole: "technical-reviewer",
+ teamId: "team-qubit",
+ sponsorId: "quantum-startup-d",
+ sameInstitution: false,
+ recentCoauthorshipMonths: null,
+ sponsorEmployment: false,
+ financialInterestUsd: 0,
+ competingTeamMembership: true,
+ anonymityRequired: true,
+ identitySharedWithSponsor: true
+ }
+];
+
+module.exports = { assignments };
diff --git a/challenge-recusal-guard/test.js b/challenge-recusal-guard/test.js
new file mode 100644
index 00000000..a1ba4c87
--- /dev/null
+++ b/challenge-recusal-guard/test.js
@@ -0,0 +1,25 @@
+const assert = require("assert");
+const { assignments } = require("./sample-data");
+const { decisionFor, evaluateAssignments, severityRank } = require("./index");
+
+assert.strictEqual(decisionFor("clear"), "allow-scoring");
+assert.strictEqual(decisionFor("medium"), "require-disclosure");
+assert.strictEqual(decisionFor("high"), "recuse-evaluator");
+assert.strictEqual(decisionFor("critical"), "hold-payout-decision");
+assert.strictEqual(severityRank([{ severity: "medium" }, { severity: "critical" }]), "critical");
+
+const report = evaluateAssignments(assignments);
+assert.strictEqual(report.totalAssignments, 4);
+assert.strictEqual(report.payoutHolds, 2);
+assert.strictEqual(report.recusals, 1);
+assert.strictEqual(report.disclosures, 1);
+assert.strictEqual(report.allowed, 0);
+
+const quantum = report.reports.find((entry) => entry.challengeId === "quantum-noise-reduction");
+assert(quantum.findings.some((finding) => finding.type === "competing-team-membership"));
+assert(quantum.findings.some((finding) => finding.type === "anonymity-breach"));
+
+const sponsor = report.reports.find((entry) => entry.challengeId === "materials-catalyst-model");
+assert(sponsor.findings.some((finding) => finding.type === "material-financial-interest"));
+
+console.log("challenge-recusal-guard tests passed");