diff --git a/mentorship-impact-ladder/README.md b/mentorship-impact-ladder/README.md
new file mode 100644
index 0000000..2c1dfd2
--- /dev/null
+++ b/mentorship-impact-ladder/README.md
@@ -0,0 +1,28 @@
+# Mentorship Impact Ladder
+
+This module is a focused slice for SCIBASE.AI issue #15, Community & User Reputation System.
+
+It evaluates mentor/trainee reputation progression using supervised peer reviews, reproducibility reruns, dataset/code contributions, CRediT roles, independent endorsements, and conflict safeguards. The sample data is synthetic and dependency-free.
+
+## What It Covers
+
+- Mentorship-specific badge tiers such as emerging mentor, trusted mentor, and open science champion.
+- Trainee growth credit from supervised reviews, validation work, dataset curation, and project comments.
+- Mentor reputation deltas that require independent endorsements and reproducibility-linked evidence.
+- Conflict and nepotism safeguards before badge or leaderboard promotion.
+- Institution-ready mentorship packets with deterministic digests.
+- JSON audit packets, Markdown review packets, SVG summaries, and a short MP4 demo artifact.
+
+## Run
+
+```bash
+node mentorship-impact-ladder/test.js
+node mentorship-impact-ladder/demo.js
+```
+
+Demo output is written to:
+
+- `reports/mentorship-audit.json`
+- `reports/institution-packet.md`
+- `reports/mentorship-ladder.svg`
+- `reports/demo.mp4`
diff --git a/mentorship-impact-ladder/demo.js b/mentorship-impact-ladder/demo.js
new file mode 100644
index 0000000..420bbdf
--- /dev/null
+++ b/mentorship-impact-ladder/demo.js
@@ -0,0 +1,25 @@
+const fs = require("node:fs");
+const path = require("node:path");
+const data = require("./sample-data");
+const { renderMarkdown, renderSvg, runMentorshipAudit } = require("./index");
+
+const report = runMentorshipAudit(data);
+const reportsDir = path.join(__dirname, "reports");
+fs.mkdirSync(reportsDir, { recursive: true });
+fs.writeFileSync(path.join(reportsDir, "mentorship-audit.json"), JSON.stringify(report, null, 2));
+fs.writeFileSync(path.join(reportsDir, "institution-packet.md"), renderMarkdown(report));
+fs.writeFileSync(path.join(reportsDir, "mentorship-ladder.svg"), renderSvg(report));
+
+console.log(JSON.stringify({
+ averageScore: report.summary.averageScore,
+ badgeEligibleCount: report.summary.badgeEligibleCount,
+ moderatorReviewCount: report.summary.moderatorReviewCount,
+ totalFindings: report.summary.totalFindings,
+ auditDigest: report.auditDigest,
+ reports: [
+ "reports/mentorship-audit.json",
+ "reports/institution-packet.md",
+ "reports/mentorship-ladder.svg",
+ "reports/demo.mp4"
+ ]
+}, null, 2));
diff --git a/mentorship-impact-ladder/index.js b/mentorship-impact-ladder/index.js
new file mode 100644
index 0000000..c9b309a
--- /dev/null
+++ b/mentorship-impact-ladder/index.js
@@ -0,0 +1,265 @@
+const crypto = require("node:crypto");
+
+const ROLE_WEIGHTS = {
+ review: 8,
+ methodology: 9,
+ software: 9,
+ validation: 10,
+ "data-curation": 9,
+ supervision: 7
+};
+
+function stableStringify(value) {
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
+ if (value && typeof value === "object") {
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
+ }
+ return JSON.stringify(value);
+}
+
+function digest(value) {
+ return crypto.createHash("sha256").update(stableStringify(value)).digest("hex");
+}
+
+function byId(items) {
+ return new Map(items.map((item) => [item.id, item]));
+}
+
+function mean(values) {
+ if (!values.length) return 0;
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
+}
+
+function roleCoverageScore(evidenceItems) {
+ const roles = new Set(evidenceItems.flatMap((item) => item.creditedRoles || []));
+ const score = [...roles].reduce((total, role) => total + (ROLE_WEIGHTS[role] || 4), 0);
+ return Math.min(28, score);
+}
+
+function endorsementScore(evidenceItems) {
+ return Math.min(18, evidenceItems.filter((item) => item.independentEndorsement).length * 6);
+}
+
+function reproducibilityScore(evidenceItems) {
+ return Math.min(18, evidenceItems.filter((item) => item.reproducibilityLinked).length * 6);
+}
+
+function visibilityScore(evidenceItems) {
+ const publicCount = evidenceItems.filter((item) => item.publicMode === "public").length;
+ const semiPrivateCount = evidenceItems.filter((item) => item.publicMode === "semi-private").length;
+ return Math.min(12, publicCount * 4 + semiPrivateCount * 2);
+}
+
+function evidenceQualityScore(evidenceItems) {
+ return Math.round(mean(evidenceItems.map((item) => item.qualityScore)) * 0.24);
+}
+
+function detectSafeguards(pair, mentor, trainee, evidenceItems) {
+ const findings = [];
+ if (!pair.acknowledgements.includes(pair.traineeId)) {
+ findings.push({
+ severity: "high",
+ code: "trainee-acknowledgement-missing",
+ message: `${trainee.displayName} has not acknowledged mentorship credit for ${pair.id}.`,
+ task: "Hold badge promotion until the trainee acknowledges the mentorship packet."
+ });
+ }
+ if (mentor.conflicts.includes(pair.traineeId) || trainee.conflicts.includes(pair.mentorId) || pair.declaredRelationship === "family-member") {
+ findings.push({
+ severity: "high",
+ code: "relationship-conflict-review-required",
+ message: `${pair.id} has a declared relationship conflict that can bias reputation credit.`,
+ task: "Route this mentorship packet to an independent moderator before assigning badge credit."
+ });
+ }
+ if (!evidenceItems.some((item) => item.independentEndorsement)) {
+ findings.push({
+ severity: "medium",
+ code: "independent-endorsement-missing",
+ message: `${pair.id} has no independent endorsement evidence.`,
+ task: "Request an independent project owner or reviewer endorsement."
+ });
+ }
+ if (!evidenceItems.some((item) => item.reproducibilityLinked)) {
+ findings.push({
+ severity: "medium",
+ code: "reproducibility-evidence-missing",
+ message: `${pair.id} lacks reproducibility-linked mentorship evidence.`,
+ task: "Attach a rerun, validation, or artifact verification record."
+ });
+ }
+ if (new Set(evidenceItems.map((item) => item.projectId)).size < 2 && evidenceItems.length < 4) {
+ findings.push({
+ severity: "low",
+ code: "portfolio-breadth-low",
+ message: `${pair.id} has limited project breadth for a durable mentorship badge.`,
+ task: "Collect one more supervised contribution on a separate project."
+ });
+ }
+ return findings;
+}
+
+function tierFor(score, findings, ladder) {
+ if (findings.some((finding) => finding.code === "relationship-conflict-review-required")) return "moderator-review";
+ if (score >= ladder.thresholds.champion) return "open-science-champion";
+ if (score >= ladder.thresholds.trusted) return "trusted-mentor";
+ if (score >= ladder.thresholds.emerging) return "emerging-mentor";
+ return "not-yet-eligible";
+}
+
+function evaluatePair(pair, context) {
+ const mentor = context.members.get(pair.mentorId);
+ const trainee = context.members.get(pair.traineeId);
+ const evidenceItems = pair.evidenceIds.map((id) => context.evidence.get(id)).filter(Boolean);
+ const findings = detectSafeguards(pair, mentor, trainee, evidenceItems);
+ const score = Math.min(100, Math.round(
+ evidenceQualityScore(evidenceItems) +
+ roleCoverageScore(evidenceItems) +
+ endorsementScore(evidenceItems) +
+ reproducibilityScore(evidenceItems) +
+ visibilityScore(evidenceItems) +
+ mentor.reputation * 0.08
+ ));
+ const tier = tierFor(score, findings, context.ladder);
+ const traineeGrowth = Math.max(0, Math.round(score * 0.18 - findings.length * 2));
+ const mentorDelta = tier === "moderator-review" ? 0 : Math.round(score * 0.1);
+ return {
+ mentorshipId: pair.id,
+ mentor: { id: mentor.id, displayName: mentor.displayName },
+ trainee: { id: trainee.id, displayName: trainee.displayName },
+ score,
+ tier,
+ traineeGrowth,
+ mentorDelta,
+ evidenceCount: evidenceItems.length,
+ creditedRoles: [...new Set(evidenceItems.flatMap((item) => item.creditedRoles || []))].sort(),
+ findings,
+ packetDigest: digest({
+ pair,
+ evidenceItems,
+ score,
+ tier,
+ findings
+ })
+ };
+}
+
+function summarize(pairReports) {
+ const allFindings = pairReports.flatMap((report) => report.findings);
+ const countsByTier = pairReports.reduce((counts, report) => {
+ counts[report.tier] = (counts[report.tier] || 0) + 1;
+ return counts;
+ }, {});
+ const countsBySeverity = allFindings.reduce((counts, finding) => {
+ counts[finding.severity] = (counts[finding.severity] || 0) + 1;
+ return counts;
+ }, {});
+ return {
+ averageScore: Math.round(mean(pairReports.map((report) => report.score))),
+ badgeEligibleCount: pairReports.filter((report) => ["open-science-champion", "trusted-mentor", "emerging-mentor"].includes(report.tier)).length,
+ moderatorReviewCount: pairReports.filter((report) => report.tier === "moderator-review").length,
+ totalFindings: allFindings.length,
+ countsByTier,
+ countsBySeverity
+ };
+}
+
+function buildInstitutionPacket(pairReports) {
+ return pairReports.map((report) => ({
+ mentorshipId: report.mentorshipId,
+ mentorId: report.mentor.id,
+ traineeId: report.trainee.id,
+ tier: report.tier,
+ creditedRoles: report.creditedRoles,
+ traineeGrowth: report.traineeGrowth,
+ mentorDelta: report.mentorDelta,
+ packetDigest: report.packetDigest,
+ actions: report.findings.map((finding) => finding.task)
+ }));
+}
+
+function runMentorshipAudit(data) {
+ const context = {
+ members: byId(data.members),
+ evidence: byId(data.evidence),
+ ladder: data.community.ladders[0]
+ };
+ const pairReports = data.mentorships.map((pair) => evaluatePair(pair, context));
+ const packet = {
+ community: {
+ id: data.community.id,
+ domain: data.community.domain,
+ reviewPeriod: data.community.reviewPeriod
+ },
+ module: "mentorship-impact-ladder",
+ summary: summarize(pairReports),
+ pairReports,
+ institutionPacket: buildInstitutionPacket(pairReports)
+ };
+ return {
+ ...packet,
+ auditDigest: digest(packet)
+ };
+}
+
+function renderMarkdown(report) {
+ const lines = [
+ `# Mentorship Impact Ladder: ${report.community.domain}`,
+ "",
+ `Average score: **${report.summary.averageScore}**`,
+ `Badge eligible pairs: **${report.summary.badgeEligibleCount}**`,
+ `Moderator review pairs: **${report.summary.moderatorReviewCount}**`,
+ "",
+ "## Mentorship Packets"
+ ];
+ for (const pair of report.pairReports) {
+ lines.push("", `### ${pair.mentorshipId}: ${pair.mentor.displayName} -> ${pair.trainee.displayName}`);
+ lines.push(`Tier: **${pair.tier}**`);
+ lines.push(`Score: **${pair.score}**`);
+ lines.push(`Roles: ${pair.creditedRoles.join(", ") || "none"}`);
+ if (!pair.findings.length) {
+ lines.push("- No safeguards blocking badge progression.");
+ } else {
+ for (const finding of pair.findings) {
+ lines.push(`- [${finding.severity}] ${finding.message}`);
+ lines.push(` Task: ${finding.task}`);
+ }
+ }
+ }
+ lines.push("", `Audit digest: \`${report.auditDigest}\``);
+ return `${lines.join("\n")}\n`;
+}
+
+function renderSvg(report) {
+ const width = 960;
+ const rowHeight = 92;
+ const height = 158 + report.pairReports.length * rowHeight;
+ const rows = report.pairReports.map((pair, index) => {
+ const y = 142 + index * rowHeight;
+ const color = pair.tier === "moderator-review" ? "#c53030" : pair.score >= 88 ? "#2f855a" : pair.score >= 70 ? "#2b6cb0" : "#b7791f";
+ return [
+ `${pair.mentorshipId}`,
+ ``,
+ ``,
+ `${pair.tier} (${pair.score})`,
+ `${pair.mentor.displayName} -> ${pair.trainee.displayName} | ${pair.findings.length} finding(s)`
+ ].join("");
+ }).join("");
+ return [
+ ``
+ ].join("");
+}
+
+module.exports = {
+ runMentorshipAudit,
+ evaluatePair,
+ renderMarkdown,
+ renderSvg,
+ digest
+};
diff --git a/mentorship-impact-ladder/reports/demo.mp4 b/mentorship-impact-ladder/reports/demo.mp4
new file mode 100644
index 0000000..cd3e192
Binary files /dev/null and b/mentorship-impact-ladder/reports/demo.mp4 differ
diff --git a/mentorship-impact-ladder/reports/institution-packet.md b/mentorship-impact-ladder/reports/institution-packet.md
new file mode 100644
index 0000000..622128d
--- /dev/null
+++ b/mentorship-impact-ladder/reports/institution-packet.md
@@ -0,0 +1,39 @@
+# Mentorship Impact Ladder: computational-neuroscience
+
+Average score: **65**
+Badge eligible pairs: **2**
+Moderator review pairs: **1**
+
+## Mentorship Packets
+
+### pair-1: Dr. Mira Chen -> Lina Gomez
+Tier: **open-science-champion**
+Score: **100**
+Roles: data-curation, methodology, review, software, supervision, validation
+- No safeguards blocking badge progression.
+
+### pair-2: Dr. Omar Patel -> Kai Smith
+Tier: **emerging-mentor**
+Score: **52**
+Roles: review
+- [medium] pair-2 lacks reproducibility-linked mentorship evidence.
+ Task: Attach a rerun, validation, or artifact verification record.
+- [low] pair-2 has limited project breadth for a durable mentorship badge.
+ Task: Collect one more supervised contribution on a separate project.
+
+### pair-3: Dr. Mira Chen -> Noah Chen
+Tier: **moderator-review**
+Score: **43**
+Roles: review, supervision
+- [high] Noah Chen has not acknowledged mentorship credit for pair-3.
+ Task: Hold badge promotion until the trainee acknowledges the mentorship packet.
+- [high] pair-3 has a declared relationship conflict that can bias reputation credit.
+ Task: Route this mentorship packet to an independent moderator before assigning badge credit.
+- [medium] pair-3 has no independent endorsement evidence.
+ Task: Request an independent project owner or reviewer endorsement.
+- [medium] pair-3 lacks reproducibility-linked mentorship evidence.
+ Task: Attach a rerun, validation, or artifact verification record.
+- [low] pair-3 has limited project breadth for a durable mentorship badge.
+ Task: Collect one more supervised contribution on a separate project.
+
+Audit digest: `5e5b5aeb170d943b0ed87df8174939e9c115c5c71fffb203f341f24cfc5a3d91`
diff --git a/mentorship-impact-ladder/reports/mentorship-audit.json b/mentorship-impact-ladder/reports/mentorship-audit.json
new file mode 100644
index 0000000..b4253d4
--- /dev/null
+++ b/mentorship-impact-ladder/reports/mentorship-audit.json
@@ -0,0 +1,196 @@
+{
+ "community": {
+ "id": "community-neuro-tools",
+ "domain": "computational-neuroscience",
+ "reviewPeriod": "2026-Q2"
+ },
+ "module": "mentorship-impact-ladder",
+ "summary": {
+ "averageScore": 65,
+ "badgeEligibleCount": 2,
+ "moderatorReviewCount": 1,
+ "totalFindings": 7,
+ "countsByTier": {
+ "open-science-champion": 1,
+ "emerging-mentor": 1,
+ "moderator-review": 1
+ },
+ "countsBySeverity": {
+ "medium": 3,
+ "low": 2,
+ "high": 2
+ }
+ },
+ "pairReports": [
+ {
+ "mentorshipId": "pair-1",
+ "mentor": {
+ "id": "mentor-a",
+ "displayName": "Dr. Mira Chen"
+ },
+ "trainee": {
+ "id": "trainee-a",
+ "displayName": "Lina Gomez"
+ },
+ "score": 100,
+ "tier": "open-science-champion",
+ "traineeGrowth": 18,
+ "mentorDelta": 10,
+ "evidenceCount": 5,
+ "creditedRoles": [
+ "data-curation",
+ "methodology",
+ "review",
+ "software",
+ "supervision",
+ "validation"
+ ],
+ "findings": [],
+ "packetDigest": "5ca41608d183976afc9e1e4a782bcb228ac8560dae5f1c6356aba9abe498ecd5"
+ },
+ {
+ "mentorshipId": "pair-2",
+ "mentor": {
+ "id": "mentor-b",
+ "displayName": "Dr. Omar Patel"
+ },
+ "trainee": {
+ "id": "trainee-b",
+ "displayName": "Kai Smith"
+ },
+ "score": 52,
+ "tier": "emerging-mentor",
+ "traineeGrowth": 5,
+ "mentorDelta": 5,
+ "evidenceCount": 3,
+ "creditedRoles": [
+ "review"
+ ],
+ "findings": [
+ {
+ "severity": "medium",
+ "code": "reproducibility-evidence-missing",
+ "message": "pair-2 lacks reproducibility-linked mentorship evidence.",
+ "task": "Attach a rerun, validation, or artifact verification record."
+ },
+ {
+ "severity": "low",
+ "code": "portfolio-breadth-low",
+ "message": "pair-2 has limited project breadth for a durable mentorship badge.",
+ "task": "Collect one more supervised contribution on a separate project."
+ }
+ ],
+ "packetDigest": "0bfd6ac0d0f517b0495891b1c56b555c147ffb729d1cb9e6da910b9e85e28768"
+ },
+ {
+ "mentorshipId": "pair-3",
+ "mentor": {
+ "id": "mentor-a",
+ "displayName": "Dr. Mira Chen"
+ },
+ "trainee": {
+ "id": "trainee-c",
+ "displayName": "Noah Chen"
+ },
+ "score": 43,
+ "tier": "moderator-review",
+ "traineeGrowth": 0,
+ "mentorDelta": 0,
+ "evidenceCount": 2,
+ "creditedRoles": [
+ "review",
+ "supervision"
+ ],
+ "findings": [
+ {
+ "severity": "high",
+ "code": "trainee-acknowledgement-missing",
+ "message": "Noah Chen has not acknowledged mentorship credit for pair-3.",
+ "task": "Hold badge promotion until the trainee acknowledges the mentorship packet."
+ },
+ {
+ "severity": "high",
+ "code": "relationship-conflict-review-required",
+ "message": "pair-3 has a declared relationship conflict that can bias reputation credit.",
+ "task": "Route this mentorship packet to an independent moderator before assigning badge credit."
+ },
+ {
+ "severity": "medium",
+ "code": "independent-endorsement-missing",
+ "message": "pair-3 has no independent endorsement evidence.",
+ "task": "Request an independent project owner or reviewer endorsement."
+ },
+ {
+ "severity": "medium",
+ "code": "reproducibility-evidence-missing",
+ "message": "pair-3 lacks reproducibility-linked mentorship evidence.",
+ "task": "Attach a rerun, validation, or artifact verification record."
+ },
+ {
+ "severity": "low",
+ "code": "portfolio-breadth-low",
+ "message": "pair-3 has limited project breadth for a durable mentorship badge.",
+ "task": "Collect one more supervised contribution on a separate project."
+ }
+ ],
+ "packetDigest": "fba97b73e812afea2878a2284926d6c8bfeefe9aac3a88bee16a858d384ede97"
+ }
+ ],
+ "institutionPacket": [
+ {
+ "mentorshipId": "pair-1",
+ "mentorId": "mentor-a",
+ "traineeId": "trainee-a",
+ "tier": "open-science-champion",
+ "creditedRoles": [
+ "data-curation",
+ "methodology",
+ "review",
+ "software",
+ "supervision",
+ "validation"
+ ],
+ "traineeGrowth": 18,
+ "mentorDelta": 10,
+ "packetDigest": "5ca41608d183976afc9e1e4a782bcb228ac8560dae5f1c6356aba9abe498ecd5",
+ "actions": []
+ },
+ {
+ "mentorshipId": "pair-2",
+ "mentorId": "mentor-b",
+ "traineeId": "trainee-b",
+ "tier": "emerging-mentor",
+ "creditedRoles": [
+ "review"
+ ],
+ "traineeGrowth": 5,
+ "mentorDelta": 5,
+ "packetDigest": "0bfd6ac0d0f517b0495891b1c56b555c147ffb729d1cb9e6da910b9e85e28768",
+ "actions": [
+ "Attach a rerun, validation, or artifact verification record.",
+ "Collect one more supervised contribution on a separate project."
+ ]
+ },
+ {
+ "mentorshipId": "pair-3",
+ "mentorId": "mentor-a",
+ "traineeId": "trainee-c",
+ "tier": "moderator-review",
+ "creditedRoles": [
+ "review",
+ "supervision"
+ ],
+ "traineeGrowth": 0,
+ "mentorDelta": 0,
+ "packetDigest": "fba97b73e812afea2878a2284926d6c8bfeefe9aac3a88bee16a858d384ede97",
+ "actions": [
+ "Hold badge promotion until the trainee acknowledges the mentorship packet.",
+ "Route this mentorship packet to an independent moderator before assigning badge credit.",
+ "Request an independent project owner or reviewer endorsement.",
+ "Attach a rerun, validation, or artifact verification record.",
+ "Collect one more supervised contribution on a separate project."
+ ]
+ }
+ ],
+ "auditDigest": "5e5b5aeb170d943b0ed87df8174939e9c115c5c71fffb203f341f24cfc5a3d91"
+}
\ No newline at end of file
diff --git a/mentorship-impact-ladder/reports/mentorship-ladder.svg b/mentorship-impact-ladder/reports/mentorship-ladder.svg
new file mode 100644
index 0000000..a897379
--- /dev/null
+++ b/mentorship-impact-ladder/reports/mentorship-ladder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/mentorship-impact-ladder/requirements-map.md b/mentorship-impact-ladder/requirements-map.md
new file mode 100644
index 0000000..018fedc
--- /dev/null
+++ b/mentorship-impact-ladder/requirements-map.md
@@ -0,0 +1,17 @@
+# Requirements Map
+
+Source: SCIBASE.AI issue #15, Community & User Reputation System.
+
+| Requirement | Implementation |
+| --- | --- |
+| Peer reviews and comments | Supervised peer reviews and inline review comments are mentorship evidence inputs. |
+| Review history on profiles | Pair reports emit mentor and trainee progression deltas for profile timelines. |
+| Contributor credits | Evidence records carry CRediT-style roles such as review, validation, software, data-curation, and supervision. |
+| Visible credit on researcher profiles | `institutionPacket` provides profile-safe mentorship credit summaries and digests. |
+| CRediT taxonomy support | `creditedRoles` are normalized and scored through role weights. |
+| Transparent reputation metrics | Scores are built from quality, role coverage, endorsements, reproducibility, visibility, and mentor baseline reputation. |
+| Badges and incentive tiers | The ladder emits emerging mentor, trusted mentor, open science champion, and moderator-review tiers. |
+| Abuse resistance | Relationship conflicts, missing trainee acknowledgement, missing endorsements, and missing reproducibility evidence create moderator or repair tasks. |
+| Institutional reporting | Markdown packets and deterministic packet digests support promotion and mentorship reporting. |
+| Reviewer-facing artifacts | Demo emits JSON, Markdown, SVG, and MP4 artifacts under `reports/`. |
+| Local verification | `test.js` covers tier assignment, conflict safeguards, trainee acknowledgement, reproducibility requirements, digest stability, Markdown output, and SVG output. |
diff --git a/mentorship-impact-ladder/sample-data.js b/mentorship-impact-ladder/sample-data.js
new file mode 100644
index 0000000..5baf1c5
--- /dev/null
+++ b/mentorship-impact-ladder/sample-data.js
@@ -0,0 +1,207 @@
+const community = {
+ id: "community-neuro-tools",
+ domain: "computational-neuroscience",
+ reviewPeriod: "2026-Q2",
+ ladders: [
+ {
+ id: "open-science-mentor",
+ name: "Open Science Mentor",
+ thresholds: {
+ emerging: 45,
+ trusted: 70,
+ champion: 88
+ }
+ }
+ ]
+};
+
+const members = [
+ {
+ id: "mentor-a",
+ displayName: "Dr. Mira Chen",
+ role: "mentor",
+ institution: "Northlake Lab",
+ reputation: 82,
+ conflicts: ["trainee-c"],
+ priorBadge: "Trusted Reviewer"
+ },
+ {
+ id: "mentor-b",
+ displayName: "Dr. Omar Patel",
+ role: "mentor",
+ institution: "Cedar Institute",
+ reputation: 61,
+ conflicts: [],
+ priorBadge: null
+ },
+ {
+ id: "trainee-a",
+ displayName: "Lina Gomez",
+ role: "trainee",
+ institution: "Northlake Lab",
+ reputation: 34,
+ conflicts: [],
+ priorBadge: null
+ },
+ {
+ id: "trainee-b",
+ displayName: "Kai Smith",
+ role: "trainee",
+ institution: "Cedar Institute",
+ reputation: 41,
+ conflicts: [],
+ priorBadge: null
+ },
+ {
+ id: "trainee-c",
+ displayName: "Noah Chen",
+ role: "trainee",
+ institution: "Northlake Lab",
+ reputation: 29,
+ conflicts: ["mentor-a"],
+ priorBadge: null
+ }
+];
+
+const mentorships = [
+ {
+ id: "pair-1",
+ mentorId: "mentor-a",
+ traineeId: "trainee-a",
+ startDate: "2026-01-12",
+ declaredRelationship: "formal-lab-mentor",
+ goal: "Train reproducible notebook review and dataset curation",
+ evidenceIds: ["rev-1", "rerun-1", "dataset-1", "comment-1", "credit-1"],
+ acknowledgements: ["trainee-a", "mentor-a", "project-owner-7"]
+ },
+ {
+ id: "pair-2",
+ mentorId: "mentor-b",
+ traineeId: "trainee-b",
+ startDate: "2026-02-18",
+ declaredRelationship: "community-mentor",
+ goal: "Build first peer-review portfolio",
+ evidenceIds: ["rev-2", "comment-2", "credit-2"],
+ acknowledgements: ["trainee-b", "mentor-b"]
+ },
+ {
+ id: "pair-3",
+ mentorId: "mentor-a",
+ traineeId: "trainee-c",
+ startDate: "2026-03-04",
+ declaredRelationship: "family-member",
+ goal: "Accelerate challenge reputation tier",
+ evidenceIds: ["rev-3", "credit-3"],
+ acknowledgements: ["mentor-a"]
+ }
+];
+
+const evidence = [
+ {
+ id: "rev-1",
+ type: "supervised-peer-review",
+ projectId: "proj-brain-atlas",
+ qualityScore: 91,
+ publicMode: "semi-private",
+ creditedRoles: ["review", "methodology"],
+ independentEndorsement: true,
+ reproducibilityLinked: true
+ },
+ {
+ id: "rerun-1",
+ type: "reproducibility-rerun",
+ projectId: "proj-brain-atlas",
+ qualityScore: 88,
+ publicMode: "public",
+ creditedRoles: ["software", "validation"],
+ independentEndorsement: true,
+ reproducibilityLinked: true
+ },
+ {
+ id: "dataset-1",
+ type: "dataset-curation",
+ projectId: "proj-brain-atlas",
+ qualityScore: 84,
+ publicMode: "public",
+ creditedRoles: ["data-curation"],
+ independentEndorsement: true,
+ reproducibilityLinked: true
+ },
+ {
+ id: "comment-1",
+ type: "inline-review-comment",
+ projectId: "proj-brain-atlas",
+ qualityScore: 78,
+ publicMode: "semi-private",
+ creditedRoles: ["review"],
+ independentEndorsement: false,
+ reproducibilityLinked: false
+ },
+ {
+ id: "credit-1",
+ type: "credit-approval",
+ projectId: "proj-brain-atlas",
+ qualityScore: 95,
+ publicMode: "public",
+ creditedRoles: ["supervision", "validation", "data-curation"],
+ independentEndorsement: true,
+ reproducibilityLinked: true
+ },
+ {
+ id: "rev-2",
+ type: "supervised-peer-review",
+ projectId: "proj-microbiome",
+ qualityScore: 68,
+ publicMode: "semi-private",
+ creditedRoles: ["review"],
+ independentEndorsement: false,
+ reproducibilityLinked: false
+ },
+ {
+ id: "comment-2",
+ type: "inline-review-comment",
+ projectId: "proj-microbiome",
+ qualityScore: 71,
+ publicMode: "public",
+ creditedRoles: ["review"],
+ independentEndorsement: true,
+ reproducibilityLinked: false
+ },
+ {
+ id: "credit-2",
+ type: "credit-approval",
+ projectId: "proj-microbiome",
+ qualityScore: 74,
+ publicMode: "public",
+ creditedRoles: ["review"],
+ independentEndorsement: true,
+ reproducibilityLinked: false
+ },
+ {
+ id: "rev-3",
+ type: "supervised-peer-review",
+ projectId: "proj-private-challenge",
+ qualityScore: 82,
+ publicMode: "anonymous",
+ creditedRoles: ["review"],
+ independentEndorsement: false,
+ reproducibilityLinked: false
+ },
+ {
+ id: "credit-3",
+ type: "credit-approval",
+ projectId: "proj-private-challenge",
+ qualityScore: 90,
+ publicMode: "anonymous",
+ creditedRoles: ["supervision"],
+ independentEndorsement: false,
+ reproducibilityLinked: false
+ }
+];
+
+module.exports = {
+ community,
+ members,
+ mentorships,
+ evidence
+};
diff --git a/mentorship-impact-ladder/test.js b/mentorship-impact-ladder/test.js
new file mode 100644
index 0000000..581450b
--- /dev/null
+++ b/mentorship-impact-ladder/test.js
@@ -0,0 +1,33 @@
+const assert = require("node:assert/strict");
+const data = require("./sample-data");
+const { digest, renderMarkdown, renderSvg, runMentorshipAudit } = require("./index");
+
+const report = runMentorshipAudit(data);
+const byPair = new Map(report.pairReports.map((pair) => [pair.mentorshipId, pair]));
+
+assert.equal(report.module, "mentorship-impact-ladder");
+assert.equal(report.pairReports.length, 3);
+assert.match(report.auditDigest, /^[a-f0-9]{64}$/);
+
+assert.equal(byPair.get("pair-1").tier, "open-science-champion");
+assert.ok(byPair.get("pair-1").score >= 88);
+assert.equal(byPair.get("pair-1").findings.length, 0);
+assert.ok(byPair.get("pair-1").creditedRoles.includes("validation"));
+
+assert.equal(byPair.get("pair-2").tier, "emerging-mentor");
+assert.ok(byPair.get("pair-2").findings.some((finding) => finding.code === "reproducibility-evidence-missing"));
+
+assert.equal(byPair.get("pair-3").tier, "moderator-review");
+assert.ok(byPair.get("pair-3").findings.some((finding) => finding.code === "trainee-acknowledgement-missing"));
+assert.ok(byPair.get("pair-3").findings.some((finding) => finding.code === "relationship-conflict-review-required"));
+assert.equal(byPair.get("pair-3").mentorDelta, 0);
+
+assert.equal(report.summary.badgeEligibleCount, 2);
+assert.equal(report.summary.moderatorReviewCount, 1);
+assert.ok(report.institutionPacket.every((packet) => packet.packetDigest));
+
+assert.equal(digest({ b: 2, a: 1 }), digest({ a: 1, b: 2 }));
+assert.ok(renderMarkdown(report).includes("Mentorship Impact Ladder"));
+assert.ok(renderSvg(report).startsWith("