From 91826d0a80cbc1c07815098c2053c3efc303c6a9 Mon Sep 17 00:00:00 2001 From: taherd <183945978+taherdhanera@users.noreply.github.com> Date: Sat, 23 May 2026 00:02:35 +0530 Subject: [PATCH] Add citation context fit assistant --- citation-context-fit-assistant/README.md | 38 +++ citation-context-fit-assistant/demo-video.js | 173 ++++++++++++ citation-context-fit-assistant/demo.js | 18 ++ citation-context-fit-assistant/index.js | 262 ++++++++++++++++++ citation-context-fit-assistant/package.json | 14 + .../reports/demo.webm | Bin 0 -> 33557 bytes .../reports/reviewer-packet.md | 39 +++ .../reports/summary.json | 106 +++++++ .../reports/summary.svg | 16 ++ .../requirements-map.md | 17 ++ citation-context-fit-assistant/sample-data.js | 96 +++++++ citation-context-fit-assistant/test.js | 77 +++++ 12 files changed, 856 insertions(+) create mode 100644 citation-context-fit-assistant/README.md create mode 100644 citation-context-fit-assistant/demo-video.js create mode 100644 citation-context-fit-assistant/demo.js create mode 100644 citation-context-fit-assistant/index.js create mode 100644 citation-context-fit-assistant/package.json create mode 100644 citation-context-fit-assistant/reports/demo.webm create mode 100644 citation-context-fit-assistant/reports/reviewer-packet.md create mode 100644 citation-context-fit-assistant/reports/summary.json create mode 100644 citation-context-fit-assistant/reports/summary.svg create mode 100644 citation-context-fit-assistant/requirements-map.md create mode 100644 citation-context-fit-assistant/sample-data.js create mode 100644 citation-context-fit-assistant/test.js diff --git a/citation-context-fit-assistant/README.md b/citation-context-fit-assistant/README.md new file mode 100644 index 00000000..39505686 --- /dev/null +++ b/citation-context-fit-assistant/README.md @@ -0,0 +1,38 @@ +# Citation Context-Fit Assistant + +Self-contained AI-Assisted Research Tools slice for +`SCIBASE-AI/SCIBASE.AI#13`. + +The assistant validates citation recommendations before one-click insertion. It +checks whether each candidate supports, contradicts, only contextualizes, or is +irrelevant to the highlighted manuscript claim. It also checks citation intent +labels, evidence strength, field fit, stale evidence, and whether contradictory +citations include an explicit contrast note. + +This is intentionally separate from broad AI tool suites, evidence-grounded +summarizers, citation provenance, citation metadata integrity, citation style +normalization, citation diversity, citation retraction watch, methods +reproducibility, figure/table evidence, protocol deviation, novelty overlap, +manuscript similarity, ethics/data availability, statistical consistency, study +power, unit consistency, supplementary readiness, and biomethods provenance +slices. + +## Run + +```bash +npm run check +npm test +npm run demo +npm run demo:video +``` + +## Outputs + +- `reports/summary.json` +- `reports/reviewer-packet.md` +- `reports/summary.svg` +- `reports/demo.webm` + +All data is synthetic. The module does not call DOI registries, Crossref, +PubMed, arXiv, Semantic Scholar, publishers, external corpora, or live citation +insertion systems. diff --git a/citation-context-fit-assistant/demo-video.js b/citation-context-fit-assistant/demo-video.js new file mode 100644 index 00000000..c1234d7e --- /dev/null +++ b/citation-context-fit-assistant/demo-video.js @@ -0,0 +1,173 @@ +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const { execFileSync } = require("child_process"); + +const reportDir = path.join(__dirname, "reports"); +const outputPath = path.join(reportDir, "demo.webm"); + +const chromeCandidates = [ + process.env.CHROME_PATH, + "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", + "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", + "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe", + "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" +].filter(Boolean); + +function findBrowser() { + const found = chromeCandidates.find((candidate) => fs.existsSync(candidate)); + if (!found) { + throw new Error("Chrome or Edge was not found. Set CHROME_PATH to generate reports/demo.webm."); + } + return found; +} + +function fileUrl(filePath) { + return `file:///${filePath.replace(/\\/g, "/")}`; +} + +const html = String.raw` + + + + Citation context fit assistant demo + + + + +
recording
+ + +`; + +fs.mkdirSync(reportDir, { recursive: true }); + +const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "citation-context-fit-demo-")); +const htmlPath = path.join(tempDir, "demo.html"); +const profileDir = path.join(tempDir, "profile"); +fs.writeFileSync(htmlPath, html, "utf8"); + +const stdout = execFileSync( + findBrowser(), + [ + "--headless=new", + "--disable-gpu", + "--disable-dev-shm-usage", + "--autoplay-policy=no-user-gesture-required", + "--run-all-compositor-stages-before-draw", + "--virtual-time-budget=7500", + `--user-data-dir=${profileDir}`, + "--dump-dom", + fileUrl(htmlPath) + ], + { encoding: "utf8", maxBuffer: 30 * 1024 * 1024 } +); + +const match = stdout.match(/data:video\/webm;base64,([A-Za-z0-9+/=]+)/); +if (!match) { + throw new Error(`Demo video generation failed. Browser output ended with: ${stdout.slice(-600)}`); +} + +fs.writeFileSync(outputPath, Buffer.from(match[1], "base64")); +console.log(`Generated ${path.relative(process.cwd(), outputPath)}`); diff --git a/citation-context-fit-assistant/demo.js b/citation-context-fit-assistant/demo.js new file mode 100644 index 00000000..1d152ecd --- /dev/null +++ b/citation-context-fit-assistant/demo.js @@ -0,0 +1,18 @@ +const fs = require("fs"); +const path = require("path"); +const { project } = require("./sample-data"); +const { buildReviewPacket, renderMarkdownReport, renderSvgSummary } = require("./index"); + +const reportDir = path.join(__dirname, "reports"); +fs.mkdirSync(reportDir, { recursive: true }); + +const packet = buildReviewPacket(project); + +fs.writeFileSync(path.join(reportDir, "summary.json"), `${JSON.stringify(packet, null, 2)}\n`, "utf8"); +fs.writeFileSync(path.join(reportDir, "reviewer-packet.md"), renderMarkdownReport(packet), "utf8"); +fs.writeFileSync(path.join(reportDir, "summary.svg"), renderSvgSummary(packet), "utf8"); + +console.log(`Generated reports for ${packet.assistant}`); +console.log(`Decision: ${packet.decision}`); +console.log(`Score: ${packet.score}`); +console.log(`Findings: ${packet.findings.length}`); diff --git a/citation-context-fit-assistant/index.js b/citation-context-fit-assistant/index.js new file mode 100644 index 00000000..39a0c964 --- /dev/null +++ b/citation-context-fit-assistant/index.js @@ -0,0 +1,262 @@ +const SEVERITY_WEIGHTS = { + critical: 34, + high: 20, + medium: 10, + low: 4 +}; + +function addFinding(findings, severity, rule, message, action, refs = []) { + findings.push({ severity, rule, message, action, refs }); +} + +function yearsOld(asOfDate, year) { + return new Date(asOfDate).getUTCFullYear() - year; +} + +function claimById(project) { + return new Map(project.manuscript.highlightedClaims.map((claim) => [claim.id, claim])); +} + +function evaluateCandidate(project, claim, candidate, findings) { + if (!claim) { + addFinding( + findings, + "critical", + "citation-claim-anchor-missing", + `${candidate.id} references missing highlighted claim ${candidate.claimId}.`, + "Block insertion until the citation is attached to a valid manuscript claim.", + [candidate.id, candidate.claimId] + ); + return; + } + + if (project.policy.insertionRequiresIntent && !candidate.citationIntent) { + addFinding( + findings, + "high", + "citation-intent-missing", + `${candidate.id} has no citation intent label.`, + "Require the assistant to label the citation as direct support, background, method, or contrast.", + [candidate.id, claim.id] + ); + } + + if (candidate.citationIntent !== claim.intendedCitationRole) { + addFinding( + findings, + "medium", + "citation-intent-claim-role-mismatch", + `${candidate.id} intent ${candidate.citationIntent} does not match claim role ${claim.intendedCitationRole}.`, + "Ask the citation tool to relabel or move the candidate before one-click insertion.", + [candidate.id, claim.id] + ); + } + + if (candidate.relation === "irrelevant") { + addFinding( + findings, + "critical", + "citation-context-irrelevant", + `${candidate.id} is irrelevant to ${claim.id}.`, + "Suppress the recommendation from the citation tool.", + [candidate.id, claim.id] + ); + } + + if (candidate.relation === "contradicts" && claim.intendedCitationRole === "direct-support") { + addFinding( + findings, + "critical", + "contradictory-citation-for-supporting-claim", + `${candidate.id} contradicts a claim that requested direct support.`, + "Block one-click insertion unless the manuscript text is rewritten as a contrast or limitation.", + [candidate.id, claim.id] + ); + } + + if (candidate.relation === "contradicts" && project.policy.allowContradictoryOnlyWithNote && !candidate.insertionNote) { + addFinding( + findings, + "high", + "contradictory-citation-note-missing", + `${candidate.id} is contradictory but has no insertion note.`, + "Require an explicit contrast note before insertion.", + [candidate.id, claim.id] + ); + } + + if (candidate.citationIntent === "direct-support" && candidate.evidenceStrength < project.policy.minimumSupportStrength) { + addFinding( + findings, + "high", + "direct-support-evidence-too-weak", + `${candidate.id} evidence strength ${candidate.evidenceStrength} is below support threshold.`, + "Downgrade the candidate to background/context or require a stronger source.", + [candidate.id, claim.id] + ); + } + + if (candidate.fieldOverlap < project.policy.minimumFieldOverlap) { + addFinding( + findings, + "high", + "citation-field-fit-too-low", + `${candidate.id} field overlap ${candidate.fieldOverlap} is below the accepted threshold.`, + "Hold the candidate for manual review or retrieve a field-matched citation.", + [candidate.id, claim.id, candidate.field] + ); + } + + if (yearsOld(project.asOfDate, candidate.year) > project.policy.staleEvidenceYears && candidate.citationIntent !== "background") { + addFinding( + findings, + "medium", + "citation-evidence-stale-for-claim", + `${candidate.id} is ${yearsOld(project.asOfDate, candidate.year)} years old for a non-background claim.`, + "Ask the citation tool to retrieve fresher evidence or mark this as historical context.", + [candidate.id, String(candidate.year)] + ); + } + + if (candidate.relation === "contextualizes" && candidate.citationIntent === "direct-support") { + addFinding( + findings, + "high", + "context-only-citation-used-as-support", + `${candidate.id} only contextualizes ${claim.id} but is labeled direct support.`, + "Change the insertion label to background or select a direct evidence source.", + [candidate.id, claim.id] + ); + } +} + +function evaluateCitationFit(project) { + const findings = []; + const claims = claimById(project); + + for (const candidate of project.candidates) { + evaluateCandidate(project, claims.get(candidate.claimId), candidate, findings); + } + + const decisions = project.candidates.map((candidate) => { + const candidateFindings = findings.filter((finding) => finding.refs.includes(candidate.id)); + const hasCritical = candidateFindings.some((finding) => finding.severity === "critical"); + const hasHigh = candidateFindings.some((finding) => finding.severity === "high"); + return { + candidateId: candidate.id, + claimId: candidate.claimId, + decision: hasCritical ? "suppress" : hasHigh ? "manual-review" : "allow-insertion", + rules: candidateFindings.map((finding) => finding.rule) + }; + }); + + const severitySummary = findings.reduce( + (summary, finding) => { + summary[finding.severity] += 1; + return summary; + }, + { critical: 0, high: 0, medium: 0, low: 0 } + ); + const score = Math.max(0, 100 - findings.reduce((sum, finding) => sum + SEVERITY_WEIGHTS[finding.severity], 0)); + + return { findings, decisions, severitySummary, score }; +} + +function decisionFromEvaluation(evaluation) { + if (evaluation.severitySummary.critical > 0) { + return "block-unsafe-citation-insertions"; + } + if (evaluation.severitySummary.high > 0 || evaluation.score < 75) { + return "hold-citations-for-manual-review"; + } + if (evaluation.score < 90) { + return "review-citation-context-before-insertion"; + } + return "citation-context-fit-ready"; +} + +function buildReviewPacket(project) { + const evaluation = evaluateCitationFit(project); + return { + assistant: "citation-context-fit-assistant", + issue: "SCIBASE-AI/SCIBASE.AI#13", + manuscriptId: project.manuscript.id, + title: project.manuscript.title, + asOfDate: project.asOfDate, + decision: decisionFromEvaluation(evaluation), + score: evaluation.score, + severitySummary: evaluation.severitySummary, + findings: evaluation.findings, + insertionDecisions: evaluation.decisions, + safety: [ + "Synthetic manuscript claims and citation candidates only", + "No DOI, Crossref, PubMed, arXiv, Semantic Scholar, publisher, or external corpus calls", + "No private manuscripts, credentials, real literature metadata, or live citation insertions" + ] + }; +} + +function renderMarkdownReport(packet) { + const lines = [ + "# Citation Context-Fit Assistant", + "", + `Manuscript: ${packet.title}`, + `Issue: ${packet.issue}`, + `Decision: ${packet.decision}`, + `Score: ${packet.score}`, + "", + "## Insertion Decisions", + "" + ]; + + for (const decision of packet.insertionDecisions) { + lines.push(`- ${decision.candidateId} for ${decision.claimId}: ${decision.decision}`); + if (decision.rules.length > 0) { + lines.push(` - Rules: ${decision.rules.join(", ")}`); + } + } + + lines.push("", "## Findings", ""); + for (const finding of packet.findings) { + lines.push(`- **${finding.severity} / ${finding.rule}**: ${finding.message}`); + lines.push(` - Action: ${finding.action}`); + lines.push(` - Refs: ${finding.refs.join(", ") || "none"}`); + } + + lines.push("", "## Safety", ""); + for (const item of packet.safety) { + lines.push(`- ${item}`); + } + + return `${lines.join("\n")}\n`; +} + +function renderSvgSummary(packet) { + const scoreWidth = Math.max(44, Math.min(760, packet.score * 7.6)); + return ` + + Citation Context-Fit Assistant + ${packet.title} + + ${packet.decision} + Critical ${packet.severitySummary.critical} | High ${packet.severitySummary.high} | Findings ${packet.findings.length} + + Context-fit score + + + ${packet.score}/100 + + Block misleading one-click citations + Checks support, contradiction, context-only use, field fit, evidence strength, recency, and insertion notes. + +`; +} + +module.exports = { + buildReviewPacket, + decisionFromEvaluation, + evaluateCitationFit, + renderMarkdownReport, + renderSvgSummary, + yearsOld +}; diff --git a/citation-context-fit-assistant/package.json b/citation-context-fit-assistant/package.json new file mode 100644 index 00000000..00c19c69 --- /dev/null +++ b/citation-context-fit-assistant/package.json @@ -0,0 +1,14 @@ +{ + "name": "citation-context-fit-assistant", + "version": "1.0.0", + "description": "Deterministic assistant for citation recommendation context-fit checks.", + "main": "index.js", + "private": true, + "type": "commonjs", + "scripts": { + "check": "node --check index.js && node --check sample-data.js && node --check demo.js && node --check demo-video.js && node --check test.js", + "test": "node test.js", + "demo": "node demo.js", + "demo:video": "node demo-video.js" + } +} diff --git a/citation-context-fit-assistant/reports/demo.webm b/citation-context-fit-assistant/reports/demo.webm new file mode 100644 index 0000000000000000000000000000000000000000..31b1ae1d711e9f1594a924162a92a5f116a3fcbe GIT binary patch literal 33557 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 literal 0 HcmV?d00001 diff --git a/citation-context-fit-assistant/reports/reviewer-packet.md b/citation-context-fit-assistant/reports/reviewer-packet.md new file mode 100644 index 00000000..f434a458 --- /dev/null +++ b/citation-context-fit-assistant/reports/reviewer-packet.md @@ -0,0 +1,39 @@ +# Citation Context-Fit Assistant + +Manuscript: Synthetic Microbiome Intervention Review +Issue: SCIBASE-AI/SCIBASE.AI#13 +Decision: block-unsafe-citation-insertions +Score: 0 + +## Insertion Decisions + +- cand-001 for claim-001: allow-insertion +- cand-002 for claim-002: suppress + - Rules: contradictory-citation-for-supporting-claim, contradictory-citation-note-missing +- cand-003 for claim-003: allow-insertion +- cand-004 for claim-001: suppress + - Rules: citation-context-irrelevant, direct-support-evidence-too-weak, citation-field-fit-too-low + +## Findings + +- **critical / contradictory-citation-for-supporting-claim**: cand-002 contradicts a claim that requested direct support. + - Action: Block one-click insertion unless the manuscript text is rewritten as a contrast or limitation. + - Refs: cand-002, claim-002 +- **high / contradictory-citation-note-missing**: cand-002 is contradictory but has no insertion note. + - Action: Require an explicit contrast note before insertion. + - Refs: cand-002, claim-002 +- **critical / citation-context-irrelevant**: cand-004 is irrelevant to claim-001. + - Action: Suppress the recommendation from the citation tool. + - Refs: cand-004, claim-001 +- **high / direct-support-evidence-too-weak**: cand-004 evidence strength 0.3 is below support threshold. + - Action: Downgrade the candidate to background/context or require a stronger source. + - Refs: cand-004, claim-001 +- **high / citation-field-fit-too-low**: cand-004 field overlap 0.18 is below the accepted threshold. + - Action: Hold the candidate for manual review or retrieve a field-matched citation. + - Refs: cand-004, claim-001, environmental-microbiology + +## Safety + +- Synthetic manuscript claims and citation candidates only +- No DOI, Crossref, PubMed, arXiv, Semantic Scholar, publisher, or external corpus calls +- No private manuscripts, credentials, real literature metadata, or live citation insertions diff --git a/citation-context-fit-assistant/reports/summary.json b/citation-context-fit-assistant/reports/summary.json new file mode 100644 index 00000000..3b347125 --- /dev/null +++ b/citation-context-fit-assistant/reports/summary.json @@ -0,0 +1,106 @@ +{ + "assistant": "citation-context-fit-assistant", + "issue": "SCIBASE-AI/SCIBASE.AI#13", + "manuscriptId": "ms-ai-citation-013", + "title": "Synthetic Microbiome Intervention Review", + "asOfDate": "2026-05-22", + "decision": "block-unsafe-citation-insertions", + "score": 0, + "severitySummary": { + "critical": 2, + "high": 3, + "medium": 0, + "low": 0 + }, + "findings": [ + { + "severity": "critical", + "rule": "contradictory-citation-for-supporting-claim", + "message": "cand-002 contradicts a claim that requested direct support.", + "action": "Block one-click insertion unless the manuscript text is rewritten as a contrast or limitation.", + "refs": [ + "cand-002", + "claim-002" + ] + }, + { + "severity": "high", + "rule": "contradictory-citation-note-missing", + "message": "cand-002 is contradictory but has no insertion note.", + "action": "Require an explicit contrast note before insertion.", + "refs": [ + "cand-002", + "claim-002" + ] + }, + { + "severity": "critical", + "rule": "citation-context-irrelevant", + "message": "cand-004 is irrelevant to claim-001.", + "action": "Suppress the recommendation from the citation tool.", + "refs": [ + "cand-004", + "claim-001" + ] + }, + { + "severity": "high", + "rule": "direct-support-evidence-too-weak", + "message": "cand-004 evidence strength 0.3 is below support threshold.", + "action": "Downgrade the candidate to background/context or require a stronger source.", + "refs": [ + "cand-004", + "claim-001" + ] + }, + { + "severity": "high", + "rule": "citation-field-fit-too-low", + "message": "cand-004 field overlap 0.18 is below the accepted threshold.", + "action": "Hold the candidate for manual review or retrieve a field-matched citation.", + "refs": [ + "cand-004", + "claim-001", + "environmental-microbiology" + ] + } + ], + "insertionDecisions": [ + { + "candidateId": "cand-001", + "claimId": "claim-001", + "decision": "allow-insertion", + "rules": [] + }, + { + "candidateId": "cand-002", + "claimId": "claim-002", + "decision": "suppress", + "rules": [ + "contradictory-citation-for-supporting-claim", + "contradictory-citation-note-missing" + ] + }, + { + "candidateId": "cand-003", + "claimId": "claim-003", + "decision": "allow-insertion", + "rules": [] + }, + { + "candidateId": "cand-004", + "claimId": "claim-001", + "decision": "suppress", + "rules": [ + "citation-context-irrelevant", + "direct-support-evidence-too-weak", + "citation-field-fit-too-low" + ] + } + ], + "safety": [ + "Synthetic manuscript claims and citation candidates only", + "No DOI, Crossref, PubMed, arXiv, Semantic Scholar, publisher, or external corpus calls", + "No private manuscripts, credentials, real literature metadata, or live citation insertions" + ] +} diff --git a/citation-context-fit-assistant/reports/summary.svg b/citation-context-fit-assistant/reports/summary.svg new file mode 100644 index 00000000..fba237da --- /dev/null +++ b/citation-context-fit-assistant/reports/summary.svg @@ -0,0 +1,16 @@ + + + Citation Context-Fit Assistant + Synthetic Microbiome Intervention Review + + block-unsafe-citation-insertions + Critical 2 | High 3 | Findings 5 + + Context-fit score + + + 0/100 + + Block misleading one-click citations + Checks support, contradiction, context-only use, field fit, evidence strength, recency, and insertion notes. + diff --git a/citation-context-fit-assistant/requirements-map.md b/citation-context-fit-assistant/requirements-map.md new file mode 100644 index 00000000..1722b698 --- /dev/null +++ b/citation-context-fit-assistant/requirements-map.md @@ -0,0 +1,17 @@ +# Requirements Map + +Issue: `SCIBASE-AI/SCIBASE.AI#13` + +| Issue requirement | Implementation | +| --- | --- | +| AI citation tool | Validates recommended citations before one-click insertion into a manuscript. | +| Context-aware retrieval | Checks candidate relation to highlighted claims: supports, contradicts, contextualizes, or irrelevant. | +| Completeness and relevance | Scores evidence strength, field fit, stale evidence, and citation intent alignment. | +| Peer-review quality | Blocks misleading direct-support citations and requires notes for contradictory evidence. | +| Safe local validation | Includes dependency-free tests and demo generation from synthetic manuscript/citation metadata only. | + +## Non-goals + +- No live DOI, Crossref, PubMed, arXiv, Semantic Scholar, publisher, corpus, or citation database calls. +- No private manuscripts, credentials, real literature metadata, or live citation insertions. +- No replacement for citation metadata, style, diversity, retraction, provenance, similarity, ethics, unit, or biomethods workflows. diff --git a/citation-context-fit-assistant/sample-data.js b/citation-context-fit-assistant/sample-data.js new file mode 100644 index 00000000..dcc1eedc --- /dev/null +++ b/citation-context-fit-assistant/sample-data.js @@ -0,0 +1,96 @@ +const project = { + asOfDate: "2026-05-22", + policy: { + minimumSupportStrength: 0.72, + staleEvidenceYears: 8, + minimumFieldOverlap: 0.45, + insertionRequiresIntent: true, + allowContradictoryOnlyWithNote: true + }, + manuscript: { + id: "ms-ai-citation-013", + title: "Synthetic Microbiome Intervention Review", + highlightedClaims: [ + { + id: "claim-001", + text: "Short-course prebiotic treatment improves alpha diversity within two weeks.", + field: "microbiome", + claimType: "causal", + polarity: "positive", + intendedCitationRole: "direct-support" + }, + { + id: "claim-002", + text: "The workflow generalizes across pediatric and geriatric cohorts.", + field: "clinical-microbiome", + claimType: "generalization", + polarity: "positive", + intendedCitationRole: "direct-support" + }, + { + id: "claim-003", + text: "Older metagenomic pipelines remain useful as historical baselines.", + field: "metagenomics", + claimType: "background", + polarity: "neutral", + intendedCitationRole: "background" + } + ] + }, + candidates: [ + { + id: "cand-001", + claimId: "claim-001", + title: "Prebiotic Intervention Effects in Synthetic Adult Cohorts", + year: 2024, + field: "microbiome", + citationIntent: "direct-support", + relation: "supports", + evidenceStrength: 0.86, + fieldOverlap: 0.91, + polarity: "positive", + insertionNote: "Supports two-week alpha-diversity improvement in adult cohorts." + }, + { + id: "cand-002", + claimId: "claim-002", + title: "Pediatric Microbiome Response Limits", + year: 2021, + field: "clinical-microbiome", + citationIntent: "direct-support", + relation: "contradicts", + evidenceStrength: 0.81, + fieldOverlap: 0.74, + polarity: "negative", + insertionNote: "" + }, + { + id: "cand-003", + claimId: "claim-003", + title: "Early Metagenomic Pipeline Benchmarks", + year: 2012, + field: "metagenomics", + citationIntent: "background", + relation: "contextualizes", + evidenceStrength: 0.64, + fieldOverlap: 0.67, + polarity: "neutral", + insertionNote: "Historical baseline only." + }, + { + id: "cand-004", + claimId: "claim-001", + title: "Soil Microbial Bioreactor Survey", + year: 2025, + field: "environmental-microbiology", + citationIntent: "direct-support", + relation: "irrelevant", + evidenceStrength: 0.3, + fieldOverlap: 0.18, + polarity: "neutral", + insertionNote: "Related organism methods." + } + ] +}; + +module.exports = { project }; diff --git a/citation-context-fit-assistant/test.js b/citation-context-fit-assistant/test.js new file mode 100644 index 00000000..930606d9 --- /dev/null +++ b/citation-context-fit-assistant/test.js @@ -0,0 +1,77 @@ +const assert = require("assert"); +const { project } = require("./sample-data"); +const { buildReviewPacket, evaluateCitationFit, renderMarkdownReport, renderSvgSummary, yearsOld } = require("./index"); + +const evaluation = evaluateCitationFit(project); +const packet = buildReviewPacket(project); + +assert.strictEqual(packet.assistant, "citation-context-fit-assistant"); +assert.strictEqual(packet.issue, "SCIBASE-AI/SCIBASE.AI#13"); +assert.strictEqual(packet.decision, "block-unsafe-citation-insertions"); + +assert.ok( + evaluation.findings.some((finding) => finding.rule === "contradictory-citation-for-supporting-claim"), + "expected contradictory citation finding" +); +assert.ok( + evaluation.findings.some((finding) => finding.rule === "contradictory-citation-note-missing"), + "expected missing contrast note finding" +); +assert.ok( + evaluation.findings.some((finding) => finding.rule === "citation-context-irrelevant"), + "expected irrelevant citation finding" +); +assert.ok( + evaluation.findings.some((finding) => finding.rule === "citation-field-fit-too-low"), + "expected field fit finding" +); +assert.strictEqual(yearsOld("2026-05-22", 2012), 14); + +const decisionByCandidate = new Map(evaluation.decisions.map((decision) => [decision.candidateId, decision.decision])); +assert.strictEqual(decisionByCandidate.get("cand-001"), "allow-insertion"); +assert.strictEqual(decisionByCandidate.get("cand-002"), "suppress"); +assert.strictEqual(decisionByCandidate.get("cand-004"), "suppress"); + +const cleanProject = JSON.parse(JSON.stringify(project)); +cleanProject.candidates = [ + { + id: "cand-clean-1", + claimId: "claim-001", + title: "Prebiotic Intervention Effects in Synthetic Adult Cohorts", + year: 2024, + field: "microbiome", + citationIntent: "direct-support", + relation: "supports", + evidenceStrength: 0.9, + fieldOverlap: 0.95, + polarity: "positive", + insertionNote: "Directly supports two-week diversity improvement." + }, + { + id: "cand-clean-2", + claimId: "claim-003", + title: "Early Metagenomic Pipeline Benchmarks", + year: 2012, + field: "metagenomics", + citationIntent: "background", + relation: "contextualizes", + evidenceStrength: 0.64, + fieldOverlap: 0.67, + polarity: "neutral", + insertionNote: "Historical baseline only." + } +]; + +const cleanPacket = buildReviewPacket(cleanProject); +assert.strictEqual(cleanPacket.decision, "citation-context-fit-ready"); +assert.strictEqual(cleanPacket.findings.length, 0); + +const markdown = renderMarkdownReport(packet); +assert.ok(markdown.includes("## Insertion Decisions")); +assert.ok(markdown.includes("contradictory-citation-for-supporting-claim")); + +const svg = renderSvgSummary(packet); +assert.ok(svg.includes("