From a12a162a1e788c502ff0c9908d8f2db1f5b23b59 Mon Sep 17 00:00:00 2001 From: Axel Palumbo Date: Fri, 15 May 2026 18:15:18 +0200 Subject: [PATCH] Add research rebuttal response pack --- research-rebuttal-response-pack/README.md | 23 +++ .../data/sample-review-packet.json | 66 ++++++ research-rebuttal-response-pack/docs/demo.mp4 | Bin 0 -> 34325 bytes research-rebuttal-response-pack/docs/demo.svg | 22 ++ .../docs/requirement-map.md | 26 +++ research-rebuttal-response-pack/package.json | 12 ++ .../scripts/demo.js | 23 +++ .../src/rebuttal-response-pack.js | 191 ++++++++++++++++++ .../test/rebuttal-response-pack.test.js | 33 +++ 9 files changed, 396 insertions(+) create mode 100644 research-rebuttal-response-pack/README.md create mode 100644 research-rebuttal-response-pack/data/sample-review-packet.json create mode 100644 research-rebuttal-response-pack/docs/demo.mp4 create mode 100644 research-rebuttal-response-pack/docs/demo.svg create mode 100644 research-rebuttal-response-pack/docs/requirement-map.md create mode 100644 research-rebuttal-response-pack/package.json create mode 100644 research-rebuttal-response-pack/scripts/demo.js create mode 100644 research-rebuttal-response-pack/src/rebuttal-response-pack.js create mode 100644 research-rebuttal-response-pack/test/rebuttal-response-pack.test.js diff --git a/research-rebuttal-response-pack/README.md b/research-rebuttal-response-pack/README.md new file mode 100644 index 0000000..533fbc3 --- /dev/null +++ b/research-rebuttal-response-pack/README.md @@ -0,0 +1,23 @@ +# Research Rebuttal Response Pack + +Deterministic author rebuttal and revision planning for SCIBASE AI research assistants. + +The module ingests a manuscript review packet and produces: + +- a reviewer response matrix, +- concern clusters, +- evidence requests, +- revision tasks, +- unresolved-risk flags, +- resubmission readiness scoring, +- an editor-facing summary. + +It is dependency-free and uses synthetic local data for the demo. + +## Run + +```bash +npm run check +npm test +npm run demo +``` diff --git a/research-rebuttal-response-pack/data/sample-review-packet.json b/research-rebuttal-response-pack/data/sample-review-packet.json new file mode 100644 index 0000000..7fa998f --- /dev/null +++ b/research-rebuttal-response-pack/data/sample-review-packet.json @@ -0,0 +1,66 @@ +{ + "manuscript": { + "id": "ms-graph-retrieval-2026", + "title": "Graph Retrieval Signals Improve Literature Discovery", + "decision": "major-revision", + "claims": [ + { + "id": "c1", + "text": "The graph retrieval ranker improves recall for interdisciplinary literature discovery.", + "evidenceIds": ["ev-benchmark", "ev-ablation"] + }, + { + "id": "c2", + "text": "The method remains robust across biomedical and materials-science corpora.", + "evidenceIds": ["ev-benchmark"] + }, + { + "id": "c3", + "text": "The online prototype can be reproduced from the released scripts.", + "evidenceIds": ["ev-scripts"] + } + ], + "evidence": [ + { + "id": "ev-benchmark", + "kind": "benchmark", + "description": "Cross-domain retrieval benchmark with 12,000 labeled query-document pairs.", + "status": "available" + }, + { + "id": "ev-ablation", + "kind": "ablation", + "description": "Ablation table comparing graph, citation, and keyword signals.", + "status": "available" + }, + { + "id": "ev-scripts", + "kind": "code", + "description": "Prototype reproduction scripts without pinned dependency versions.", + "status": "partial" + } + ] + }, + "reviews": [ + { + "reviewer": "R1", + "severity": "major", + "text": "The core recall claim is interesting, but the manuscript does not show confidence intervals or a statistical test for the benchmark delta." + }, + { + "reviewer": "R2", + "severity": "major", + "text": "The materials-science evaluation appears much smaller than the biomedical corpus. Please explain whether the robustness claim is overgeneralized." + }, + { + "reviewer": "R2", + "severity": "minor", + "text": "The reproduction scripts do not pin package versions, so the prototype is not reliably reproducible." + }, + { + "reviewer": "R3", + "severity": "major", + "text": "The ablation table is useful, but it is unclear which graph features drive the largest performance gains." + } + ] +} diff --git a/research-rebuttal-response-pack/docs/demo.mp4 b/research-rebuttal-response-pack/docs/demo.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..b97b45655c01aa76e39ecee8bff25c6102d090d7 GIT binary patch literal 34325 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 literal 0 HcmV?d00001 diff --git a/research-rebuttal-response-pack/docs/demo.svg b/research-rebuttal-response-pack/docs/demo.svg new file mode 100644 index 0000000..2fe8b97 --- /dev/null +++ b/research-rebuttal-response-pack/docs/demo.svg @@ -0,0 +1,22 @@ + + + + Research rebuttal response pack + Reviewer findings become author actions, evidence requests, and editor-ready readiness signals. + + Review findings + statistical support + scope concern + reproducibility gap + + Response matrix + author strategy + revision tasks + evidence requests + + Readiness packet + risk blockers + editor summary + next action + Verification: npm run check, npm test, npm run demo + diff --git a/research-rebuttal-response-pack/docs/requirement-map.md b/research-rebuttal-response-pack/docs/requirement-map.md new file mode 100644 index 0000000..16aa898 --- /dev/null +++ b/research-rebuttal-response-pack/docs/requirement-map.md @@ -0,0 +1,26 @@ +# Requirement Map + +This slice targets SCIBASE issue #16 by adding a focused author rebuttal and revision response pack for the AI-powered research assistant workflow. + +## Covered capabilities + +- Auto peer-review reports: reviewer findings are converted into response rows with concern topics, response strategy, and editor-facing summaries. +- Reproducibility checks: partial code/dependency evidence creates high-priority evidence requests and unresolved-risk flags. +- Research gap discovery support: scope/generalization concerns create revision tasks that identify underpowered domains and missing evidence. +- Responsible AI assistant behavior: the module is deterministic, credential-free, and uses synthetic local data only. + +## Distinction from existing #16 slices + +- Not a broad assistant suite. +- Not a protocol trace or evidence-grounding packet. +- Not a reviewer calibration bench. +- Not a redline packet. +- Not a replication planner. + +The output is specifically an author-facing rebuttal matrix plus editor-facing resubmission readiness packet. + +## Verification + +- `npm run check` +- `npm test` +- `npm run demo` diff --git a/research-rebuttal-response-pack/package.json b/research-rebuttal-response-pack/package.json new file mode 100644 index 0000000..ab4c868 --- /dev/null +++ b/research-rebuttal-response-pack/package.json @@ -0,0 +1,12 @@ +{ + "name": "research-rebuttal-response-pack", + "version": "1.0.0", + "description": "Deterministic author rebuttal and revision response planning for SCIBASE AI research assistants.", + "type": "module", + "scripts": { + "check": "node --check src/rebuttal-response-pack.js && node --check scripts/demo.js", + "test": "node --test test/rebuttal-response-pack.test.js", + "demo": "node scripts/demo.js" + }, + "license": "MIT" +} diff --git a/research-rebuttal-response-pack/scripts/demo.js b/research-rebuttal-response-pack/scripts/demo.js new file mode 100644 index 0000000..8a81205 --- /dev/null +++ b/research-rebuttal-response-pack/scripts/demo.js @@ -0,0 +1,23 @@ +import samplePacket from "../data/sample-review-packet.json" with { type: "json" }; +import { buildRebuttalResponsePack } from "../src/rebuttal-response-pack.js"; + +const pack = buildRebuttalResponsePack(samplePacket); + +console.log(`Rebuttal response pack for: ${pack.title}`); +console.log(`Decision: ${pack.decision}`); +console.log(`Readiness: ${pack.resubmissionReadiness.status} (${pack.resubmissionReadiness.score}/100)`); +console.log("\nResponse matrix:"); +for (const row of pack.responseMatrix) { + console.log(`- ${row.id} [${row.severity}] ${row.concernTopic}`); + console.log(` claims: ${row.linkedClaimIds.join(", ") || "manual-link-required"}`); + console.log(` strategy: ${row.responseStrategy}`); + console.log(` tasks: ${row.revisionTasks.join(" | ")}`); +} + +console.log("\nConcern clusters:"); +for (const cluster of pack.concernClusters) { + console.log(`- ${cluster.topic}: ${cluster.responseIds.join(", ")} (${cluster.highestSeverity})`); +} + +console.log("\nEditor summary:"); +console.log(JSON.stringify(pack.editorSummary, null, 2)); diff --git a/research-rebuttal-response-pack/src/rebuttal-response-pack.js b/research-rebuttal-response-pack/src/rebuttal-response-pack.js new file mode 100644 index 0000000..18ad258 --- /dev/null +++ b/research-rebuttal-response-pack/src/rebuttal-response-pack.js @@ -0,0 +1,191 @@ +const TOPIC_RULES = [ + { + topic: "statistical support", + patterns: ["confidence", "statistical", "test", "significance", "delta"], + responseStrategy: "Add quantitative uncertainty support before defending the claim.", + revisionTemplate: "Add confidence intervals, test method, and benchmark delta interpretation." + }, + { + topic: "scope and generalization", + patterns: ["robust", "overgeneralized", "smaller", "corpus", "domain"], + responseStrategy: "Narrow the claim or add cross-domain evidence.", + revisionTemplate: "Clarify domain coverage, sample balance, and limits of generalization." + }, + { + topic: "reproducibility", + patterns: ["reproduc", "scripts", "package", "dependency", "version"], + responseStrategy: "Convert reproducibility concern into a concrete artifact fix.", + revisionTemplate: "Pin dependencies, publish run commands, and add environment checks." + }, + { + topic: "feature attribution", + patterns: ["ablation", "features", "drive", "unclear", "gains"], + responseStrategy: "Explain which mechanism supports the reported improvement.", + revisionTemplate: "Expand ablation narrative with ranked feature contributions." + } +]; + +const SEVERITY_WEIGHT = { + minor: 1, + moderate: 2, + major: 3, + critical: 4 +}; + +function normalizeText(value) { + return String(value || "").toLowerCase(); +} + +function classifyConcern(reviewText) { + const text = normalizeText(reviewText); + const scored = TOPIC_RULES.map((rule) => ({ + ...rule, + score: rule.patterns.filter((pattern) => text.includes(pattern)).length + })).sort((a, b) => b.score - a.score || a.topic.localeCompare(b.topic)); + + return scored[0].score > 0 ? scored[0] : { + topic: "editorial clarity", + responseStrategy: "Acknowledge the concern and make the affected claim easier to audit.", + revisionTemplate: "Add a targeted clarification and cite the supporting artifact." + }; +} + +function relevantClaims(manuscript, reviewText) { + const text = normalizeText(reviewText); + return manuscript.claims.filter((claim) => { + const claimTokens = normalizeText(claim.text).split(/[^a-z0-9]+/).filter((token) => token.length > 5); + return claimTokens.some((token) => text.includes(token)); + }); +} + +function evidenceById(manuscript) { + return new Map(manuscript.evidence.map((entry) => [entry.id, entry])); +} + +function evidenceRequests(manuscript, claims, concern) { + const evidenceMap = evidenceById(manuscript); + const requests = []; + + for (const claim of claims) { + const partialEvidence = claim.evidenceIds + .map((id) => evidenceMap.get(id)) + .filter((entry) => entry && entry.status !== "available"); + + if (partialEvidence.length > 0) { + requests.push({ + claimId: claim.id, + priority: "high", + request: `Complete ${partialEvidence.map((entry) => entry.kind).join(", ")} evidence for claim ${claim.id}.` + }); + } + } + + if (concern.topic === "statistical support") { + requests.push({ + claimId: claims[0]?.id || "manuscript", + priority: "high", + request: "Attach uncertainty intervals and statistical test output for the benchmark result." + }); + } + + if (concern.topic === "scope and generalization") { + requests.push({ + claimId: claims[0]?.id || "manuscript", + priority: "medium", + request: "Add domain-size table and revise wording where evidence is underpowered." + }); + } + + return requests; +} + +function buildResponseRow(manuscript, review, index) { + const concern = classifyConcern(review.text); + const claims = relevantClaims(manuscript, review.text); + const weight = SEVERITY_WEIGHT[review.severity] || 2; + const requests = evidenceRequests(manuscript, claims, concern); + const unresolvedRisk = weight >= 3 && requests.some((request) => request.priority === "high"); + + return { + id: `response-${index + 1}`, + reviewer: review.reviewer, + severity: review.severity, + concernTopic: concern.topic, + reviewerConcern: review.text, + linkedClaimIds: claims.map((claim) => claim.id), + responseStrategy: concern.responseStrategy, + authorResponseDraft: `We thank ${review.reviewer} for this ${concern.topic} concern. We will revise the manuscript by: ${concern.revisionTemplate}`, + revisionTasks: [ + concern.revisionTemplate, + ...requests.map((request) => request.request) + ], + evidenceRequests: requests, + unresolvedRisk, + riskScore: weight + requests.length + (unresolvedRisk ? 2 : 0) + }; +} + +function clusterRows(rows) { + const clusters = new Map(); + for (const row of rows) { + if (!clusters.has(row.concernTopic)) { + clusters.set(row.concernTopic, { + topic: row.concernTopic, + responseIds: [], + highestSeverity: row.severity, + sharedTasks: new Set() + }); + } + const cluster = clusters.get(row.concernTopic); + cluster.responseIds.push(row.id); + row.revisionTasks.forEach((task) => cluster.sharedTasks.add(task)); + if ((SEVERITY_WEIGHT[row.severity] || 0) > (SEVERITY_WEIGHT[cluster.highestSeverity] || 0)) { + cluster.highestSeverity = row.severity; + } + } + + return [...clusters.values()].map((cluster) => ({ + ...cluster, + sharedTasks: [...cluster.sharedTasks] + })); +} + +function readiness(rows) { + const totalRisk = rows.reduce((sum, row) => sum + row.riskScore, 0); + const unresolvedMajor = rows.filter((row) => row.unresolvedRisk).length; + const score = Math.max(0, 100 - totalRisk * 5 - unresolvedMajor * 10); + + return { + score, + status: score >= 80 ? "ready-with-minor-edits" : score >= 55 ? "major-revision-needed" : "not-ready", + blockers: rows + .filter((row) => row.unresolvedRisk) + .map((row) => `${row.id}: ${row.concernTopic}`) + }; +} + +export function buildRebuttalResponsePack(packet) { + if (!packet?.manuscript || !Array.isArray(packet.reviews)) { + throw new Error("A manuscript and review list are required."); + } + + const responseMatrix = packet.reviews.map((review, index) => buildResponseRow(packet.manuscript, review, index)); + const concernClusters = clusterRows(responseMatrix); + const resubmissionReadiness = readiness(responseMatrix); + + return { + manuscriptId: packet.manuscript.id, + title: packet.manuscript.title, + decision: packet.manuscript.decision, + responseMatrix, + concernClusters, + resubmissionReadiness, + editorSummary: { + responseCount: responseMatrix.length, + highRiskResponses: responseMatrix.filter((row) => row.unresolvedRisk).map((row) => row.id), + recommendedNextAction: resubmissionReadiness.status === "not-ready" + ? "Resolve high-risk evidence gaps before resubmission." + : "Prepare tracked-change manuscript and response letter." + } + }; +} diff --git a/research-rebuttal-response-pack/test/rebuttal-response-pack.test.js b/research-rebuttal-response-pack/test/rebuttal-response-pack.test.js new file mode 100644 index 0000000..ee14861 --- /dev/null +++ b/research-rebuttal-response-pack/test/rebuttal-response-pack.test.js @@ -0,0 +1,33 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; +import { buildRebuttalResponsePack } from "../src/rebuttal-response-pack.js"; +import samplePacket from "../data/sample-review-packet.json" with { type: "json" }; + +describe("buildRebuttalResponsePack", () => { + it("creates one response row per reviewer concern", () => { + const pack = buildRebuttalResponsePack(samplePacket); + assert.equal(pack.responseMatrix.length, samplePacket.reviews.length); + assert.equal(pack.manuscriptId, "ms-graph-retrieval-2026"); + }); + + it("classifies statistical and reproducibility concerns deterministically", () => { + const pack = buildRebuttalResponsePack(samplePacket); + const topics = pack.responseMatrix.map((row) => row.concernTopic); + assert.ok(topics.includes("statistical support")); + assert.ok(topics.includes("reproducibility")); + }); + + it("requests missing evidence for high-risk responses", () => { + const pack = buildRebuttalResponsePack(samplePacket); + const highRiskRows = pack.responseMatrix.filter((row) => row.unresolvedRisk); + assert.ok(highRiskRows.length >= 1); + assert.ok(highRiskRows.some((row) => row.evidenceRequests.length > 0)); + }); + + it("exports an editor-facing readiness summary", () => { + const pack = buildRebuttalResponsePack(samplePacket); + assert.match(pack.resubmissionReadiness.status, /revision|ready|not-ready/); + assert.equal(pack.editorSummary.responseCount, 4); + assert.ok(Array.isArray(pack.editorSummary.highRiskResponses)); + }); +});