From 846188d346dfa36d0be3257d5e9a518f335d210f Mon Sep 17 00:00:00 2001 From: shaiananvari8 <228813044+shaiananvari8@users.noreply.github.com> Date: Wed, 27 May 2026 23:45:48 -0500 Subject: [PATCH] Add Bayesian prior sensitivity assistant --- .../README.md | 35 +++ .../acceptance-notes.md | 19 ++ bayesian-prior-sensitivity-assistant/demo.js | 84 +++++++ bayesian-prior-sensitivity-assistant/index.js | 229 ++++++++++++++++++ .../make-demo-video.js | 163 +++++++++++++ .../package.json | 13 + .../bayesian-prior-sensitivity-packet.json | 138 +++++++++++ .../bayesian-prior-sensitivity-report.md | 35 +++ .../reports/demo.avi | Bin 0 -> 2765416 bytes .../reports/summary.svg | 20 ++ .../requirements-map.md | 13 + .../sample-data.js | 143 +++++++++++ bayesian-prior-sensitivity-assistant/test.js | 61 +++++ 13 files changed, 953 insertions(+) create mode 100644 bayesian-prior-sensitivity-assistant/README.md create mode 100644 bayesian-prior-sensitivity-assistant/acceptance-notes.md create mode 100644 bayesian-prior-sensitivity-assistant/demo.js create mode 100644 bayesian-prior-sensitivity-assistant/index.js create mode 100644 bayesian-prior-sensitivity-assistant/make-demo-video.js create mode 100644 bayesian-prior-sensitivity-assistant/package.json create mode 100644 bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-packet.json create mode 100644 bayesian-prior-sensitivity-assistant/reports/bayesian-prior-sensitivity-report.md create mode 100644 bayesian-prior-sensitivity-assistant/reports/demo.avi create mode 100644 bayesian-prior-sensitivity-assistant/reports/summary.svg create mode 100644 bayesian-prior-sensitivity-assistant/requirements-map.md create mode 100644 bayesian-prior-sensitivity-assistant/sample-data.js create mode 100644 bayesian-prior-sensitivity-assistant/test.js 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 ` + + + + Bayesian prior sensitivity assistant + Auto peer-review red flags for posterior robustness + ${bar(56, 154, "Major revision", mixed.major, "#b91c1c")} + ${bar(56, 214, "Minor revision", mixed.minor, "#b7791f")} + ${bar(56, 274, "Ready", mixed.ready, "#15803d")} + Digest ${packet.mixed.auditDigest.slice(0, 16)} + +`; +} + +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 0000000000000000000000000000000000000000..cc3de3b756aa88929cb4448718f4a036f8afac49 GIT binary patch literal 2765416 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 literal 0 HcmV?d00001 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 @@ + + + + + Bayesian prior sensitivity assistant + Auto peer-review red flags for posterior robustness + Major revision + + + 1 analysis item(s) + Minor revision + + + 1 analysis item(s) + Ready + + + 1 analysis item(s) + Digest 0f1f6b29a79b3861 + 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();