diff --git a/bayesian-prior-sensitivity-assistant/README.md b/bayesian-prior-sensitivity-assistant/README.md
new file mode 100644
index 00000000..d3c9c4af
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/README.md
@@ -0,0 +1,35 @@
+# Bayesian Prior Sensitivity Assistant
+
+Synthetic, dependency-free auto peer-review assistant for SCIBASE issue #16, AI-Powered Research Assistant Suite.
+
+This module evaluates Bayesian analysis review packets for prior-sensitivity and posterior-robustness risks before an AI assistant promotes manuscript feedback to authors or reviewers.
+
+## What It Checks
+
+- priors used without manuscript disclosure
+- informative priors without domain justification
+- posterior direction flips under alternate priors
+- large posterior shifts under prior sensitivity runs
+- convergence failures, divergent transitions, and low effective sample size
+- missing or weak prior predictive checks
+- strong manuscript claims that are not robust across priors
+
+## Run
+
+```bash
+npm run check
+npm test
+npm run demo
+npm run demo:video
+```
+
+The demo writes reviewer artifacts under `reports/`:
+
+- `bayesian-prior-sensitivity-packet.json`
+- `bayesian-prior-sensitivity-report.md`
+- `summary.svg`
+- `demo.avi`
+
+## Safety
+
+All records are synthetic. The module does not read local files outside this folder, call external services, execute notebooks, or process real manuscripts.
diff --git a/bayesian-prior-sensitivity-assistant/acceptance-notes.md b/bayesian-prior-sensitivity-assistant/acceptance-notes.md
new file mode 100644
index 00000000..b8554317
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/acceptance-notes.md
@@ -0,0 +1,19 @@
+# Acceptance Notes
+
+Reviewer acceptance checklist:
+
+- The high-risk clinical Bayesian model is held for major revision.
+- The borderline materials model is marked for minor revision.
+- The ready ecology model passes without findings.
+- Strong claims that are not prior-robust are blocked.
+- Audit digests are deterministic across repeated runs.
+- Demo artifacts are generated locally from synthetic data only.
+
+Validation commands:
+
+```bash
+npm run check
+npm test
+npm run demo
+npm run demo:video
+```
diff --git a/bayesian-prior-sensitivity-assistant/demo.js b/bayesian-prior-sensitivity-assistant/demo.js
new file mode 100644
index 00000000..bc065bb1
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/demo.js
@@ -0,0 +1,84 @@
+"use strict";
+
+const fs = require("fs");
+const path = require("path");
+const { assessBayesianPriorSensitivity } = require("./index");
+const { mixedBayesianReviewPacket, readyBayesianReviewPacket } = require("./sample-data");
+
+const reportsDir = path.join(__dirname, "reports");
+fs.mkdirSync(reportsDir, { recursive: true });
+
+const packet = {
+ mixed: assessBayesianPriorSensitivity(mixedBayesianReviewPacket),
+ ready: assessBayesianPriorSensitivity(readyBayesianReviewPacket)
+};
+
+fs.writeFileSync(path.join(reportsDir, "bayesian-prior-sensitivity-packet.json"), `${JSON.stringify(packet, null, 2)}\n`);
+fs.writeFileSync(path.join(reportsDir, "bayesian-prior-sensitivity-report.md"), renderMarkdown(packet));
+fs.writeFileSync(path.join(reportsDir, "summary.svg"), renderSvg(packet));
+
+console.log(`Wrote ${path.relative(process.cwd(), reportsDir)}`);
+console.log(`Mixed packet status: ${packet.mixed.status}`);
+console.log(`Audit digest: ${packet.mixed.auditDigest}`);
+
+function renderMarkdown(packet) {
+ const lines = [
+ "# Bayesian Prior Sensitivity Assistant Report",
+ "",
+ "Synthetic auto peer-review packet for SCIBASE issue #16.",
+ "",
+ "## Summary",
+ "",
+ "| Packet | Status | Major | Minor | Ready | Digest |",
+ "| --- | --- | ---: | ---: | ---: | --- |",
+ `| mixed | ${packet.mixed.status} | ${packet.mixed.summary.major} | ${packet.mixed.summary.minor} | ${packet.mixed.summary.ready} | ${packet.mixed.auditDigest.slice(0, 12)} |`,
+ `| ready | ${packet.ready.status} | ${packet.ready.summary.major} | ${packet.ready.summary.minor} | ${packet.ready.summary.ready} | ${packet.ready.auditDigest.slice(0, 12)} |`,
+ "",
+ "## Analysis Decisions",
+ ""
+ ];
+
+ for (const analysis of packet.mixed.analyses) {
+ lines.push(`### ${analysis.analysisId}: ${analysis.decision}`);
+ for (const blocker of analysis.blockers) {
+ lines.push(`- blocker ${blocker.code}: ${blocker.message}`);
+ }
+ for (const warning of analysis.warnings) {
+ lines.push(`- warning ${warning.code}: ${warning.message}`);
+ }
+ lines.push("");
+ }
+
+ lines.push("## Non-Overlap Notes", "");
+ lines.push(
+ "This module focuses specifically on Bayesian prior sensitivity, convergence diagnostics, prior predictive checks, and posterior claim calibration. It does not implement a broad assistant suite, multiple-comparison correction, generic statistical consistency checks, study power feasibility, uncertainty tone review, prompt safety, literature freshness, image integrity, external validity, or protocol trace workflows."
+ );
+
+ return `${lines.join("\n")}\n`;
+}
+
+function renderSvg(packet) {
+ const mixed = packet.mixed.summary;
+ return `
+
+`;
+}
+
+function bar(x, y, label, count, color) {
+ const width = Math.max(24, count * 120);
+ return [
+ ` ${label}`,
+ ` `,
+ ` `,
+ ` ${count} analysis item(s)`
+ ].join("\n");
+}
diff --git a/bayesian-prior-sensitivity-assistant/index.js b/bayesian-prior-sensitivity-assistant/index.js
new file mode 100644
index 00000000..00e38d15
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/index.js
@@ -0,0 +1,229 @@
+"use strict";
+
+const crypto = require("crypto");
+
+const DEFAULT_POLICY = {
+ maxRhat: 1.01,
+ severeRhat: 1.05,
+ minEffectiveSampleSize: 400,
+ severeEffectiveSampleSize: 150,
+ maxPosteriorShift: 0.2,
+ minPriorPredictiveCoverage: 0.7,
+ strongClaimProbability: 0.95
+};
+
+function assessBayesianPriorSensitivity(packet, policy = {}) {
+ const settings = { ...DEFAULT_POLICY, ...policy };
+ const generatedAt = new Date(packet.generatedAt || "2026-05-28T00:00:00Z");
+ const analyses = (packet.analyses || []).map((analysis) => evaluateAnalysis(analysis, settings));
+ const summary = summarize(analyses);
+ const payload = {
+ generatedAt: generatedAt.toISOString(),
+ manuscriptId: packet.manuscriptId || "synthetic-bayesian-manuscript",
+ status: summary.status,
+ summary,
+ analyses
+ };
+
+ return {
+ ...payload,
+ auditDigest: digest(payload)
+ };
+}
+
+function evaluateAnalysis(analysis, settings) {
+ const blockers = [];
+ const warnings = [];
+ const actions = [];
+
+ checkPriorDisclosure(analysis, blockers, warnings, actions);
+ checkPriorSensitivity(analysis, settings, blockers, warnings, actions);
+ checkDiagnostics(analysis, settings, blockers, warnings, actions);
+ checkPriorPredictive(analysis, settings, blockers, warnings, actions);
+ checkClaims(analysis, settings, blockers, warnings, actions);
+
+ const decision = blockers.length > 0
+ ? "major_revision_prior_robustness"
+ : warnings.length > 0
+ ? "minor_revision_prior_robustness"
+ : "ready_for_peer_review";
+
+ return {
+ analysisId: analysis.id,
+ domain: analysis.domain,
+ decision,
+ blockers,
+ warnings,
+ actions: [...new Set(actions)]
+ };
+}
+
+function checkPriorDisclosure(analysis, blockers, warnings, actions) {
+ const undisclosed = analysis.priors.filter((prior) => !prior.reported);
+ const unjustifiedInformative = analysis.priors.filter((prior) => prior.reported && prior.strength === "informative" && !prior.justification);
+
+ if (undisclosed.length > 0) {
+ blockers.push({
+ code: "prior_not_reported",
+ message: `${undisclosed.length} model prior(s) are used without manuscript disclosure.`
+ });
+ actions.push("Disclose all model priors with parameter values and rationale.");
+ }
+
+ if (unjustifiedInformative.length > 0) {
+ warnings.push({
+ code: "informative_prior_not_justified",
+ message: `${unjustifiedInformative.length} informative prior(s) lack domain justification.`
+ });
+ actions.push("Add domain rationale or sensitivity analysis for informative priors.");
+ }
+}
+
+function checkPriorSensitivity(analysis, settings, blockers, warnings, actions) {
+ for (const run of analysis.sensitivityRuns) {
+ const baseline = run.baselineEstimate;
+ const alternate = run.alternateEstimate;
+ const shift = Math.abs(alternate - baseline);
+ const directionFlips = Math.sign(baseline) !== 0 && Math.sign(alternate) !== 0 && Math.sign(baseline) !== Math.sign(alternate);
+
+ if (directionFlips) {
+ blockers.push({
+ code: "posterior_direction_flips_under_prior",
+ message: `${run.name} changes effect direction from ${round(baseline)} to ${round(alternate)}.`
+ });
+ actions.push("Mark the manuscript claim as prior-sensitive and add a robustness table.");
+ } else if (shift > settings.maxPosteriorShift) {
+ warnings.push({
+ code: "large_posterior_shift_under_prior",
+ message: `${run.name} shifts posterior estimate by ${round(shift)}.`
+ });
+ actions.push("Report alternate-prior posterior estimates beside the primary model.");
+ }
+ }
+}
+
+function checkDiagnostics(analysis, settings, blockers, warnings, actions) {
+ const diagnostics = analysis.diagnostics;
+
+ if (diagnostics.divergentTransitions > 0) {
+ blockers.push({
+ code: "divergent_transitions_present",
+ message: `${diagnostics.divergentTransitions} divergent transition(s) were reported.`
+ });
+ actions.push("Resolve sampler divergences before accepting posterior claims.");
+ }
+
+ if (diagnostics.maxRhat > settings.severeRhat) {
+ blockers.push({
+ code: "severe_rhat_convergence_failure",
+ message: `Maximum R-hat is ${round(diagnostics.maxRhat)}.`
+ });
+ actions.push("Rerun or reparameterize the model until convergence diagnostics pass.");
+ } else if (diagnostics.maxRhat > settings.maxRhat) {
+ warnings.push({
+ code: "rhat_above_review_threshold",
+ message: `Maximum R-hat is ${round(diagnostics.maxRhat)}.`
+ });
+ actions.push("Include convergence diagnostics and rerun chains if needed.");
+ }
+
+ if (diagnostics.minEffectiveSampleSize < settings.severeEffectiveSampleSize) {
+ blockers.push({
+ code: "severe_effective_sample_size_shortfall",
+ message: `Minimum effective sample size is ${diagnostics.minEffectiveSampleSize}.`
+ });
+ actions.push("Increase sampling effort or simplify the model before review.");
+ } else if (diagnostics.minEffectiveSampleSize < settings.minEffectiveSampleSize) {
+ warnings.push({
+ code: "low_effective_sample_size",
+ message: `Minimum effective sample size is ${diagnostics.minEffectiveSampleSize}.`
+ });
+ actions.push("Report effective sample sizes and rerun where posterior tails are unstable.");
+ }
+}
+
+function checkPriorPredictive(analysis, settings, blockers, warnings, actions) {
+ const check = analysis.priorPredictiveCheck;
+
+ if (!check.reported) {
+ blockers.push({
+ code: "prior_predictive_check_missing",
+ message: "No prior predictive check is reported for the Bayesian model."
+ });
+ actions.push("Add a prior predictive check before claims are promoted to peer-review text.");
+ return;
+ }
+
+ if (check.coverage < settings.minPriorPredictiveCoverage) {
+ warnings.push({
+ code: "weak_prior_predictive_coverage",
+ message: `Prior predictive coverage is ${Math.round(check.coverage * 100)}%.`
+ });
+ actions.push("Revise priors or explain why prior predictive coverage is low.");
+ }
+}
+
+function checkClaims(analysis, settings, blockers, warnings, actions) {
+ for (const claim of analysis.claims) {
+ const strongLanguage = ["proves", "conclusive", "definitive"].includes(claim.strength);
+
+ if (strongLanguage && !claim.robustAcrossPriors) {
+ blockers.push({
+ code: "strong_claim_not_prior_robust",
+ message: `${claim.id} uses strong language without prior-robust support.`
+ });
+ actions.push("Downgrade claim language or add prior-robust evidence.");
+ } else if (strongLanguage && claim.posteriorProbability < settings.strongClaimProbability) {
+ warnings.push({
+ code: "claim_probability_below_strong_language",
+ message: `${claim.id} has posterior probability ${round(claim.posteriorProbability)} with strong wording.`
+ });
+ actions.push("Calibrate the manuscript claim to the posterior probability.");
+ }
+ }
+}
+
+function summarize(analyses) {
+ const major = analyses.filter((analysis) => analysis.decision === "major_revision_prior_robustness").length;
+ const minor = analyses.filter((analysis) => analysis.decision === "minor_revision_prior_robustness").length;
+ const ready = analyses.filter((analysis) => analysis.decision === "ready_for_peer_review").length;
+ const blockerCount = analyses.reduce((sum, analysis) => sum + analysis.blockers.length, 0);
+ const warningCount = analyses.reduce((sum, analysis) => sum + analysis.warnings.length, 0);
+ const status = major > 0 ? "hold_peer_review_claims" : minor > 0 ? "revise_before_review" : "ready";
+
+ return {
+ status,
+ analysisCount: analyses.length,
+ major,
+ minor,
+ ready,
+ blockerCount,
+ warningCount
+ };
+}
+
+function digest(value) {
+ return crypto.createHash("sha256").update(stableStringify(value)).digest("hex");
+}
+
+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 round(value) {
+ return Math.round(value * 100) / 100;
+}
+
+module.exports = {
+ assessBayesianPriorSensitivity,
+ DEFAULT_POLICY
+};
diff --git a/bayesian-prior-sensitivity-assistant/make-demo-video.js b/bayesian-prior-sensitivity-assistant/make-demo-video.js
new file mode 100644
index 00000000..9ead76c3
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/make-demo-video.js
@@ -0,0 +1,163 @@
+"use strict";
+
+const fs = require("fs");
+const path = require("path");
+
+const width = 320;
+const height = 180;
+const fps = 2;
+const frameCount = 16;
+const rowSize = Math.ceil((width * 3) / 4) * 4;
+const frameSize = rowSize * height;
+const reportsDir = path.join(__dirname, "reports");
+const outputPath = path.join(reportsDir, "demo.avi");
+
+fs.mkdirSync(reportsDir, { recursive: true });
+
+const frames = [];
+for (let i = 0; i < frameCount; i += 1) {
+ frames.push(makeFrame(i));
+}
+
+const chunks = [];
+const indexes = [];
+let offset = 4;
+for (const frame of frames) {
+ const chunk = riffChunk("00db", frame);
+ chunks.push(chunk);
+ indexes.push(indexEntry("00db", 0x10, offset, frame.length));
+ offset += chunk.length;
+}
+
+const hdrl = listChunk("hdrl", [
+ riffChunk("avih", aviHeader()),
+ listChunk("strl", [
+ riffChunk("strh", streamHeader()),
+ riffChunk("strf", bitmapInfoHeader())
+ ])
+]);
+const movi = listChunk("movi", chunks);
+const idx1 = riffChunk("idx1", Buffer.concat(indexes));
+const riff = riffChunk("RIFF", Buffer.concat([Buffer.from("AVI "), hdrl, movi, idx1]));
+
+fs.writeFileSync(outputPath, riff);
+console.log(`Wrote ${path.relative(process.cwd(), outputPath)} (${riff.length} bytes)`);
+
+function makeFrame(frameIndex) {
+ const frame = Buffer.alloc(frameSize, 0xff);
+ const progress = (frameIndex + 1) / frameCount;
+ fillRect(frame, 0, 0, width, height, [247, 247, 251]);
+ fillRect(frame, 18, 18, 284, 144, [255, 255, 255]);
+ strokeRect(frame, 18, 18, 284, 144, [210, 216, 226]);
+ fillRect(frame, 38, 50, 240, 18, [232, 237, 244]);
+ fillRect(frame, 38, 50, Math.round(240 * progress), 18, [185, 28, 28]);
+ fillRect(frame, 38, 90, 240, 18, [232, 237, 244]);
+ fillRect(frame, 38, 90, Math.round(120 * progress), 18, [183, 121, 31]);
+ fillRect(frame, 38, 130, 240, 18, [232, 237, 244]);
+ fillRect(frame, 38, 130, Math.round(120 * progress), 18, [21, 128, 61]);
+ drawTicks(frame, 38, 76, frameIndex);
+ return frame;
+}
+
+function drawTicks(frame, x, y, frameIndex) {
+ const count = Math.min(5, Math.floor(frameIndex / 3) + 1);
+ for (let i = 0; i < count; i += 1) {
+ fillRect(frame, x + i * 18, y, 10, 10, [42, 72, 88]);
+ }
+}
+
+function fillRect(frame, x, y, w, h, rgb) {
+ for (let py = y; py < y + h; py += 1) {
+ if (py < 0 || py >= height) continue;
+ for (let px = x; px < x + w; px += 1) {
+ if (px < 0 || px >= width) continue;
+ setPixel(frame, px, py, rgb);
+ }
+ }
+}
+
+function strokeRect(frame, x, y, w, h, rgb) {
+ fillRect(frame, x, y, w, 1, rgb);
+ fillRect(frame, x, y + h - 1, w, 1, rgb);
+ fillRect(frame, x, y, 1, h, rgb);
+ fillRect(frame, x + w - 1, y, 1, h, rgb);
+}
+
+function setPixel(frame, x, y, rgb) {
+ const bottomUpY = height - y - 1;
+ const index = bottomUpY * rowSize + x * 3;
+ frame[index] = rgb[2];
+ frame[index + 1] = rgb[1];
+ frame[index + 2] = rgb[0];
+}
+
+function aviHeader() {
+ const buffer = Buffer.alloc(56);
+ buffer.writeUInt32LE(Math.round(1000000 / fps), 0);
+ buffer.writeUInt32LE(frameSize * fps, 4);
+ buffer.writeUInt32LE(0, 8);
+ buffer.writeUInt32LE(0x10, 12);
+ buffer.writeUInt32LE(frameCount, 16);
+ buffer.writeUInt32LE(0, 20);
+ buffer.writeUInt32LE(1, 24);
+ buffer.writeUInt32LE(frameSize, 28);
+ buffer.writeUInt32LE(width, 32);
+ buffer.writeUInt32LE(height, 36);
+ return buffer;
+}
+
+function streamHeader() {
+ const buffer = Buffer.alloc(56);
+ buffer.write("vids", 0, 4, "ascii");
+ buffer.write("DIB ", 4, 4, "ascii");
+ buffer.writeUInt32LE(0, 8);
+ buffer.writeUInt32LE(0, 12);
+ buffer.writeUInt32LE(0, 16);
+ buffer.writeUInt32LE(1, 20);
+ buffer.writeUInt32LE(fps, 24);
+ buffer.writeUInt32LE(0, 28);
+ buffer.writeUInt32LE(frameCount, 32);
+ buffer.writeUInt32LE(frameSize, 36);
+ buffer.writeInt32LE(-1, 40);
+ buffer.writeUInt32LE(0, 44);
+ buffer.writeInt16LE(0, 48);
+ buffer.writeInt16LE(0, 50);
+ buffer.writeInt16LE(width, 52);
+ buffer.writeInt16LE(height, 54);
+ return buffer;
+}
+
+function bitmapInfoHeader() {
+ const buffer = Buffer.alloc(40);
+ buffer.writeUInt32LE(40, 0);
+ buffer.writeInt32LE(width, 4);
+ buffer.writeInt32LE(height, 8);
+ buffer.writeUInt16LE(1, 12);
+ buffer.writeUInt16LE(24, 14);
+ buffer.writeUInt32LE(0, 16);
+ buffer.writeUInt32LE(frameSize, 20);
+ return buffer;
+}
+
+function indexEntry(id, flags, chunkOffset, size) {
+ const buffer = Buffer.alloc(16);
+ buffer.write(id, 0, 4, "ascii");
+ buffer.writeUInt32LE(flags, 4);
+ buffer.writeUInt32LE(chunkOffset, 8);
+ buffer.writeUInt32LE(size, 12);
+ return buffer;
+}
+
+function riffChunk(id, payload) {
+ const size = payload.length;
+ const pad = size % 2 === 1 ? 1 : 0;
+ const buffer = Buffer.alloc(8 + size + pad);
+ buffer.write(id, 0, 4, "ascii");
+ buffer.writeUInt32LE(size, 4);
+ payload.copy(buffer, 8);
+ return buffer;
+}
+
+function listChunk(type, chunks) {
+ return riffChunk("LIST", Buffer.concat([Buffer.from(type, "ascii"), ...chunks]));
+}
diff --git a/bayesian-prior-sensitivity-assistant/package.json b/bayesian-prior-sensitivity-assistant/package.json
new file mode 100644
index 00000000..45e6a26a
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "bayesian-prior-sensitivity-assistant",
+ "version": "1.0.0",
+ "description": "Synthetic Bayesian prior sensitivity review assistant for SCIBASE issue 16.",
+ "main": "index.js",
+ "scripts": {
+ "check": "node --check index.js && node --check sample-data.js && node --check test.js && node --check demo.js && node --check make-demo-video.js",
+ "test": "node test.js",
+ "demo": "node demo.js",
+ "demo:video": "node make-demo-video.js"
+ },
+ "license": "MIT"
+}
diff --git a/bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-packet.json b/bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-packet.json
new file mode 100644
index 00000000..94709540
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-packet.json
@@ -0,0 +1,138 @@
+{
+ "mixed": {
+ "generatedAt": "2026-05-28T07:30:00.000Z",
+ "manuscriptId": "synthetic-bayesian-review",
+ "status": "hold_peer_review_claims",
+ "summary": {
+ "status": "hold_peer_review_claims",
+ "analysisCount": 3,
+ "major": 1,
+ "minor": 1,
+ "ready": 1,
+ "blockerCount": 7,
+ "warningCount": 6
+ },
+ "analyses": [
+ {
+ "analysisId": "clinical-survival-model",
+ "domain": "clinical trials",
+ "decision": "major_revision_prior_robustness",
+ "blockers": [
+ {
+ "code": "prior_not_reported",
+ "message": "1 model prior(s) are used without manuscript disclosure."
+ },
+ {
+ "code": "posterior_direction_flips_under_prior",
+ "message": "skeptical treatment prior changes effect direction from 0.31 to -0.08."
+ },
+ {
+ "code": "divergent_transitions_present",
+ "message": "14 divergent transition(s) were reported."
+ },
+ {
+ "code": "severe_rhat_convergence_failure",
+ "message": "Maximum R-hat is 1.08."
+ },
+ {
+ "code": "severe_effective_sample_size_shortfall",
+ "message": "Minimum effective sample size is 112."
+ },
+ {
+ "code": "prior_predictive_check_missing",
+ "message": "No prior predictive check is reported for the Bayesian model."
+ },
+ {
+ "code": "strong_claim_not_prior_robust",
+ "message": "claim-treatment-definitive uses strong language without prior-robust support."
+ }
+ ],
+ "warnings": [
+ {
+ "code": "informative_prior_not_justified",
+ "message": "1 informative prior(s) lack domain justification."
+ },
+ {
+ "code": "large_posterior_shift_under_prior",
+ "message": "wider frailty prior shifts posterior estimate by 0.27."
+ }
+ ],
+ "actions": [
+ "Disclose all model priors with parameter values and rationale.",
+ "Add domain rationale or sensitivity analysis for informative priors.",
+ "Mark the manuscript claim as prior-sensitive and add a robustness table.",
+ "Report alternate-prior posterior estimates beside the primary model.",
+ "Resolve sampler divergences before accepting posterior claims.",
+ "Rerun or reparameterize the model until convergence diagnostics pass.",
+ "Increase sampling effort or simplify the model before review.",
+ "Add a prior predictive check before claims are promoted to peer-review text.",
+ "Downgrade claim language or add prior-robust evidence."
+ ]
+ },
+ {
+ "analysisId": "materials-yield-model",
+ "domain": "materials science",
+ "decision": "minor_revision_prior_robustness",
+ "blockers": [],
+ "warnings": [
+ {
+ "code": "informative_prior_not_justified",
+ "message": "1 informative prior(s) lack domain justification."
+ },
+ {
+ "code": "rhat_above_review_threshold",
+ "message": "Maximum R-hat is 1.02."
+ },
+ {
+ "code": "low_effective_sample_size",
+ "message": "Minimum effective sample size is 260."
+ },
+ {
+ "code": "weak_prior_predictive_coverage",
+ "message": "Prior predictive coverage is 58%."
+ }
+ ],
+ "actions": [
+ "Add domain rationale or sensitivity analysis for informative priors.",
+ "Include convergence diagnostics and rerun chains if needed.",
+ "Report effective sample sizes and rerun where posterior tails are unstable.",
+ "Revise priors or explain why prior predictive coverage is low."
+ ]
+ },
+ {
+ "analysisId": "ecology-occupancy-model",
+ "domain": "ecology",
+ "decision": "ready_for_peer_review",
+ "blockers": [],
+ "warnings": [],
+ "actions": []
+ }
+ ],
+ "auditDigest": "0f1f6b29a79b38614e0b5c509f8479266adf55dd81661884b67153e2eb5f9757"
+ },
+ "ready": {
+ "generatedAt": "2026-05-28T07:30:00.000Z",
+ "manuscriptId": "synthetic-ready-bayesian-review",
+ "status": "ready",
+ "summary": {
+ "status": "ready",
+ "analysisCount": 1,
+ "major": 0,
+ "minor": 0,
+ "ready": 1,
+ "blockerCount": 0,
+ "warningCount": 0
+ },
+ "analyses": [
+ {
+ "analysisId": "ecology-occupancy-model",
+ "domain": "ecology",
+ "decision": "ready_for_peer_review",
+ "blockers": [],
+ "warnings": [],
+ "actions": []
+ }
+ ],
+ "auditDigest": "43a9b74dc9b5b4ed480e414f2f5d7b6a7e9d8dae4df9b00cfddabb354aeea190"
+ }
+}
diff --git a/bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-report.md b/bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-report.md
new file mode 100644
index 00000000..89cf4706
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-report.md
@@ -0,0 +1,35 @@
+# Bayesian Prior Sensitivity Assistant Report
+
+Synthetic auto peer-review packet for SCIBASE issue #16.
+
+## Summary
+
+| Packet | Status | Major | Minor | Ready | Digest |
+| --- | --- | ---: | ---: | ---: | --- |
+| mixed | hold_peer_review_claims | 1 | 1 | 1 | 0f1f6b29a79b |
+| ready | ready | 0 | 0 | 1 | 43a9b74dc9b5 |
+
+## Analysis Decisions
+
+### clinical-survival-model: major_revision_prior_robustness
+- blocker prior_not_reported: 1 model prior(s) are used without manuscript disclosure.
+- blocker posterior_direction_flips_under_prior: skeptical treatment prior changes effect direction from 0.31 to -0.08.
+- blocker divergent_transitions_present: 14 divergent transition(s) were reported.
+- blocker severe_rhat_convergence_failure: Maximum R-hat is 1.08.
+- blocker severe_effective_sample_size_shortfall: Minimum effective sample size is 112.
+- blocker prior_predictive_check_missing: No prior predictive check is reported for the Bayesian model.
+- blocker strong_claim_not_prior_robust: claim-treatment-definitive uses strong language without prior-robust support.
+- warning informative_prior_not_justified: 1 informative prior(s) lack domain justification.
+- warning large_posterior_shift_under_prior: wider frailty prior shifts posterior estimate by 0.27.
+
+### materials-yield-model: minor_revision_prior_robustness
+- warning informative_prior_not_justified: 1 informative prior(s) lack domain justification.
+- warning rhat_above_review_threshold: Maximum R-hat is 1.02.
+- warning low_effective_sample_size: Minimum effective sample size is 260.
+- warning weak_prior_predictive_coverage: Prior predictive coverage is 58%.
+
+### ecology-occupancy-model: ready_for_peer_review
+
+## Non-Overlap Notes
+
+This module focuses specifically on Bayesian prior sensitivity, convergence diagnostics, prior predictive checks, and posterior claim calibration. It does not implement a broad assistant suite, multiple-comparison correction, generic statistical consistency checks, study power feasibility, uncertainty tone review, prompt safety, literature freshness, image integrity, external validity, or protocol trace workflows.
diff --git a/bayesian-prior-sensitivity-assistant/reports/demo.avi b/bayesian-prior-sensitivity-assistant/reports/demo.avi
new file mode 100644
index 00000000..cc3de3b7
Binary files /dev/null and b/bayesian-prior-sensitivity-assistant/reports/demo.avi differ
diff --git a/bayesian-prior-sensitivity-assistant/reports/summary.svg b/bayesian-prior-sensitivity-assistant/reports/summary.svg
new file mode 100644
index 00000000..b9c0ce1f
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/reports/summary.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/bayesian-prior-sensitivity-assistant/requirements-map.md b/bayesian-prior-sensitivity-assistant/requirements-map.md
new file mode 100644
index 00000000..460e8720
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/requirements-map.md
@@ -0,0 +1,13 @@
+# Requirements Map
+
+| Issue #16 requirement | Coverage |
+| --- | --- |
+| Auto peer review reports | Emits structured reviewer findings, severity, remediation actions, and deterministic audit digests. |
+| Statistical or methodological red flags | Flags prior sensitivity, convergence, divergent transitions, low effective sample size, and weak prior predictive coverage. |
+| Claims vs. evidence alignment | Checks whether strong manuscript claims remain robust under alternate priors and posterior probabilities. |
+| Adaptive templates per domain | Includes synthetic clinical, materials science, and ecology review packets with domain labels. |
+| Reproducibility confidence support | Records deterministic outcomes and diagnostics that can feed reproducibility review workflows. |
+
+## Non-Overlap
+
+This is not a broad AI assistant suite, multiple-comparison control tool, generic statistical consistency checker, study power feasibility assistant, uncertainty tone reviewer, prompt safety guard, literature freshness checker, image integrity tool, external-validity reviewer, or protocol trace workflow. It focuses narrowly on Bayesian prior sensitivity and posterior robustness.
diff --git a/bayesian-prior-sensitivity-assistant/sample-data.js b/bayesian-prior-sensitivity-assistant/sample-data.js
new file mode 100644
index 00000000..51eaa528
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/sample-data.js
@@ -0,0 +1,143 @@
+"use strict";
+
+const mixedBayesianReviewPacket = {
+ generatedAt: "2026-05-28T07:30:00Z",
+ manuscriptId: "synthetic-bayesian-review",
+ analyses: [
+ {
+ id: "clinical-survival-model",
+ domain: "clinical trials",
+ priors: [
+ {
+ name: "treatment_log_hazard_ratio",
+ reported: true,
+ strength: "informative",
+ justification: false
+ },
+ {
+ name: "baseline_hazard_spline",
+ reported: false,
+ strength: "weakly-informative",
+ justification: false
+ }
+ ],
+ sensitivityRuns: [
+ {
+ name: "skeptical treatment prior",
+ baselineEstimate: 0.31,
+ alternateEstimate: -0.08
+ },
+ {
+ name: "wider frailty prior",
+ baselineEstimate: 0.31,
+ alternateEstimate: 0.04
+ }
+ ],
+ diagnostics: {
+ maxRhat: 1.08,
+ minEffectiveSampleSize: 112,
+ divergentTransitions: 14
+ },
+ priorPredictiveCheck: {
+ reported: false,
+ coverage: 0
+ },
+ claims: [
+ {
+ id: "claim-treatment-definitive",
+ posteriorProbability: 0.91,
+ strength: "definitive",
+ robustAcrossPriors: false
+ }
+ ]
+ },
+ {
+ id: "materials-yield-model",
+ domain: "materials science",
+ priors: [
+ {
+ name: "thermal_gradient_effect",
+ reported: true,
+ strength: "informative",
+ justification: false
+ }
+ ],
+ sensitivityRuns: [
+ {
+ name: "neutral process prior",
+ baselineEstimate: 0.44,
+ alternateEstimate: 0.29
+ }
+ ],
+ diagnostics: {
+ maxRhat: 1.02,
+ minEffectiveSampleSize: 260,
+ divergentTransitions: 0
+ },
+ priorPredictiveCheck: {
+ reported: true,
+ coverage: 0.58
+ },
+ claims: [
+ {
+ id: "claim-yield-improves",
+ posteriorProbability: 0.94,
+ strength: "likely",
+ robustAcrossPriors: true
+ }
+ ]
+ },
+ {
+ id: "ecology-occupancy-model",
+ domain: "ecology",
+ priors: [
+ {
+ name: "occupancy_intercept",
+ reported: true,
+ strength: "weakly-informative",
+ justification: true
+ }
+ ],
+ sensitivityRuns: [
+ {
+ name: "wide occupancy prior",
+ baselineEstimate: 0.22,
+ alternateEstimate: 0.18
+ },
+ {
+ name: "skeptical occupancy prior",
+ baselineEstimate: 0.22,
+ alternateEstimate: 0.2
+ }
+ ],
+ diagnostics: {
+ maxRhat: 1,
+ minEffectiveSampleSize: 920,
+ divergentTransitions: 0
+ },
+ priorPredictiveCheck: {
+ reported: true,
+ coverage: 0.82
+ },
+ claims: [
+ {
+ id: "claim-habitat-associated",
+ posteriorProbability: 0.96,
+ strength: "likely",
+ robustAcrossPriors: true
+ }
+ ]
+ }
+ ]
+};
+
+const readyBayesianReviewPacket = {
+ generatedAt: "2026-05-28T07:30:00Z",
+ manuscriptId: "synthetic-ready-bayesian-review",
+ analyses: [mixedBayesianReviewPacket.analyses[2]]
+};
+
+module.exports = {
+ mixedBayesianReviewPacket,
+ readyBayesianReviewPacket
+};
diff --git a/bayesian-prior-sensitivity-assistant/test.js b/bayesian-prior-sensitivity-assistant/test.js
new file mode 100644
index 00000000..1da0814b
--- /dev/null
+++ b/bayesian-prior-sensitivity-assistant/test.js
@@ -0,0 +1,61 @@
+"use strict";
+
+const assert = require("assert");
+const { assessBayesianPriorSensitivity } = require("./index");
+const { mixedBayesianReviewPacket, readyBayesianReviewPacket } = require("./sample-data");
+
+function testHighRiskBayesianAnalysisIsHeld() {
+ const result = assessBayesianPriorSensitivity(mixedBayesianReviewPacket);
+ const clinical = result.analyses.find((analysis) => analysis.analysisId === "clinical-survival-model");
+ const codes = clinical.blockers.map((blocker) => blocker.code);
+
+ assert.strictEqual(result.status, "hold_peer_review_claims");
+ assert.strictEqual(clinical.decision, "major_revision_prior_robustness");
+ assert(codes.includes("prior_not_reported"));
+ assert(codes.includes("posterior_direction_flips_under_prior"));
+ assert(codes.includes("divergent_transitions_present"));
+ assert(codes.includes("severe_rhat_convergence_failure"));
+ assert(codes.includes("severe_effective_sample_size_shortfall"));
+ assert(codes.includes("prior_predictive_check_missing"));
+ assert(codes.includes("strong_claim_not_prior_robust"));
+}
+
+function testModerateBayesianAnalysisNeedsMinorRevision() {
+ const result = assessBayesianPriorSensitivity(mixedBayesianReviewPacket);
+ const materials = result.analyses.find((analysis) => analysis.analysisId === "materials-yield-model");
+ const codes = materials.warnings.map((warning) => warning.code);
+
+ assert.strictEqual(materials.decision, "minor_revision_prior_robustness");
+ assert(codes.includes("informative_prior_not_justified"));
+ assert(codes.includes("rhat_above_review_threshold"));
+ assert(codes.includes("low_effective_sample_size"));
+ assert(codes.includes("weak_prior_predictive_coverage"));
+}
+
+function testReadyBayesianAnalysisPasses() {
+ const result = assessBayesianPriorSensitivity(readyBayesianReviewPacket);
+ const ready = result.analyses[0];
+
+ assert.strictEqual(result.status, "ready");
+ assert.strictEqual(ready.decision, "ready_for_peer_review");
+ assert.strictEqual(ready.blockers.length, 0);
+ assert.strictEqual(ready.warnings.length, 0);
+}
+
+function testDigestIsDeterministic() {
+ const first = assessBayesianPriorSensitivity(mixedBayesianReviewPacket);
+ const second = assessBayesianPriorSensitivity(mixedBayesianReviewPacket);
+
+ assert.match(first.auditDigest, /^[a-f0-9]{64}$/);
+ assert.strictEqual(first.auditDigest, second.auditDigest);
+}
+
+function run() {
+ testHighRiskBayesianAnalysisIsHeld();
+ testModerateBayesianAnalysisNeedsMinorRevision();
+ testReadyBayesianAnalysisPasses();
+ testDigestIsDeterministic();
+ console.log("4 tests passed");
+}
+
+run();