From 510af9ad0a5d681a5ef0201484333044e56c478e Mon Sep 17 00:00:00 2001 From: telemarkdigital-publisher Date: Sun, 16 Aug 2026 21:41:14 +0200 Subject: [PATCH] Add compute rerun feasibility guard --- PR_BODY_COMPUTE_RERUN_FEASIBILITY_16.md | 34 ++ .../README.md | 47 +++ .../data/sample-rerun-packets.json | 143 +++++++ .../package.json | 21 + .../reports/demo.mp4 | Bin 0 -> 36734 bytes .../reports/reviewer-packet.md | 20 + .../reports/summary.json | 224 +++++++++++ .../reports/summary.svg | 44 +++ .../scripts/demo.js | 95 +++++ .../scripts/render-demo-video.js | 68 ++++ .../src/index.js | 366 ++++++++++++++++++ .../test/compute-rerun-feasibility.test.js | 49 +++ 12 files changed, 1111 insertions(+) create mode 100644 PR_BODY_COMPUTE_RERUN_FEASIBILITY_16.md create mode 100644 research-compute-rerun-feasibility-guard/README.md create mode 100644 research-compute-rerun-feasibility-guard/data/sample-rerun-packets.json create mode 100644 research-compute-rerun-feasibility-guard/package.json create mode 100644 research-compute-rerun-feasibility-guard/reports/demo.mp4 create mode 100644 research-compute-rerun-feasibility-guard/reports/reviewer-packet.md create mode 100644 research-compute-rerun-feasibility-guard/reports/summary.json create mode 100644 research-compute-rerun-feasibility-guard/reports/summary.svg create mode 100644 research-compute-rerun-feasibility-guard/scripts/demo.js create mode 100644 research-compute-rerun-feasibility-guard/scripts/render-demo-video.js create mode 100644 research-compute-rerun-feasibility-guard/src/index.js create mode 100644 research-compute-rerun-feasibility-guard/test/compute-rerun-feasibility.test.js diff --git a/PR_BODY_COMPUTE_RERUN_FEASIBILITY_16.md b/PR_BODY_COMPUTE_RERUN_FEASIBILITY_16.md new file mode 100644 index 00000000..fed579db --- /dev/null +++ b/PR_BODY_COMPUTE_RERUN_FEASIBILITY_16.md @@ -0,0 +1,34 @@ +/claim #16 + +## Summary + +Adds `research-compute-rerun-feasibility-guard`, a standalone local package for the AI-Powered Research Assistant Suite. + +The guard prevents AI assistant reproducibility language from being released until the project has enough compute-resource evidence for a practical reviewer rerun. It checks hardware specs, RAM/VRAM feasibility, runtime budget, checkpoint/resume evidence, container/lockfile/runtime digests, accelerator determinism, large data-transfer fixture availability, paid cloud/HPC disclosure, and long-run resource notes. + +## Demo Video + +`research-compute-rerun-feasibility-guard/reports/demo.mp4` + +The MP4 is a synthetic FFmpeg-rendered slate. It does not capture a desktop, private data, credentials, real manuscripts, or paid services. + +## Validation + +From `research-compute-rerun-feasibility-guard`: + +- `npm run check` +- `npm test` +- `npm run demo` +- `npm run video` + +Repository-level: + +- `git diff --check` + +## Notes + +- No external APIs. +- No paid cloud resources. +- No credentials or private data. +- Synthetic fixtures only. +- Scope is distinct from existing #16 slices around general dependency locks, evidence grounding, external validity, image integrity, statistical/model assumptions, data fabrication, and generic reproducibility attempts. This package specifically gates AI assistant output on practical compute rerun feasibility and resource disclosure. diff --git a/research-compute-rerun-feasibility-guard/README.md b/research-compute-rerun-feasibility-guard/README.md new file mode 100644 index 00000000..4e4255a6 --- /dev/null +++ b/research-compute-rerun-feasibility-guard/README.md @@ -0,0 +1,47 @@ +# Research Compute Rerun Feasibility Guard + +This package adds a local release gate for the SCIBASE AI-Powered Research Assistant Suite. It checks whether an AI assistant can safely describe a manuscript or project as practically reproducible before the assistant output reaches authors, reviewers, journals, or funders. + +The guard focuses on compute feasibility rather than general dependency locking or claim grounding. It asks whether a reviewer can actually rerun the work with the stated resources, time budget, determinism controls, and cost disclosure. + +## What It Checks + +- Hardware profile completeness: CPU, RAM, accelerator, and VRAM. +- Dataset working set vs RAM and GPU memory. +- Runtime estimate vs reviewer budget. +- Long-run checkpoint/resume evidence. +- Container, lockfile, runtime, or notebook environment digests. +- Accelerator determinism controls and seed policy. +- Large data transfer and fixture availability. +- Paid cloud or queued HPC requirements and disclosure. +- Resource-impact note for long reruns. + +## Release Decisions + +- `RELEASE_ASSISTANT_OUTPUT`: the assistant can present the rerun as reviewer-feasible. +- `REVISE_ASSISTANT_OUTPUT`: wording must be softened until missing resource evidence is repaired. +- `HOLD_ASSISTANT_OUTPUT`: the assistant must not endorse reproducibility because the compute plan is materially unsafe. + +## Local Demo + +```bash +npm run check +npm test +npm run demo +npm run video +``` + +Generated artifacts: + +- `reports/summary.json` +- `reports/reviewer-packet.md` +- `reports/summary.svg` +- `reports/demo.mp4` + +All fixtures are synthetic. The package uses no external APIs, paid cloud, credentials, private data, real manuscripts, or desktop capture. + +## Why This Fits Issue #16 + +Issue #16 includes a reproducibility checker as a core AI research assistant capability. A research assistant can mislead reviewers if it says a project is reproducible while the rerun secretly requires unavailable GPUs, huge memory, long queues, paid cloud resources, or nondeterministic accelerator settings. + +This guard adds a practical compute/resource release gate so the assistant can distinguish a reviewer-feasible rerun from an overclaimed reproducibility endorsement. diff --git a/research-compute-rerun-feasibility-guard/data/sample-rerun-packets.json b/research-compute-rerun-feasibility-guard/data/sample-rerun-packets.json new file mode 100644 index 00000000..9927a9b4 --- /dev/null +++ b/research-compute-rerun-feasibility-guard/data/sample-rerun-packets.json @@ -0,0 +1,143 @@ +[ + { + "id": "paper-cpu-fixture-pass", + "title": "Open fixture rerun for a tabular treatment-effect study", + "assistantOutput": "pre-submission reproducibility checker", + "dataset": { + "sizeGb": 1.2, + "downloadGb": 1.2, + "workingSetMultiplier": 1.8, + "syntheticFixtureAvailable": true, + "requiresGpuResidency": false + }, + "compute": { + "cpuCores": 8, + "ramGb": 32, + "accelerator": "cpu", + "gpuCount": 0, + "gpuVramGb": 0, + "estimatedRuntimeHours": 1.5, + "reviewerBudgetHours": 4, + "paidCloudRequired": false, + "hpcQueueRequired": false + }, + "environment": { + "containerDigest": "sha256:fixture-pass-container", + "lockfileDigest": "sha256:fixture-pass-lock" + }, + "reproducibility": { + "seedPolicy": "fixed seed family with three rerun seeds", + "determinismControls": ["seed manifest", "variance tolerance report"], + "checkpointResumeEvidence": "not required for short CPU rerun" + }, + "disclosure": { + "estimatedCostUsd": 0, + "carbonOrResourceNote": "CPU fixture rerun completes locally under two hours." + } + }, + { + "id": "paper-gpu-overclaim-hold", + "title": "Large multimodal training rerun marked reproducible without resources", + "assistantOutput": "AI reviewer reproducibility endorsement", + "dataset": { + "sizeGb": 480, + "downloadGb": 620, + "workingSetMultiplier": 2.2, + "syntheticFixtureAvailable": false, + "requiresGpuResidency": true + }, + "compute": { + "cpuCores": 16, + "ramGb": 64, + "accelerator": "cuda", + "gpuCount": 1, + "gpuVramGb": 24, + "estimatedRuntimeHours": 38, + "reviewerBudgetHours": 8, + "paidCloudRequired": true, + "hpcQueueRequired": false + }, + "environment": {}, + "reproducibility": { + "seedPolicy": "", + "determinismControls": [], + "checkpointResumeEvidence": "" + }, + "disclosure": { + "estimatedCostUsd": 0, + "carbonOrResourceNote": "" + } + }, + { + "id": "paper-hpc-revise", + "title": "Queued HPC simulation with enough hardware but weak disclosure", + "assistantOutput": "journal internal reviewer copilot", + "dataset": { + "sizeGb": 32, + "downloadGb": 40, + "workingSetMultiplier": 1.5, + "syntheticFixtureAvailable": true, + "requiresGpuResidency": false + }, + "compute": { + "cpuCores": 48, + "ramGb": 256, + "accelerator": "cpu", + "gpuCount": 0, + "gpuVramGb": 0, + "estimatedRuntimeHours": 12, + "reviewerBudgetHours": 8, + "paidCloudRequired": false, + "hpcQueueRequired": true + }, + "environment": { + "runtimeDigest": "sha256:hpc-module-stack" + }, + "reproducibility": { + "seedPolicy": "fixed Monte Carlo seed manifest", + "determinismControls": ["seed manifest", "variance tolerance"], + "checkpointResumeEvidence": "checkpoint manifest for every 90 simulated minutes" + }, + "disclosure": { + "estimatedCostUsd": 0, + "queuePolicy": "institutional queue, expected wait not stated", + "carbonOrResourceNote": "" + } + }, + { + "id": "paper-vram-fixture-needed", + "title": "Single-cell embedding rerun with GPU memory shortfall", + "assistantOutput": "research-gap assistant reproducibility sidebar", + "dataset": { + "sizeGb": 78, + "downloadGb": 92, + "workingSetMultiplier": 1.4, + "syntheticFixtureAvailable": false, + "requiresGpuResidency": true + }, + "compute": { + "cpuCores": 24, + "ramGb": 192, + "accelerator": "cuda", + "gpuCount": 1, + "gpuVramGb": 40, + "estimatedRuntimeHours": 7, + "reviewerBudgetHours": 6, + "paidCloudRequired": true, + "hpcQueueRequired": true + }, + "environment": { + "containerDigest": "sha256:single-cell-container" + }, + "reproducibility": { + "seedPolicy": "seeded embedding initialization", + "determinismControls": ["seed manifest", "cuda deterministic kernels"], + "checkpointResumeEvidence": "" + }, + "disclosure": { + "estimatedCostUsd": 48, + "queuePolicy": "shared GPU queue", + "carbonOrResourceNote": "" + } + } +] diff --git a/research-compute-rerun-feasibility-guard/package.json b/research-compute-rerun-feasibility-guard/package.json new file mode 100644 index 00000000..c8515ccc --- /dev/null +++ b/research-compute-rerun-feasibility-guard/package.json @@ -0,0 +1,21 @@ +{ + "name": "research-compute-rerun-feasibility-guard", + "version": "1.0.0", + "description": "Local guard that checks whether AI research assistant reproducibility claims are compute-feasible before reviewer release.", + "type": "module", + "main": "src/index.js", + "scripts": { + "check": "node --check src/index.js && node --check scripts/demo.js && node --check scripts/render-demo-video.js && node --check test/compute-rerun-feasibility.test.js", + "test": "node --test", + "demo": "node scripts/demo.js", + "video": "node scripts/render-demo-video.js" + }, + "keywords": [ + "research-assistant", + "reproducibility", + "compute-feasibility", + "peer-review", + "scibase" + ], + "license": "MIT" +} diff --git a/research-compute-rerun-feasibility-guard/reports/demo.mp4 b/research-compute-rerun-feasibility-guard/reports/demo.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..daea6bb294da9e8f4e1ab2a549de8347c604ea0c GIT binary patch literal 36734 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 literal 0 HcmV?d00001 diff --git a/research-compute-rerun-feasibility-guard/reports/reviewer-packet.md b/research-compute-rerun-feasibility-guard/reports/reviewer-packet.md new file mode 100644 index 00000000..6178792d --- /dev/null +++ b/research-compute-rerun-feasibility-guard/reports/reviewer-packet.md @@ -0,0 +1,20 @@ +# Research Compute Rerun Feasibility Guard + +Generated: 2026-08-16T19:40:17.361Z + +This packet is a local synthetic demonstration for SCIBASE issue #16. It checks whether an AI-powered research assistant should release reproducibility language before the compute plan is practically rerunnable by a reviewer. + +| Packet | Release gate | Score | Working set | Top findings | +| --- | --- | ---: | ---: | --- | +| paper-cpu-fixture-pass | RELEASE_ASSISTANT_OUTPUT | 100 | 2.16 GB | None | +| paper-gpu-overclaim-hold | HOLD_ASSISTANT_OUTPUT | 0 | 1056 GB | CONTAINER_OR_LOCKFILE_MISSING (high); DATASET_MEMORY_EXCEEDS_NODE (critical); GPU_VRAM_FEASIBILITY_GAP (high) | +| paper-hpc-revise | REVISE_ASSISTANT_OUTPUT | 40 | 48 GB | RUNTIME_BUDGET_UNREALISTIC (medium); CLOUD_OR_HPC_COST_UNDISCLOSED (high); RESOURCE_IMPACT_NOTE_MISSING (low) | +| paper-vram-fixture-needed | HOLD_ASSISTANT_OUTPUT | 0 | 109.2 GB | GPU_VRAM_FEASIBILITY_GAP (high); RUNTIME_BUDGET_UNREALISTIC (medium); CHECKPOINT_RESUME_MISSING (high) | + +## Release Policy + +- `RELEASE_ASSISTANT_OUTPUT`: the assistant can present the rerun as reviewer-feasible. +- `REVISE_ASSISTANT_OUTPUT`: assistant wording must be softened until missing resource evidence is repaired. +- `HOLD_ASSISTANT_OUTPUT`: the assistant must not endorse reproducibility because compute feasibility is materially unsafe. + +No external API, private data, paid cloud, real manuscript, or user desktop capture is used. diff --git a/research-compute-rerun-feasibility-guard/reports/summary.json b/research-compute-rerun-feasibility-guard/reports/summary.json new file mode 100644 index 00000000..225aa5f5 --- /dev/null +++ b/research-compute-rerun-feasibility-guard/reports/summary.json @@ -0,0 +1,224 @@ +{ + "generatedAt": "2026-08-16T19:40:17.361Z", + "packetCount": 4, + "decisionCounts": { + "RELEASE_ASSISTANT_OUTPUT": 1, + "HOLD_ASSISTANT_OUTPUT": 2, + "REVISE_ASSISTANT_OUTPUT": 1 + }, + "results": [ + { + "id": "paper-cpu-fixture-pass", + "title": "Open fixture rerun for a tabular treatment-effect study", + "decision": "RELEASE_ASSISTANT_OUTPUT", + "score": 100, + "summary": { + "packetId": "paper-cpu-fixture-pass", + "title": "Open fixture rerun for a tabular treatment-effect study", + "assistantOutput": "pre-submission reproducibility checker", + "estimatedWorkingSetGb": 2.16, + "ramGb": 32, + "totalGpuVramGb": 0, + "findingCount": 0, + "worstSeverity": "none" + }, + "findings": [], + "releaseGate": { + "canReleaseAssistantOutput": true, + "reviewerMessage": "Compute evidence is sufficient for the assistant to present the rerun as reviewer-feasible." + } + }, + { + "id": "paper-gpu-overclaim-hold", + "title": "Large multimodal training rerun marked reproducible without resources", + "decision": "HOLD_ASSISTANT_OUTPUT", + "score": 0, + "summary": { + "packetId": "paper-gpu-overclaim-hold", + "title": "Large multimodal training rerun marked reproducible without resources", + "assistantOutput": "AI reviewer reproducibility endorsement", + "estimatedWorkingSetGb": 1056, + "ramGb": 64, + "totalGpuVramGb": 24, + "findingCount": 10, + "worstSeverity": "critical" + }, + "findings": [ + { + "code": "CONTAINER_OR_LOCKFILE_MISSING", + "severity": "high", + "title": "Executable environment evidence is missing", + "evidence": "No container digest, lockfile digest, runtime digest, or notebook environment digest was provided.", + "remediation": "Attach a digest-pinned container, lockfile, runtime manifest, or archived notebook environment." + }, + { + "code": "DATASET_MEMORY_EXCEEDS_NODE", + "severity": "critical", + "title": "Dataset working set exceeds stated system memory", + "evidence": "1056 GB estimated working set vs 64 GB RAM with 4 GB reserved headroom.", + "remediation": "Provide a smaller reviewer fixture, streaming pipeline proof, shard plan, or larger reproducible hardware profile." + }, + { + "code": "GPU_VRAM_FEASIBILITY_GAP", + "severity": "high", + "title": "GPU-resident workload exceeds available VRAM", + "evidence": "1056 GB working set marked GPU-resident vs 24 GB total VRAM.", + "remediation": "Document gradient accumulation, checkpointing, offload, or a verified smaller fixture before release." + }, + { + "code": "RUNTIME_BUDGET_UNREALISTIC", + "severity": "high", + "title": "Reviewer rerun time exceeds the stated review budget", + "evidence": "38 hour rerun estimate vs 8 hour reviewer budget.", + "remediation": "Split the rerun into smoke, fixture, and full modes or disclose that full reproduction requires extended resources." + }, + { + "code": "CHECKPOINT_RESUME_MISSING", + "severity": "high", + "title": "Long rerun lacks checkpoint or resume evidence", + "evidence": "38 hour run has no checkpoint/resume evidence in the packet.", + "remediation": "Attach a checkpoint manifest, restart transcript, or resumable workflow proof." + }, + { + "code": "NONDETERMINISTIC_ACCELERATOR_PATH", + "severity": "high", + "title": "Accelerator rerun lacks determinism controls", + "evidence": "cuda path is present without seed policy plus deterministic kernel/runtime controls.", + "remediation": "Document seed handling, deterministic kernel settings, tolerance windows, and accepted variance." + }, + { + "code": "SEED_POLICY_MISSING", + "severity": "medium", + "title": "Seed or variance policy is missing", + "evidence": "The assistant packet has no seed policy and no deterministic control list.", + "remediation": "Add seed policy, variance acceptance bands, and a rerun transcript showing stable outputs." + }, + { + "code": "DATA_ACCESS_BANDWIDTH_GAP", + "severity": "medium", + "title": "Large data transfer lacks a reviewer fixture", + "evidence": "620 GB download is required and no synthetic or reduced reviewer fixture is listed.", + "remediation": "Provide a small fixture, cached digest, or staged data-access plan with expected transfer time." + }, + { + "code": "CLOUD_OR_HPC_COST_UNDISCLOSED", + "severity": "high", + "title": "Paid cloud or queued HPC requirement is not disclosed", + "evidence": "The packet requires paid cloud/HPC resources but does not disclose expected reviewer cost.", + "remediation": "State cost, queue assumptions, no-cost fixture alternative, and who bears the rerun cost." + }, + { + "code": "RESOURCE_IMPACT_NOTE_MISSING", + "severity": "low", + "title": "Long compute rerun lacks resource-impact disclosure", + "evidence": "No carbon/resource note is attached for a long-running rerun.", + "remediation": "Add a short resource-impact note and reviewer-facing rerun alternatives." + } + ], + "releaseGate": { + "canReleaseAssistantOutput": false, + "reviewerMessage": "Hold or revise the assistant's reproducibility wording until compute feasibility evidence is repaired." + } + }, + { + "id": "paper-hpc-revise", + "title": "Queued HPC simulation with enough hardware but weak disclosure", + "decision": "REVISE_ASSISTANT_OUTPUT", + "score": 40, + "summary": { + "packetId": "paper-hpc-revise", + "title": "Queued HPC simulation with enough hardware but weak disclosure", + "assistantOutput": "journal internal reviewer copilot", + "estimatedWorkingSetGb": 48, + "ramGb": 256, + "totalGpuVramGb": 0, + "findingCount": 3, + "worstSeverity": "high" + }, + "findings": [ + { + "code": "RUNTIME_BUDGET_UNREALISTIC", + "severity": "medium", + "title": "Reviewer rerun time exceeds the stated review budget", + "evidence": "12 hour rerun estimate vs 8 hour reviewer budget.", + "remediation": "Split the rerun into smoke, fixture, and full modes or disclose that full reproduction requires extended resources." + }, + { + "code": "CLOUD_OR_HPC_COST_UNDISCLOSED", + "severity": "high", + "title": "Paid cloud or queued HPC requirement is not disclosed", + "evidence": "The packet requires paid cloud/HPC resources but does not disclose expected reviewer cost.", + "remediation": "State cost, queue assumptions, no-cost fixture alternative, and who bears the rerun cost." + }, + { + "code": "RESOURCE_IMPACT_NOTE_MISSING", + "severity": "low", + "title": "Long compute rerun lacks resource-impact disclosure", + "evidence": "No carbon/resource note is attached for a long-running rerun.", + "remediation": "Add a short resource-impact note and reviewer-facing rerun alternatives." + } + ], + "releaseGate": { + "canReleaseAssistantOutput": false, + "reviewerMessage": "Hold or revise the assistant's reproducibility wording until compute feasibility evidence is repaired." + } + }, + { + "id": "paper-vram-fixture-needed", + "title": "Single-cell embedding rerun with GPU memory shortfall", + "decision": "HOLD_ASSISTANT_OUTPUT", + "score": 0, + "summary": { + "packetId": "paper-vram-fixture-needed", + "title": "Single-cell embedding rerun with GPU memory shortfall", + "assistantOutput": "research-gap assistant reproducibility sidebar", + "estimatedWorkingSetGb": 109.2, + "ramGb": 192, + "totalGpuVramGb": 40, + "findingCount": 5, + "worstSeverity": "high" + }, + "findings": [ + { + "code": "GPU_VRAM_FEASIBILITY_GAP", + "severity": "high", + "title": "GPU-resident workload exceeds available VRAM", + "evidence": "109.2 GB working set marked GPU-resident vs 40 GB total VRAM.", + "remediation": "Document gradient accumulation, checkpointing, offload, or a verified smaller fixture before release." + }, + { + "code": "RUNTIME_BUDGET_UNREALISTIC", + "severity": "medium", + "title": "Reviewer rerun time exceeds the stated review budget", + "evidence": "7 hour rerun estimate vs 6 hour reviewer budget.", + "remediation": "Split the rerun into smoke, fixture, and full modes or disclose that full reproduction requires extended resources." + }, + { + "code": "CHECKPOINT_RESUME_MISSING", + "severity": "high", + "title": "Long rerun lacks checkpoint or resume evidence", + "evidence": "7 hour run has no checkpoint/resume evidence in the packet.", + "remediation": "Attach a checkpoint manifest, restart transcript, or resumable workflow proof." + }, + { + "code": "NO_LOW_COST_REVIEWER_MODE", + "severity": "high", + "title": "Expensive rerun lacks a low-cost review mode", + "evidence": "$48.00 estimated cost without a synthetic fixture or low-cost mode.", + "remediation": "Add an inexpensive smoke test and fixture-mode result before the AI assistant marks it reproducible." + }, + { + "code": "RESOURCE_IMPACT_NOTE_MISSING", + "severity": "low", + "title": "Long compute rerun lacks resource-impact disclosure", + "evidence": "No carbon/resource note is attached for a long-running rerun.", + "remediation": "Add a short resource-impact note and reviewer-facing rerun alternatives." + } + ], + "releaseGate": { + "canReleaseAssistantOutput": false, + "reviewerMessage": "Hold or revise the assistant's reproducibility wording until compute feasibility evidence is repaired." + } + } + ] +} diff --git a/research-compute-rerun-feasibility-guard/reports/summary.svg b/research-compute-rerun-feasibility-guard/reports/summary.svg new file mode 100644 index 00000000..16c5b4f2 --- /dev/null +++ b/research-compute-rerun-feasibility-guard/reports/summary.svg @@ -0,0 +1,44 @@ + + + Compute Rerun Feasibility Guard + AI assistant release gate for practical reproducibility claims + + + paper-cpu-fixture-pass + + + 100/100 + RELEASE_ASSISTANT_OUTPUT + + + + paper-gpu-overclaim-hold + + + 0/100 + HOLD_ASSISTANT_OUTPUT + + + + paper-hpc-revise + + + 40/100 + REVISE_ASSISTANT_OUTPUT + + + + paper-vram-fixture-needed + + + 0/100 + HOLD_ASSISTANT_OUTPUT + + + diff --git a/research-compute-rerun-feasibility-guard/scripts/demo.js b/research-compute-rerun-feasibility-guard/scripts/demo.js new file mode 100644 index 00000000..692510af --- /dev/null +++ b/research-compute-rerun-feasibility-guard/scripts/demo.js @@ -0,0 +1,95 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { analyzeRerunPackets } from "../src/index.js"; + +const root = process.cwd(); +const reportsDir = join(root, "reports"); +const packets = JSON.parse(await readFile(join(root, "data", "sample-rerun-packets.json"), "utf8")); +const report = analyzeRerunPackets(packets); + +await mkdir(reportsDir, { recursive: true }); +await writeFile(join(reportsDir, "summary.json"), `${JSON.stringify(report, null, 2)}\n`); +await writeFile(join(reportsDir, "reviewer-packet.md"), renderMarkdown(report)); +await writeFile(join(reportsDir, "summary.svg"), renderSvg(report)); + +console.log(`Wrote ${report.packetCount} packet report to ${reportsDir}`); +console.log(JSON.stringify(report.decisionCounts, null, 2)); + +function renderMarkdown(reportData) { + const rows = reportData.results + .map((result) => { + const topFindings = + result.findings.length === 0 + ? "None" + : result.findings + .slice(0, 3) + .map((finding) => `${finding.code} (${finding.severity})`) + .join("; "); + return `| ${result.id} | ${result.decision} | ${result.score} | ${result.summary.estimatedWorkingSetGb} GB | ${topFindings} |`; + }) + .join("\n"); + + return `# Research Compute Rerun Feasibility Guard + +Generated: ${reportData.generatedAt} + +This packet is a local synthetic demonstration for SCIBASE issue #16. It checks whether an AI-powered research assistant should release reproducibility language before the compute plan is practically rerunnable by a reviewer. + +| Packet | Release gate | Score | Working set | Top findings | +| --- | --- | ---: | ---: | --- | +${rows} + +## Release Policy + +- \`RELEASE_ASSISTANT_OUTPUT\`: the assistant can present the rerun as reviewer-feasible. +- \`REVISE_ASSISTANT_OUTPUT\`: assistant wording must be softened until missing resource evidence is repaired. +- \`HOLD_ASSISTANT_OUTPUT\`: the assistant must not endorse reproducibility because compute feasibility is materially unsafe. + +No external API, private data, paid cloud, real manuscript, or user desktop capture is used. +`; +} + +function renderSvg(reportData) { + const color = { + RELEASE_ASSISTANT_OUTPUT: "#1f9d55", + REVISE_ASSISTANT_OUTPUT: "#c27803", + HOLD_ASSISTANT_OUTPUT: "#c2410c" + }; + const rows = reportData.results + .map((result, index) => { + const y = 112 + index * 86; + const barWidth = Math.max(16, result.score * 5.1); + return ` + + ${escapeXml(result.id)} + + + ${result.score}/100 + ${result.decision} + `; + }) + .join("\n"); + + return ` + + Compute Rerun Feasibility Guard + AI assistant release gate for practical reproducibility claims +${rows} + + +`; +} + +function escapeXml(value) { + return String(value) + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """); +} diff --git a/research-compute-rerun-feasibility-guard/scripts/render-demo-video.js b/research-compute-rerun-feasibility-guard/scripts/render-demo-video.js new file mode 100644 index 00000000..05c04a14 --- /dev/null +++ b/research-compute-rerun-feasibility-guard/scripts/render-demo-video.js @@ -0,0 +1,68 @@ +import { mkdir, readFile } from "node:fs/promises"; +import { spawn } from "node:child_process"; +import { join, resolve } from "node:path"; + +const root = process.cwd(); +const reportsDir = join(root, "reports"); +const output = join(reportsDir, "demo.mp4"); +const ffmpeg = resolve(root, "..", "..", "tool_downloads", "video_tools", "node_modules", "ffmpeg-static", "ffmpeg.exe"); +const font = "C\\:/Windows/Fonts/arial.ttf"; + +await mkdir(reportsDir, { recursive: true }); +const report = JSON.parse(await readFile(join(reportsDir, "summary.json"), "utf8").catch(async () => { + throw new Error("Run npm run demo before npm run video so reports/summary.json exists."); +})); + +const counts = report.decisionCounts || {}; +const release = counts.RELEASE_ASSISTANT_OUTPUT || 0; +const revise = counts.REVISE_ASSISTANT_OUTPUT || 0; +const hold = counts.HOLD_ASSISTANT_OUTPUT || 0; +const headline = "SCIBASE #16: Compute Rerun Feasibility Guard"; +const subhead = `Release ${release} | Revise ${revise} | Hold ${hold}`; +const caption = "Synthetic demo: no desktop capture, private data, API keys, or paid cloud."; + +const drawText = [ + `drawtext=fontfile=${font}:text='${escapeDrawText(headline)}':fontsize=42:fontcolor=white:x=60:y=80`, + `drawtext=fontfile=${font}:text='${escapeDrawText(subhead)}':fontsize=34:fontcolor=0x8ee6a7:x=60:y=160`, + `drawtext=fontfile=${font}:text='${escapeDrawText("Gate AI reproducibility claims on hardware, memory, runtime, determinism, and disclosure evidence.")}':fontsize=24:fontcolor=white:x=60:y=250`, + `drawtext=fontfile=${font}:text='${escapeDrawText(caption)}':fontsize=22:fontcolor=0xcbd5e1:x=60:y=610` +].join(","); + +await run(ffmpeg, [ + "-y", + "-f", + "lavfi", + "-i", + "color=c=0x101827:s=1280x720:d=8:r=30", + "-vf", + drawText, + "-pix_fmt", + "yuv420p", + "-movflags", + "+faststart", + output +]); + +console.log(`Wrote ${output}`); + +function run(command, args) { + return new Promise((resolvePromise, reject) => { + const child = spawn(command, args, { stdio: "inherit" }); + child.on("error", reject); + child.on("exit", (code) => { + if (code === 0) { + resolvePromise(); + } else { + reject(new Error(`${command} exited with ${code}`)); + } + }); + }); +} + +function escapeDrawText(value) { + return String(value) + .replaceAll("\\", "\\\\") + .replaceAll(":", "\\:") + .replaceAll("'", "\\'") + .replaceAll(",", "\\,"); +} diff --git a/research-compute-rerun-feasibility-guard/src/index.js b/research-compute-rerun-feasibility-guard/src/index.js new file mode 100644 index 00000000..71c55c7b --- /dev/null +++ b/research-compute-rerun-feasibility-guard/src/index.js @@ -0,0 +1,366 @@ +const DECISIONS = Object.freeze({ + RELEASE: "RELEASE_ASSISTANT_OUTPUT", + REVISE: "REVISE_ASSISTANT_OUTPUT", + HOLD: "HOLD_ASSISTANT_OUTPUT" +}); + +const SEVERITY_RANK = Object.freeze({ + low: 1, + medium: 2, + high: 3, + critical: 4 +}); + +const DEFAULTS = Object.freeze({ + datasetMultiplier: 2.5, + minimumRamHeadroomGb: 4, + runtimeBudgetHours: 8, + expensiveCloudThresholdUsd: 25, + longRunHours: 6, + largeDatasetGb: 100 +}); + +function toNumber(value, fallback = 0) { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : fallback; +} + +function asArray(value) { + return Array.isArray(value) ? value : []; +} + +function createFinding(code, severity, title, evidence, remediation) { + return { code, severity, title, evidence, remediation }; +} + +function datasetWorkingSetGb(packet) { + const dataset = packet.dataset || {}; + const sizeGb = toNumber(dataset.sizeGb); + const multiplier = toNumber(dataset.workingSetMultiplier, DEFAULTS.datasetMultiplier); + return Number((sizeGb * multiplier).toFixed(2)); +} + +function computeRamGb(packet) { + const compute = packet.compute || {}; + const ramGb = toNumber(compute.ramGb); + const gpuVramGb = toNumber(compute.gpuVramGb); + const gpuCount = toNumber(compute.gpuCount); + return { + ramGb, + totalGpuVramGb: Number((gpuVramGb * Math.max(1, gpuCount || 0)).toFixed(2)) + }; +} + +function hasHardwareSpec(packet) { + const compute = packet.compute || {}; + return Boolean( + compute.cpuCores && + compute.ramGb && + (compute.accelerator === "none" || compute.gpuVramGb || compute.accelerator === "cpu") + ); +} + +function hasContainerEvidence(packet) { + const environment = packet.environment || {}; + return Boolean( + environment.containerDigest || + environment.lockfileDigest || + environment.runtimeDigest || + environment.notebookEnvironmentDigest + ); +} + +function hasDeterminismEvidence(packet) { + const reproducibility = packet.reproducibility || {}; + const controls = asArray(reproducibility.determinismControls); + return Boolean(reproducibility.seedPolicy && controls.length > 0); +} + +function resourceDisclosure(packet) { + const disclosure = packet.disclosure || {}; + return { + hasCost: disclosure.estimatedCostUsd !== undefined, + costUsd: toNumber(disclosure.estimatedCostUsd), + hasQueue: Boolean(disclosure.queuePolicy || disclosure.hpcQueuePolicy), + hasCarbon: Boolean(disclosure.carbonOrResourceNote) + }; +} + +function evaluateMemory(packet) { + const dataset = packet.dataset || {}; + const compute = computeRamGb(packet); + const findings = []; + const workingSetGb = datasetWorkingSetGb(packet); + const ramCapacity = compute.ramGb - DEFAULTS.minimumRamHeadroomGb; + + if (toNumber(dataset.sizeGb) === 0) { + return findings; + } + + if (compute.ramGb && workingSetGb > ramCapacity) { + findings.push( + createFinding( + "DATASET_MEMORY_EXCEEDS_NODE", + "critical", + "Dataset working set exceeds stated system memory", + `${workingSetGb} GB estimated working set vs ${compute.ramGb} GB RAM with ${DEFAULTS.minimumRamHeadroomGb} GB reserved headroom.`, + "Provide a smaller reviewer fixture, streaming pipeline proof, shard plan, or larger reproducible hardware profile." + ) + ); + } + + if (dataset.requiresGpuResidency && compute.totalGpuVramGb && workingSetGb > compute.totalGpuVramGb) { + findings.push( + createFinding( + "GPU_VRAM_FEASIBILITY_GAP", + "high", + "GPU-resident workload exceeds available VRAM", + `${workingSetGb} GB working set marked GPU-resident vs ${compute.totalGpuVramGb} GB total VRAM.`, + "Document gradient accumulation, checkpointing, offload, or a verified smaller fixture before release." + ) + ); + } + + return findings; +} + +function evaluateRuntime(packet) { + const compute = packet.compute || {}; + const findings = []; + const runtimeHours = toNumber(compute.estimatedRuntimeHours); + const budgetHours = toNumber(compute.reviewerBudgetHours, DEFAULTS.runtimeBudgetHours); + + if (runtimeHours > budgetHours) { + findings.push( + createFinding( + "RUNTIME_BUDGET_UNREALISTIC", + runtimeHours > budgetHours * 2 ? "high" : "medium", + "Reviewer rerun time exceeds the stated review budget", + `${runtimeHours} hour rerun estimate vs ${budgetHours} hour reviewer budget.`, + "Split the rerun into smoke, fixture, and full modes or disclose that full reproduction requires extended resources." + ) + ); + } + + if (runtimeHours >= DEFAULTS.longRunHours && !packet.reproducibility?.checkpointResumeEvidence) { + findings.push( + createFinding( + "CHECKPOINT_RESUME_MISSING", + "high", + "Long rerun lacks checkpoint or resume evidence", + `${runtimeHours} hour run has no checkpoint/resume evidence in the packet.`, + "Attach a checkpoint manifest, restart transcript, or resumable workflow proof." + ) + ); + } + + return findings; +} + +function evaluateEnvironment(packet) { + const findings = []; + + if (!hasHardwareSpec(packet)) { + findings.push( + createFinding( + "HARDWARE_SPEC_MISSING", + "high", + "Hardware profile is incomplete", + "The packet does not state enough CPU, RAM, accelerator, and VRAM information for reviewer rerun planning.", + "Publish a reviewer hardware profile with minimum and validated configurations." + ) + ); + } + + if (!hasContainerEvidence(packet)) { + findings.push( + createFinding( + "CONTAINER_OR_LOCKFILE_MISSING", + "high", + "Executable environment evidence is missing", + "No container digest, lockfile digest, runtime digest, or notebook environment digest was provided.", + "Attach a digest-pinned container, lockfile, runtime manifest, or archived notebook environment." + ) + ); + } + + return findings; +} + +function evaluateDeterminism(packet) { + const findings = []; + const compute = packet.compute || {}; + const reproducibility = packet.reproducibility || {}; + + if (compute.accelerator && compute.accelerator !== "none" && compute.accelerator !== "cpu") { + const controls = asArray(reproducibility.determinismControls).join(" ").toLowerCase(); + const mentionsNondeterminism = controls.includes("deterministic") || controls.includes("cudnn") || controls.includes("seed"); + if (!mentionsNondeterminism || !reproducibility.seedPolicy) { + findings.push( + createFinding( + "NONDETERMINISTIC_ACCELERATOR_PATH", + "high", + "Accelerator rerun lacks determinism controls", + `${compute.accelerator} path is present without seed policy plus deterministic kernel/runtime controls.`, + "Document seed handling, deterministic kernel settings, tolerance windows, and accepted variance." + ) + ); + } + } + + if (!hasDeterminismEvidence(packet)) { + findings.push( + createFinding( + "SEED_POLICY_MISSING", + "medium", + "Seed or variance policy is missing", + "The assistant packet has no seed policy and no deterministic control list.", + "Add seed policy, variance acceptance bands, and a rerun transcript showing stable outputs." + ) + ); + } + + return findings; +} + +function evaluateAccessAndDisclosure(packet) { + const findings = []; + const dataset = packet.dataset || {}; + const compute = packet.compute || {}; + const disclosure = resourceDisclosure(packet); + const downloadGb = toNumber(dataset.downloadGb, dataset.sizeGb); + + if (downloadGb >= DEFAULTS.largeDatasetGb && !dataset.syntheticFixtureAvailable) { + findings.push( + createFinding( + "DATA_ACCESS_BANDWIDTH_GAP", + "medium", + "Large data transfer lacks a reviewer fixture", + `${downloadGb} GB download is required and no synthetic or reduced reviewer fixture is listed.`, + "Provide a small fixture, cached digest, or staged data-access plan with expected transfer time." + ) + ); + } + + if ((compute.paidCloudRequired || compute.hpcQueueRequired) && (!disclosure.hasCost || disclosure.costUsd === 0)) { + findings.push( + createFinding( + "CLOUD_OR_HPC_COST_UNDISCLOSED", + "high", + "Paid cloud or queued HPC requirement is not disclosed", + "The packet requires paid cloud/HPC resources but does not disclose expected reviewer cost.", + "State cost, queue assumptions, no-cost fixture alternative, and who bears the rerun cost." + ) + ); + } + + if (disclosure.costUsd > DEFAULTS.expensiveCloudThresholdUsd && !packet.dataset?.syntheticFixtureAvailable) { + findings.push( + createFinding( + "NO_LOW_COST_REVIEWER_MODE", + "high", + "Expensive rerun lacks a low-cost review mode", + `$${disclosure.costUsd.toFixed(2)} estimated cost without a synthetic fixture or low-cost mode.`, + "Add an inexpensive smoke test and fixture-mode result before the AI assistant marks it reproducible." + ) + ); + } + + if (toNumber(packet.compute?.estimatedRuntimeHours) >= DEFAULTS.longRunHours && !disclosure.hasCarbon) { + findings.push( + createFinding( + "RESOURCE_IMPACT_NOTE_MISSING", + "low", + "Long compute rerun lacks resource-impact disclosure", + "No carbon/resource note is attached for a long-running rerun.", + "Add a short resource-impact note and reviewer-facing rerun alternatives." + ) + ); + } + + return findings; +} + +function decisionForFindings(findings) { + const worst = findings.reduce((max, finding) => Math.max(max, SEVERITY_RANK[finding.severity] || 0), 0); + const highOrWorse = findings.filter((finding) => SEVERITY_RANK[finding.severity] >= SEVERITY_RANK.high).length; + + if (worst >= SEVERITY_RANK.critical || highOrWorse >= 3) { + return DECISIONS.HOLD; + } + + if (worst >= SEVERITY_RANK.medium || findings.length > 0) { + return DECISIONS.REVISE; + } + + return DECISIONS.RELEASE; +} + +function scoreForFindings(findings) { + const penalty = findings.reduce((total, finding) => total + (SEVERITY_RANK[finding.severity] || 0) * 10, 0); + return Math.max(0, 100 - penalty); +} + +function summarizeReadiness(packet, findings) { + const workingSetGb = datasetWorkingSetGb(packet); + const compute = computeRamGb(packet); + return { + packetId: packet.id, + title: packet.title, + assistantOutput: packet.assistantOutput || "reproducibility-checker", + estimatedWorkingSetGb: workingSetGb, + ramGb: compute.ramGb, + totalGpuVramGb: compute.totalGpuVramGb, + findingCount: findings.length, + worstSeverity: + findings + .map((finding) => finding.severity) + .sort((a, b) => SEVERITY_RANK[b] - SEVERITY_RANK[a])[0] || "none" + }; +} + +export function analyzeRerunPacket(packet) { + const findings = [ + ...evaluateEnvironment(packet), + ...evaluateMemory(packet), + ...evaluateRuntime(packet), + ...evaluateDeterminism(packet), + ...evaluateAccessAndDisclosure(packet) + ]; + + const decision = decisionForFindings(findings); + const score = scoreForFindings(findings); + + return { + id: packet.id, + title: packet.title, + decision, + score, + summary: summarizeReadiness(packet, findings), + findings, + releaseGate: { + canReleaseAssistantOutput: decision === DECISIONS.RELEASE, + reviewerMessage: + decision === DECISIONS.RELEASE + ? "Compute evidence is sufficient for the assistant to present the rerun as reviewer-feasible." + : "Hold or revise the assistant's reproducibility wording until compute feasibility evidence is repaired." + } + }; +} + +export function analyzeRerunPackets(packets) { + const results = asArray(packets).map(analyzeRerunPacket); + const decisionCounts = results.reduce((counts, result) => { + counts[result.decision] = (counts[result.decision] || 0) + 1; + return counts; + }, {}); + + return { + generatedAt: new Date().toISOString(), + packetCount: results.length, + decisionCounts, + results + }; +} + +export { DECISIONS }; diff --git a/research-compute-rerun-feasibility-guard/test/compute-rerun-feasibility.test.js b/research-compute-rerun-feasibility-guard/test/compute-rerun-feasibility.test.js new file mode 100644 index 00000000..87e162f2 --- /dev/null +++ b/research-compute-rerun-feasibility-guard/test/compute-rerun-feasibility.test.js @@ -0,0 +1,49 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import assert from "node:assert/strict"; +import test from "node:test"; +import { DECISIONS, analyzeRerunPacket, analyzeRerunPackets } from "../src/index.js"; + +const fixtures = JSON.parse( + await readFile(join(process.cwd(), "data", "sample-rerun-packets.json"), "utf8") +); + +test("classifies synthetic rerun packets by release gate", () => { + const report = analyzeRerunPackets(fixtures); + assert.equal(report.packetCount, 4); + assert.equal(report.decisionCounts[DECISIONS.RELEASE], 1); + assert.equal(report.decisionCounts[DECISIONS.REVISE], 1); + assert.equal(report.decisionCounts[DECISIONS.HOLD], 2); +}); + +test("holds assistant output when memory and cloud disclosures are unsafe", () => { + const result = analyzeRerunPacket(fixtures.find((packet) => packet.id === "paper-gpu-overclaim-hold")); + assert.equal(result.decision, DECISIONS.HOLD); + assert.equal(result.releaseGate.canReleaseAssistantOutput, false); + assert(result.findings.some((finding) => finding.code === "DATASET_MEMORY_EXCEEDS_NODE")); + assert(result.findings.some((finding) => finding.code === "GPU_VRAM_FEASIBILITY_GAP")); + assert(result.findings.some((finding) => finding.code === "CLOUD_OR_HPC_COST_UNDISCLOSED")); +}); + +test("revises long queued runs when timing and resource notes are incomplete", () => { + const result = analyzeRerunPacket(fixtures.find((packet) => packet.id === "paper-hpc-revise")); + assert.equal(result.decision, DECISIONS.REVISE); + assert(result.findings.some((finding) => finding.code === "RUNTIME_BUDGET_UNREALISTIC")); + assert(result.findings.some((finding) => finding.code === "RESOURCE_IMPACT_NOTE_MISSING")); +}); + +test("releases assistant output for complete low-cost reviewer fixtures", () => { + const result = analyzeRerunPacket(fixtures.find((packet) => packet.id === "paper-cpu-fixture-pass")); + assert.equal(result.decision, DECISIONS.RELEASE); + assert.equal(result.findings.length, 0); + assert.equal(result.releaseGate.canReleaseAssistantOutput, true); + assert(result.score >= 90); +}); + +test("summaries include reviewer-relevant compute facts", () => { + const result = analyzeRerunPacket(fixtures.find((packet) => packet.id === "paper-vram-fixture-needed")); + assert.equal(result.decision, DECISIONS.HOLD); + assert.equal(result.summary.estimatedWorkingSetGb, 109.2); + assert.equal(result.summary.totalGpuVramGb, 40); + assert(result.summary.findingCount >= 3); +});