From 5e4076a87609df4cffa1ea2fb57d54a01e51e9be Mon Sep 17 00:00:00 2001 From: Keon Kim Date: Sun, 31 May 2026 15:40:49 +0900 Subject: [PATCH] Add ORCID publication claim guard --- orcid-publication-claim-guard/README.md | 33 ++ orcid-publication-claim-guard/demo.js | 38 +++ orcid-publication-claim-guard/index.js | 292 ++++++++++++++++++ orcid-publication-claim-guard/render-video.js | 51 +++ .../reports/demo.mp4 | Bin 0 -> 48595 bytes .../orcid-publication-claim-review.json | 121 ++++++++ .../reports/orcid-publication-claim-review.md | 64 ++++ .../orcid-publication-claim-review.svg | 44 +++ orcid-publication-claim-guard/sample-data.js | 117 +++++++ orcid-publication-claim-guard/test.js | 77 +++++ 10 files changed, 837 insertions(+) create mode 100644 orcid-publication-claim-guard/README.md create mode 100644 orcid-publication-claim-guard/demo.js create mode 100644 orcid-publication-claim-guard/index.js create mode 100644 orcid-publication-claim-guard/render-video.js create mode 100644 orcid-publication-claim-guard/reports/demo.mp4 create mode 100644 orcid-publication-claim-guard/reports/orcid-publication-claim-review.json create mode 100644 orcid-publication-claim-guard/reports/orcid-publication-claim-review.md create mode 100644 orcid-publication-claim-guard/reports/orcid-publication-claim-review.svg create mode 100644 orcid-publication-claim-guard/sample-data.js create mode 100644 orcid-publication-claim-guard/test.js diff --git a/orcid-publication-claim-guard/README.md b/orcid-publication-claim-guard/README.md new file mode 100644 index 00000000..378e2f94 --- /dev/null +++ b/orcid-publication-claim-guard/README.md @@ -0,0 +1,33 @@ +# ORCID Publication Claim Guard + +Self-contained reviewer artifact for the User & Project Management bounty. + +This module checks whether publications imported from ORCID, DOI metadata, or +profile sync jobs should be attached to a researcher profile automatically, +held for human review, or quarantined as a likely wrong-person claim. + +## Scope + +- Match imported publications to a researcher profile by ORCID, normalized name, + affiliation, coauthor overlap, and DOI ownership. +- Detect ambiguous homonyms, DOI conflicts, retracted or withdrawn works, and + weak evidence claims before they affect profile activity or reputation metrics. +- Produce deterministic audit evidence for reviewer and institution records. +- Use synthetic data only. No identity-provider calls, credentials, live ORCID + records, private researcher data, or external APIs. + +## Commands + +```bash +node orcid-publication-claim-guard/test.js +node orcid-publication-claim-guard/demo.js +node orcid-publication-claim-guard/render-video.js +node --check orcid-publication-claim-guard/index.js +node --check orcid-publication-claim-guard/sample-data.js +node --check orcid-publication-claim-guard/test.js +node --check orcid-publication-claim-guard/demo.js +node --check orcid-publication-claim-guard/render-video.js +``` + +The demo writes reviewer-ready JSON, Markdown, SVG, and MP4 artifacts under +`orcid-publication-claim-guard/reports/`. diff --git a/orcid-publication-claim-guard/demo.js b/orcid-publication-claim-guard/demo.js new file mode 100644 index 00000000..dd44665f --- /dev/null +++ b/orcid-publication-claim-guard/demo.js @@ -0,0 +1,38 @@ +const fs = require("fs"); +const path = require("path"); +const { + evaluatePublicationClaims, + renderMarkdownReport, + renderSvgReport, +} = require("./index"); +const { + existingClaims, + importedPublications, + researchers, +} = require("./sample-data"); + +const reportDir = path.join(__dirname, "reports"); +fs.mkdirSync(reportDir, { recursive: true }); + +const report = evaluatePublicationClaims({ + researcher: researchers[0], + publications: importedPublications, + existingClaims, +}); + +fs.writeFileSync( + path.join(reportDir, "orcid-publication-claim-review.json"), + `${JSON.stringify(report, null, 2)}\n` +); +fs.writeFileSync( + path.join(reportDir, "orcid-publication-claim-review.md"), + `${renderMarkdownReport(report)}\n` +); +fs.writeFileSync( + path.join(reportDir, "orcid-publication-claim-review.svg"), + renderSvgReport(report) +); + +console.log( + `wrote ${report.summary.total} publication decisions to ${path.relative(process.cwd(), reportDir)}` +); diff --git a/orcid-publication-claim-guard/index.js b/orcid-publication-claim-guard/index.js new file mode 100644 index 00000000..202a050f --- /dev/null +++ b/orcid-publication-claim-guard/index.js @@ -0,0 +1,292 @@ +const crypto = require("crypto"); + +function normalizeText(value) { + return String(value || "") + .toLowerCase() + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/[^a-z0-9]+/g, " ") + .trim(); +} + +function normalizeDoi(value) { + return String(value || "") + .trim() + .toLowerCase() + .replace(/^https?:\/\/(dx\.)?doi\.org\//, ""); +} + +function tokenSet(value) { + return new Set(normalizeText(value).split(" ").filter(Boolean)); +} + +function overlapScore(left, right) { + const a = tokenSet(left); + const b = tokenSet(right); + if (a.size === 0 || b.size === 0) return 0; + + let overlap = 0; + for (const token of a) { + if (b.has(token)) overlap += 1; + } + return overlap / Math.max(a.size, b.size); +} + +function nameMatches(researcher, author) { + const names = [researcher.displayName, ...(researcher.aliases || [])]; + return names.some((name) => overlapScore(name, author.name) >= 0.8); +} + +function affiliationMatches(researcher, affiliation) { + return (researcher.affiliations || []).some( + (item) => overlapScore(item, affiliation) >= 0.55 + ); +} + +function coauthorMatches(researcher, publication) { + const known = new Set( + (researcher.knownCoauthors || []).map((name) => normalizeText(name)) + ); + return (publication.authors || []) + .map((author) => normalizeText(author.name)) + .filter((name) => known.has(name)); +} + +function findResearcherAuthor(researcher, publication) { + return (publication.authors || []).find((author) => { + if (author.orcid && researcher.orcid && author.orcid === researcher.orcid) { + return true; + } + return nameMatches(researcher, author); + }); +} + +function findConflictingOwner(researcher, publication, existingClaims) { + const doi = normalizeDoi(publication.doi); + return existingClaims.find( + (claim) => + normalizeDoi(claim.doi) === doi && claim.researcherId !== researcher.id + ); +} + +function scorePublicationClaim(researcher, publication, existingClaims = []) { + const evidence = []; + const blockers = []; + const actions = []; + let score = 0; + + const matchedAuthor = findResearcherAuthor(researcher, publication); + const conflictingOwner = findConflictingOwner( + researcher, + publication, + existingClaims + ); + + if (conflictingOwner) { + blockers.push( + `DOI already claimed by ${conflictingOwner.researcherId}: ${conflictingOwner.doi}` + ); + actions.push("quarantine duplicate DOI until the existing owner is reviewed"); + } + + if (publication.status && publication.status !== "published") { + blockers.push(`publication status is ${publication.status}`); + actions.push("hold non-published or retracted work out of profile metrics"); + } + + if (!matchedAuthor) { + blockers.push("no author row matches the researcher name or ORCID"); + actions.push("request manual attribution evidence before profile attach"); + } else { + if (matchedAuthor.orcid === researcher.orcid) { + score += 45; + evidence.push("exact ORCID match on an author row"); + } else if (matchedAuthor.orcid && matchedAuthor.orcid !== researcher.orcid) { + blockers.push( + `matching name has different ORCID ${matchedAuthor.orcid}` + ); + actions.push("treat as homonym until the alternate ORCID is reconciled"); + } else { + score += 18; + evidence.push("normalized author name matches profile name or alias"); + } + + if (affiliationMatches(researcher, matchedAuthor.affiliation)) { + score += 18; + evidence.push("author affiliation matches researcher profile"); + } else if (matchedAuthor.affiliation) { + blockers.push( + `author affiliation '${matchedAuthor.affiliation}' is not in profile` + ); + actions.push("ask researcher to confirm affiliation at publication time"); + } + } + + const coauthors = coauthorMatches(researcher, publication); + if (coauthors.length > 0) { + score += Math.min(18, coauthors.length * 9); + evidence.push(`known coauthor overlap: ${coauthors.join(", ")}`); + } + + if ( + publication.year < researcher.profileWindow.start || + publication.year > researcher.profileWindow.end + ) { + blockers.push(`publication year ${publication.year} is outside profile window`); + actions.push("verify historic affiliation before attaching"); + } else { + score += 8; + evidence.push("publication year falls inside profile window"); + } + + if (publication.doi) { + score += 6; + evidence.push(`normalized DOI: ${normalizeDoi(publication.doi)}`); + } + + let decision = "review"; + if (blockers.length > 0) { + decision = "quarantine"; + } else if (score >= 72) { + decision = "auto_accept"; + actions.push("attach to profile and include in reputation metrics"); + } else { + actions.push("hold for reviewer approval before affecting metrics"); + } + + const result = { + researcherId: researcher.id, + publicationId: publication.id, + title: publication.title, + doi: normalizeDoi(publication.doi), + decision, + score, + evidence, + blockers, + actions: [...new Set(actions)], + }; + + return { + ...result, + auditDigest: digestResult(result), + }; +} + +function digestResult(result) { + return crypto + .createHash("sha256") + .update(JSON.stringify(result)) + .digest("hex"); +} + +function evaluatePublicationClaims({ + researcher, + publications, + existingClaims = [], +}) { + const decisions = publications.map((publication) => + scorePublicationClaim(researcher, publication, existingClaims) + ); + const summary = decisions.reduce( + (acc, decision) => { + acc.total += 1; + acc[decision.decision] += 1; + return acc; + }, + { total: 0, auto_accept: 0, review: 0, quarantine: 0 } + ); + + return { + generatedAt: new Date().toISOString(), + researcher: { + id: researcher.id, + displayName: researcher.displayName, + orcid: researcher.orcid, + }, + summary, + decisions, + }; +} + +function renderMarkdownReport(report) { + const lines = [ + "# ORCID Publication Claim Guard Report", + "", + `Researcher: ${report.researcher.displayName} (${report.researcher.orcid})`, + `Generated: ${report.generatedAt}`, + "", + "## Summary", + "", + `- Total imports checked: ${report.summary.total}`, + `- Auto accepted: ${report.summary.auto_accept}`, + `- Needs review: ${report.summary.review}`, + `- Quarantined: ${report.summary.quarantine}`, + "", + "## Decisions", + "", + ]; + + for (const item of report.decisions) { + lines.push(`### ${item.publicationId} - ${item.decision}`); + lines.push(""); + lines.push(`- Title: ${item.title}`); + lines.push(`- DOI: ${item.doi}`); + lines.push(`- Score: ${item.score}`); + lines.push(`- Audit digest: ${item.auditDigest}`); + lines.push(`- Evidence: ${item.evidence.join("; ") || "none"}`); + lines.push(`- Blockers: ${item.blockers.join("; ") || "none"}`); + lines.push(`- Actions: ${item.actions.join("; ")}`); + lines.push(""); + } + + return lines.join("\n"); +} + +function escapeXml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); +} + +function renderSvgReport(report) { + const rows = report.decisions + .map((item, index) => { + const y = 190 + index * 86; + const color = + item.decision === "auto_accept" + ? "#059669" + : item.decision === "review" + ? "#d97706" + : "#dc2626"; + return ` + + + + ${escapeXml(item.publicationId)} - ${escapeXml(item.decision)} + ${escapeXml(item.title.slice(0, 104))} + score ${item.score} + `; + }) + .join(""); + + return ` + + + + ORCID Publication Claim Guard + ${escapeXml(report.researcher.displayName)} - ${escapeXml(report.researcher.orcid)} + Accepted ${report.summary.auto_accept} / Review ${report.summary.review} / Quarantine ${report.summary.quarantine} + ${rows} +`; +} + +module.exports = { + evaluatePublicationClaims, + normalizeDoi, + normalizeText, + renderMarkdownReport, + renderSvgReport, + scorePublicationClaim, +}; diff --git a/orcid-publication-claim-guard/render-video.js b/orcid-publication-claim-guard/render-video.js new file mode 100644 index 00000000..a8a9261e --- /dev/null +++ b/orcid-publication-claim-guard/render-video.js @@ -0,0 +1,51 @@ +const fs = require("fs"); +const path = require("path"); +const { execFileSync } = require("child_process"); + +const reportDir = path.join(__dirname, "reports"); +const jsonPath = path.join(reportDir, "orcid-publication-claim-review.json"); +const outputPath = path.join(reportDir, "demo.mp4"); +const svgPath = path.join(reportDir, "orcid-publication-claim-review.svg"); +const framePath = path.join(reportDir, "orcid-publication-claim-review.png"); + +if (!fs.existsSync(jsonPath)) { + require("./demo"); +} + +const report = JSON.parse(fs.readFileSync(jsonPath, "utf8")); +if (!fs.existsSync(svgPath)) { + throw new Error(`missing SVG report: ${svgPath}`); +} + +execFileSync( + "rsvg-convert", + ["--width", "1280", "--height", "720", "--output", framePath, svgPath], + { stdio: "inherit" } +); + +execFileSync( + "ffmpeg", + [ + "-y", + "-loop", + "1", + "-framerate", + "25", + "-i", + framePath, + "-t", + "4", + "-pix_fmt", + "yuv420p", + "-vf", + "scale=1280:720", + "-movflags", + "+faststart", + outputPath, + ], + { stdio: "inherit" } +); + +console.log( + `wrote ${path.relative(process.cwd(), outputPath)} for ${report.summary.total} checked imports` +); diff --git a/orcid-publication-claim-guard/reports/demo.mp4 b/orcid-publication-claim-guard/reports/demo.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..7cdf6c2e0e9464491d2cd514f7e041a1f8118159 GIT binary patch literal 48595 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 literal 0 HcmV?d00001 diff --git a/orcid-publication-claim-guard/reports/orcid-publication-claim-review.json b/orcid-publication-claim-guard/reports/orcid-publication-claim-review.json new file mode 100644 index 00000000..508ade8c --- /dev/null +++ b/orcid-publication-claim-guard/reports/orcid-publication-claim-review.json @@ -0,0 +1,121 @@ +{ + "generatedAt": "2026-05-31T06:40:07.465Z", + "researcher": { + "id": "researcher-ada-kim", + "displayName": "Ada Kim", + "orcid": "0000-0002-1825-0097" + }, + "summary": { + "total": 5, + "auto_accept": 1, + "review": 1, + "quarantine": 3 + }, + "decisions": [ + { + "researcherId": "researcher-ada-kim", + "publicationId": "pub-exact-orcid", + "title": "Reproducible notebooks for kinase pathway triage", + "doi": "10.5555/northbridge.2025.042", + "decision": "auto_accept", + "score": 86, + "evidence": [ + "exact ORCID match on an author row", + "author affiliation matches researcher profile", + "known coauthor overlap: mina patel", + "publication year falls inside profile window", + "normalized DOI: 10.5555/northbridge.2025.042" + ], + "blockers": [], + "actions": [ + "attach to profile and include in reputation metrics" + ], + "auditDigest": "fb4879a5f58f01e8166d49170546a7fffb452782cbb1cffe750f9dafe22ac106" + }, + { + "researcherId": "researcher-ada-kim", + "publicationId": "pub-homonym-orcid-mismatch", + "title": "Hydrogen electrolyzer scheduling in microgrids", + "doi": "10.7777/westlake.2026.014", + "decision": "quarantine", + "score": 14, + "evidence": [ + "publication year falls inside profile window", + "normalized DOI: 10.7777/westlake.2026.014" + ], + "blockers": [ + "matching name has different ORCID 0000-0003-4444-7788", + "author affiliation 'Westlake Energy Systems' is not in profile" + ], + "actions": [ + "treat as homonym until the alternate ORCID is reconciled", + "ask researcher to confirm affiliation at publication time" + ], + "auditDigest": "6091cef81158046c74cd975865b92a7c6e5bf2e79f75e432b0f36ad4901575bf" + }, + { + "researcherId": "researcher-ada-kim", + "publicationId": "pub-weak-but-reviewable", + "title": "Sparse pathway graphs for reproducibility audits", + "doi": "10.5555/northbridge.2026.081", + "decision": "review", + "score": 59, + "evidence": [ + "normalized author name matches profile name or alias", + "author affiliation matches researcher profile", + "known coauthor overlap: grace lee", + "publication year falls inside profile window", + "normalized DOI: 10.5555/northbridge.2026.081" + ], + "blockers": [], + "actions": [ + "hold for reviewer approval before affecting metrics" + ], + "auditDigest": "0da5f48db0ec6ce9f5a6f9abeefb7b2254df777d8bf85c7ede618790e83e30fb" + }, + { + "researcherId": "researcher-ada-kim", + "publicationId": "pub-duplicate-doi", + "title": "Grid-scale battery dispatch under volatile demand", + "doi": "10.7777/westlake.2024.009", + "decision": "quarantine", + "score": 32, + "evidence": [ + "normalized author name matches profile name or alias", + "publication year falls inside profile window", + "normalized DOI: 10.7777/westlake.2024.009" + ], + "blockers": [ + "DOI already claimed by researcher-ada-kim-energy: 10.7777/westlake.2024.009", + "author affiliation 'Westlake Energy Systems' is not in profile" + ], + "actions": [ + "quarantine duplicate DOI until the existing owner is reviewed", + "ask researcher to confirm affiliation at publication time" + ], + "auditDigest": "34a98c869c817ce327a67f538283d188787e86da2b21be6fd5d3c0fa20413799" + }, + { + "researcherId": "researcher-ada-kim", + "publicationId": "pub-retracted", + "title": "Automated profile scoring from unreconciled citations", + "doi": "10.9999/example.2022.100", + "decision": "quarantine", + "score": 59, + "evidence": [ + "normalized author name matches profile name or alias", + "author affiliation matches researcher profile", + "known coauthor overlap: omar singh", + "publication year falls inside profile window", + "normalized DOI: 10.9999/example.2022.100" + ], + "blockers": [ + "publication status is retracted" + ], + "actions": [ + "hold non-published or retracted work out of profile metrics" + ], + "auditDigest": "57b56560faf9b3e7319ac2ed2050123164d26c77aff9637167af98977355bfd3" + } + ] +} diff --git a/orcid-publication-claim-guard/reports/orcid-publication-claim-review.md b/orcid-publication-claim-guard/reports/orcid-publication-claim-review.md new file mode 100644 index 00000000..9e1c3d7f --- /dev/null +++ b/orcid-publication-claim-guard/reports/orcid-publication-claim-review.md @@ -0,0 +1,64 @@ +# ORCID Publication Claim Guard Report + +Researcher: Ada Kim (0000-0002-1825-0097) +Generated: 2026-05-31T06:40:07.465Z + +## Summary + +- Total imports checked: 5 +- Auto accepted: 1 +- Needs review: 1 +- Quarantined: 3 + +## Decisions + +### pub-exact-orcid - auto_accept + +- Title: Reproducible notebooks for kinase pathway triage +- DOI: 10.5555/northbridge.2025.042 +- Score: 86 +- Audit digest: fb4879a5f58f01e8166d49170546a7fffb452782cbb1cffe750f9dafe22ac106 +- Evidence: exact ORCID match on an author row; author affiliation matches researcher profile; known coauthor overlap: mina patel; publication year falls inside profile window; normalized DOI: 10.5555/northbridge.2025.042 +- Blockers: none +- Actions: attach to profile and include in reputation metrics + +### pub-homonym-orcid-mismatch - quarantine + +- Title: Hydrogen electrolyzer scheduling in microgrids +- DOI: 10.7777/westlake.2026.014 +- Score: 14 +- Audit digest: 6091cef81158046c74cd975865b92a7c6e5bf2e79f75e432b0f36ad4901575bf +- Evidence: publication year falls inside profile window; normalized DOI: 10.7777/westlake.2026.014 +- Blockers: matching name has different ORCID 0000-0003-4444-7788; author affiliation 'Westlake Energy Systems' is not in profile +- Actions: treat as homonym until the alternate ORCID is reconciled; ask researcher to confirm affiliation at publication time + +### pub-weak-but-reviewable - review + +- Title: Sparse pathway graphs for reproducibility audits +- DOI: 10.5555/northbridge.2026.081 +- Score: 59 +- Audit digest: 0da5f48db0ec6ce9f5a6f9abeefb7b2254df777d8bf85c7ede618790e83e30fb +- Evidence: normalized author name matches profile name or alias; author affiliation matches researcher profile; known coauthor overlap: grace lee; publication year falls inside profile window; normalized DOI: 10.5555/northbridge.2026.081 +- Blockers: none +- Actions: hold for reviewer approval before affecting metrics + +### pub-duplicate-doi - quarantine + +- Title: Grid-scale battery dispatch under volatile demand +- DOI: 10.7777/westlake.2024.009 +- Score: 32 +- Audit digest: 34a98c869c817ce327a67f538283d188787e86da2b21be6fd5d3c0fa20413799 +- Evidence: normalized author name matches profile name or alias; publication year falls inside profile window; normalized DOI: 10.7777/westlake.2024.009 +- Blockers: DOI already claimed by researcher-ada-kim-energy: 10.7777/westlake.2024.009; author affiliation 'Westlake Energy Systems' is not in profile +- Actions: quarantine duplicate DOI until the existing owner is reviewed; ask researcher to confirm affiliation at publication time + +### pub-retracted - quarantine + +- Title: Automated profile scoring from unreconciled citations +- DOI: 10.9999/example.2022.100 +- Score: 59 +- Audit digest: 57b56560faf9b3e7319ac2ed2050123164d26c77aff9637167af98977355bfd3 +- Evidence: normalized author name matches profile name or alias; author affiliation matches researcher profile; known coauthor overlap: omar singh; publication year falls inside profile window; normalized DOI: 10.9999/example.2022.100 +- Blockers: publication status is retracted +- Actions: hold non-published or retracted work out of profile metrics + diff --git a/orcid-publication-claim-guard/reports/orcid-publication-claim-review.svg b/orcid-publication-claim-guard/reports/orcid-publication-claim-review.svg new file mode 100644 index 00000000..caab1903 --- /dev/null +++ b/orcid-publication-claim-guard/reports/orcid-publication-claim-review.svg @@ -0,0 +1,44 @@ + + + + + ORCID Publication Claim Guard + Ada Kim - 0000-0002-1825-0097 + Accepted 1 / Review 1 / Quarantine 3 + + + + + pub-exact-orcid - auto_accept + Reproducible notebooks for kinase pathway triage + score 86 + + + + + pub-homonym-orcid-mismatch - quarantine + Hydrogen electrolyzer scheduling in microgrids + score 14 + + + + + pub-weak-but-reviewable - review + Sparse pathway graphs for reproducibility audits + score 59 + + + + + pub-duplicate-doi - quarantine + Grid-scale battery dispatch under volatile demand + score 32 + + + + + pub-retracted - quarantine + Automated profile scoring from unreconciled citations + score 59 + + \ No newline at end of file diff --git a/orcid-publication-claim-guard/sample-data.js b/orcid-publication-claim-guard/sample-data.js new file mode 100644 index 00000000..bb612a1d --- /dev/null +++ b/orcid-publication-claim-guard/sample-data.js @@ -0,0 +1,117 @@ +const researchers = [ + { + id: "researcher-ada-kim", + orcid: "0000-0002-1825-0097", + displayName: "Ada Kim", + aliases: ["A. Kim", "Ada M. Kim"], + affiliations: ["Northbridge Institute", "Northbridge Bioinformatics Lab"], + domains: ["northbridge.example"], + knownCoauthors: ["Mina Patel", "Omar Singh", "Grace Lee"], + profileWindow: { start: 2019, end: 2026 }, + }, + { + id: "researcher-ada-kim-energy", + orcid: "0000-0003-4444-7788", + displayName: "Ada Kim", + aliases: ["A. Kim"], + affiliations: ["Westlake Energy Systems"], + domains: ["westlake.example"], + knownCoauthors: ["Evan Stone", "Priya Raman"], + profileWindow: { start: 2021, end: 2026 }, + }, +]; + +const existingClaims = [ + { + doi: "10.5555/northbridge.2025.042", + researcherId: "researcher-ada-kim", + title: "Reproducible notebooks for kinase pathway triage", + }, + { + doi: "10.7777/westlake.2024.009", + researcherId: "researcher-ada-kim-energy", + title: "Grid-scale battery dispatch under volatile demand", + }, +]; + +const importedPublications = [ + { + id: "pub-exact-orcid", + title: "Reproducible notebooks for kinase pathway triage", + doi: "10.5555/northbridge.2025.042", + year: 2025, + venue: "Journal of Computational Biology", + status: "published", + source: "orcid-sync", + authors: [ + { + name: "Ada M. Kim", + orcid: "0000-0002-1825-0097", + affiliation: "Northbridge Bioinformatics Lab", + }, + { name: "Mina Patel", affiliation: "Northbridge Institute" }, + ], + }, + { + id: "pub-homonym-orcid-mismatch", + title: "Hydrogen electrolyzer scheduling in microgrids", + doi: "10.7777/westlake.2026.014", + year: 2026, + venue: "Energy Systems Letters", + status: "published", + source: "doi-crosswalk", + authors: [ + { + name: "Ada Kim", + orcid: "0000-0003-4444-7788", + affiliation: "Westlake Energy Systems", + }, + { name: "Evan Stone", affiliation: "Westlake Energy Systems" }, + ], + }, + { + id: "pub-weak-but-reviewable", + title: "Sparse pathway graphs for reproducibility audits", + doi: "10.5555/northbridge.2026.081", + year: 2026, + venue: "Open Methods Review", + status: "published", + source: "publisher-feed", + authors: [ + { name: "A. Kim", affiliation: "Northbridge Institute" }, + { name: "Grace Lee", affiliation: "Northbridge Institute" }, + ], + }, + { + id: "pub-duplicate-doi", + title: "Grid-scale battery dispatch under volatile demand", + doi: "10.7777/westlake.2024.009", + year: 2024, + venue: "Energy Systems Letters", + status: "published", + source: "publisher-feed", + authors: [ + { name: "Ada Kim", affiliation: "Westlake Energy Systems" }, + { name: "Priya Raman", affiliation: "Westlake Energy Systems" }, + ], + }, + { + id: "pub-retracted", + title: "Automated profile scoring from unreconciled citations", + doi: "10.9999/example.2022.100", + year: 2022, + venue: "Synthetic Research Metrics", + status: "retracted", + source: "citation-import", + authors: [ + { name: "Ada Kim", affiliation: "Northbridge Institute" }, + { name: "Omar Singh", affiliation: "Northbridge Institute" }, + ], + }, +]; + +module.exports = { + researchers, + existingClaims, + importedPublications, +}; diff --git a/orcid-publication-claim-guard/test.js b/orcid-publication-claim-guard/test.js new file mode 100644 index 00000000..e10f589d --- /dev/null +++ b/orcid-publication-claim-guard/test.js @@ -0,0 +1,77 @@ +const assert = require("assert"); +const { + evaluatePublicationClaims, + normalizeDoi, + normalizeText, + scorePublicationClaim, +} = require("./index"); +const { + existingClaims, + importedPublications, + researchers, +} = require("./sample-data"); + +const primary = researchers[0]; + +assert.strictEqual( + normalizeDoi("https://doi.org/10.5555/NORTHBRIDGE.2025.042"), + "10.5555/northbridge.2025.042" +); +assert.strictEqual(normalizeText("Ada M. Kim"), "ada m kim"); + +const exact = scorePublicationClaim( + primary, + importedPublications.find((item) => item.id === "pub-exact-orcid"), + existingClaims +); +assert.strictEqual(exact.decision, "auto_accept"); +assert.ok(exact.score >= 72); +assert.ok(exact.evidence.some((entry) => entry.includes("exact ORCID"))); + +const homonym = scorePublicationClaim( + primary, + importedPublications.find((item) => item.id === "pub-homonym-orcid-mismatch"), + existingClaims +); +assert.strictEqual(homonym.decision, "quarantine"); +assert.ok(homonym.blockers.some((entry) => entry.includes("different ORCID"))); + +const weak = scorePublicationClaim( + primary, + importedPublications.find((item) => item.id === "pub-weak-but-reviewable"), + existingClaims +); +assert.strictEqual(weak.decision, "review"); +assert.ok(weak.score > 30); +assert.ok(weak.actions.some((entry) => entry.includes("reviewer approval"))); + +const duplicate = scorePublicationClaim( + primary, + importedPublications.find((item) => item.id === "pub-duplicate-doi"), + existingClaims +); +assert.strictEqual(duplicate.decision, "quarantine"); +assert.ok(duplicate.blockers.some((entry) => entry.includes("already claimed"))); + +const retracted = scorePublicationClaim( + primary, + importedPublications.find((item) => item.id === "pub-retracted"), + existingClaims +); +assert.strictEqual(retracted.decision, "quarantine"); +assert.ok(retracted.actions.some((entry) => entry.includes("profile metrics"))); + +const report = evaluatePublicationClaims({ + researcher: primary, + publications: importedPublications, + existingClaims, +}); +assert.deepStrictEqual(report.summary, { + total: 5, + auto_accept: 1, + review: 1, + quarantine: 3, +}); +assert.strictEqual(report.decisions[0].auditDigest.length, 64); + +console.log("orcid publication claim guard tests passed");