^~FWcm`*QT#Vf}g#dbwk!?M{OQ3|2!S-ZPuAc
z@xL*zCoi|}I}l`n$UU>A<%L;UjyztT_cQ()iIBpX;;ayhIH>*XQJwq=%3|@vofE9m
zDSfOc(th&D&aCx~F@U)hHaF5l)e=rfNF;^-#m(XPnv=pYTr{D6qvX`(>RJsO;z6ia
zW3Wyl=a{zNl
zsI{t%LuatLVO+t?DlJy79fzxz@tl%EWt7T`-ki18>jEGx_VRvTC~L0DZ;JvfY_Qbp
zTM3x7zG?A!9v`Fnh}~Kt4>dkl-?Q*GKCWXF){t4yA#Qvg8>6~}cFa>h^6+-Y!I2R~
zH~kqKWl%aOcx344h)>~fzS->xxQ1e>t$(^Pd6XcXaBZwq}%pN8)1<;UcW=Bx&^OWcGo
zR>G=j3Y%OH1P(Cqj>)^(C6T=vCf
z8eihk{e9l<7el;1vf})#OHDVr{wBXeg>M6x>Q5;f^)y{Wv>q`$ykmpz74WzWB7^)F!44M+WYIrl6!1!2H2qS
zd|CA!(@d~_KG-bjzgU&7?H^3XrmTM1(CesG#H7
zorRf51-W`rt9-!|@iR#*=M(e!WU@bl-#bIDJ-u>q`ITyQXhy_hKmNJ3{cg6rguSZ#
za1BhtW|?n@qvO)uD$$WNsRe0a`D{%Gp{vKMMt@kc4ovgTd5r8ed~>tG^dI7JR$YUc
z#Kp+w#2QEHIq<9+Vf($mN1(l`L>S}4qZu)mu9EL!^<
zrvHQ?5UGR6BW#5wr=VJRc?slr2IEx6M?T1|`gK|Vnq3>>kZ5{N$qTkdbrD(Ad0+t>OzwBPjCyl~en5E!DkNPm1M!qMbQd
zr!9dFf(2edw=&llmv){N1vH@cdbMSYpw<~1$MnSas+YJ^ppeM$%mPJBJF+#W`{GVWuM
z!s1EoL+qhRJBXW=+L`dt@u7=Yr2^^Z+mG*bt^t86h+O4u?Uv>`uM)eiG$R(mOIiAv
zOPKs=K9=lVD3~1t|8Hbk&N>v`5rFECfRqmfV5u5^g-+Qr5B(&XOZCV8B4n(@zMCAs
zhPv)JWh=9u~yDF0C2zS#>TqK_KV5GrjaA
z?Z}u+(QgKYSN+Ad^=FOaR=V{QlDcXVC**-p9c-@cc1?kxSf5NiBJRI$-@v7Uci+{k
z7aq0MM0N@sG1iy{|BvkDu{n6@*PGT-Q%%&^jc+DIKn`jE%4RKrnCt%0|HPy6a-6UU
zwpD*cOr>_WgD*IlMC_q1vhW_I618cWsgt2`pK>8Ic=KYd(EBMCt}ALZ6W
z+x$p5@IDS%clhjpdMm=MH?Ra~*+l+vHM+rqNehr!VX2{8aT&3_o>!JV1LfkDjIP}E
z&@isIYwVjcabdH!;TgK_v2LzwwkxkWd|&{D3a;E?Nqx=p2Km&oYR3QrjksM4V0Zva
z&E7hVhBhy+h=gSx2HC|W^=GKkV`op7&0n-H7OX5W`(Od8U^>_Jw)c|u&R3>{e)qE4
ze`h)we)`Q#O!Vng`~BD
zey*O1E5sZSKam07D)#vQgKY+=I*8o&Th449;2PfE5{wh@mT(EvEp_dt?}fM<15sRD
zqJ8&_m!hu6M|oa7OfRl02FQ)E)DM5vV~F6l@a~ko6+6}65MOz~!9!y5KAeibHoZ&o
zvzWZx4DG&XktHI0dG>7WdEZ4?VF^U;>XsIFRpuX$mdXspj*o(!bZDTgFu`#pMVHM1
zpgCZv!;8R58iEgRTz-svGDxoa(%Kaz|
z)weE^|D#YiI1rVYLxadPZa;iN<>hdILK~Zf_@g2~()7)%=@2cL>iXw(PhozR@geCl
zr>%@HSl6_Id>QafEP%g^&^gZU52%%~S>k_fTaULa)v|SNvtR8r&X|5x|Fi~(vY)f<
zgAQC!nsaT29c6^gGTEdO+^j;7G*9RC&04&vO!xb^7xl?0G&QlIC#0?Pi>n|6odI9=
zJZ(WS?!xo-3`g}W#b=u9jDU*?HqU+gJU|q&8g?l}G8r=+bsFYA=0Dl*YjpTV%>r}a
z@NCG_;`OP$?H6QMBOxS-Hu1P51tsA0J-x=+
zmMK#v=Bcm5em$(4!DzZPB<=?L)b6&SW(ZpK5x8ec`fiM60p!33v0GcxYke&jtT(e-
zF!eHZvR{sA^zgG+#qUvo#HNXeV{Nst@(C=WIR~f?VzY|3_FcqULNy?Ad4#hl`Wi{&
zCRd1V4K5K(dK&gDXU?z6SY)@a?(1*oO;%jL#0NJRW)|Mn5hS{agWX8m`|+zN*Zdh0
H{ptS$jx1Uf
literal 0
HcmV?d00001
diff --git a/repository-release-note-claim-guard/reports/release-note-claim-report.md b/repository-release-note-claim-guard/reports/release-note-claim-report.md
new file mode 100644
index 00000000..b903dde9
--- /dev/null
+++ b/repository-release-note-claim-guard/reports/release-note-claim-report.md
@@ -0,0 +1,47 @@
+# Release Note Claim Evidence Review: preprint-v2.2.0
+
+Decision: **hold_repository_release**
+Audit digest: `sha256:8431cbaec2db5bd8`
+
+## Summary
+
+| Metric | Value |
+| --- | ---: |
+| Release notes reviewed | 3 |
+| Changed artifacts reviewed | 3 |
+| Evidence items reviewed | 3 |
+| Findings | 18 |
+| High or critical findings | 12 |
+
+## Findings
+
+| Severity | Code | Message | Action |
+| --- | --- | --- | --- |
+| critical | `NO_BREAKING_CHANGE_CONFLICT` | Release note rn-101 says there are no breaking changes, but 1 changed artifact(s) are marked breaking. | `remove_no_breaking_claim_or_document_breaking_change` |
+| high | `ALL_RESULTS_OVERCLAIM` | Release note rn-102 uses high-confidence wording without passed reproduction evidence. | `downgrade_claim_or_add_evidence` |
+| high | `BREAKING_CHANGE_NOT_DISCLOSED` | Breaking artifact code/export/public-api.js has no explicit breaking-change release note. | `document_breaking_change_in_release_notes` |
+| high | `CLAIM_EVIDENCE_NOT_PASSED` | Evidence ev-old-repro for release note rn-102 has status failed. | `replace_or_rerun_failed_evidence` |
+| high | `CLAIM_KIND_EVIDENCE_MISSING` | Release note rn-101 is a api claim but lacks passed test evidence. | `attach_required_claim_evidence` |
+| high | `CLAIM_KIND_EVIDENCE_MISSING` | Release note rn-101 is a api claim but lacks passed export evidence. | `attach_required_claim_evidence` |
+| high | `CLAIM_KIND_EVIDENCE_MISSING` | Release note rn-102 is a reproducibility claim but lacks passed reproduction evidence. | `attach_required_claim_evidence` |
+| high | `CLAIM_KIND_EVIDENCE_MISSING` | Release note rn-103 is a dataset claim but lacks passed checksum evidence. | `attach_required_claim_evidence` |
+| high | `CLAIMED_COMPONENT_WITHOUT_CHANGE` | Release note rn-103 names data/public/cohort-clean.csv, but the release packet has no matching changed artifact. | `remove_claim_or_attach_changed_artifact` |
+| high | `EXPORT_DIGEST_MISMATCH` | Export manifest digest for data/restricted/cohort-clean.csv does not match the changed artifact digest. | `update_export_manifest_digest` |
+| high | `REPRODUCIBILITY_OVERCLAIM` | Release note rn-102 uses high-confidence wording without passed reproduction evidence. | `downgrade_claim_or_add_evidence` |
+| high | `VALIDATION_OVERCLAIM` | Release note rn-102 uses high-confidence wording without passed test evidence. | `downgrade_claim_or_add_evidence` |
+| medium | `CHANGE_DIGEST_NOT_UPDATED` | Changed artifact results/figures/figure-5.svg does not carry a new post-release digest. | `record_post_release_digest` |
+| medium | `CHANGED_ARTIFACT_UNMENTIONED` | Changed artifact data/restricted/cohort-clean.csv is not represented in any public release note. | `add_release_note_or_mark_internal_only` |
+| medium | `CLAIM_EVIDENCE_STALE` | Evidence ev-old-repro for release note rn-102 is 93 days old. | `refresh_release_evidence` |
+| medium | `CLAIM_PATH_NOT_COVERED_BY_EVIDENCE` | Release note rn-102 cites results/figures/figure-5.svg, but no passed evidence covers that path. | `add_path_specific_release_evidence` |
+| medium | `CLAIM_PATH_NOT_COVERED_BY_EVIDENCE` | Release note rn-103 cites data/public/cohort-clean.csv, but no passed evidence covers that path. | `add_path_specific_release_evidence` |
+| medium | `EXPORT_MANIFEST_MISSING_RELEASE_NOTE` | Export manifest does not list public release note rn-103. | `add_release_note_to_export_manifest` |
+
+## Evidence Coverage
+
+| Release note | Claim type | Changed artifacts | Passed evidence | Required evidence |
+| --- | --- | ---: | ---: | --- |
+| rn-101 | api | 1 | 1 | test, export |
+| rn-102 | reproducibility | 1 | 0 | reproduction |
+| rn-103 | dataset | 0 | 1 | data-diff, checksum |
+
+Synthetic data only. No Git provider, DOI provider, CI system, private repository, payment account, credential, or external API is contacted.
diff --git a/repository-release-note-claim-guard/reports/risky-packet.json b/repository-release-note-claim-guard/reports/risky-packet.json
new file mode 100644
index 00000000..2699c40a
--- /dev/null
+++ b/repository-release-note-claim-guard/reports/risky-packet.json
@@ -0,0 +1,439 @@
+{
+ "input": {
+ "repositoryId": "scibase-demo-repo",
+ "versionTag": "preprint-v2.2.0",
+ "reviewedAt": "2026-06-01T12:00:00Z",
+ "policy": {
+ "maxEvidenceAgeDays": 30
+ },
+ "changeSet": [
+ {
+ "id": "chg-api-export",
+ "path": "code/export/public-api.js",
+ "component": "code",
+ "digestBefore": "sha256:aaaa",
+ "digestAfter": "sha256:bbbb",
+ "noteIds": [
+ "rn-101"
+ ],
+ "publicImpact": true,
+ "breaking": true
+ },
+ {
+ "id": "chg-sensitive-data",
+ "path": "data/restricted/cohort-clean.csv",
+ "component": "data",
+ "digestBefore": "sha256:cccc",
+ "digestAfter": "sha256:dddd",
+ "publicImpact": true
+ },
+ {
+ "id": "chg-figure",
+ "path": "results/figures/figure-5.svg",
+ "component": "results",
+ "digestBefore": "sha256:eeee",
+ "digestAfter": "sha256:eeee",
+ "noteIds": [
+ "rn-102"
+ ],
+ "publicImpact": true
+ }
+ ],
+ "releaseNotes": [
+ {
+ "id": "rn-101",
+ "claimType": "api",
+ "text": "Updated the public API export with no breaking changes.",
+ "componentPaths": [
+ "code/export/public-api.js"
+ ],
+ "evidenceIds": [
+ "ev-api-review"
+ ]
+ },
+ {
+ "id": "rn-102",
+ "claimType": "reproducibility",
+ "text": "All results are now fully reproducible and validated.",
+ "componentPaths": [
+ "results/figures/figure-5.svg"
+ ],
+ "evidenceIds": [
+ "ev-old-repro"
+ ]
+ },
+ {
+ "id": "rn-103",
+ "claimType": "dataset",
+ "text": "Released a cleaned cohort dataset for reviewers.",
+ "componentPaths": [
+ "data/public/cohort-clean.csv"
+ ],
+ "evidenceIds": [
+ "ev-dataset-diff"
+ ]
+ }
+ ],
+ "evidence": [
+ {
+ "id": "ev-api-review",
+ "kind": "review",
+ "status": "passed",
+ "producedAt": "2026-05-31T10:00:00Z",
+ "coversPaths": [
+ "code/export/public-api.js"
+ ]
+ },
+ {
+ "id": "ev-old-repro",
+ "kind": "reproduction",
+ "status": "failed",
+ "producedAt": "2026-03-01T10:00:00Z",
+ "coversPaths": [
+ "results/figures/figure-5.svg"
+ ]
+ },
+ {
+ "id": "ev-dataset-diff",
+ "kind": "data-diff",
+ "status": "passed",
+ "producedAt": "2026-05-31T12:00:00Z",
+ "coversPaths": [
+ "data/restricted/cohort-clean.csv"
+ ]
+ }
+ ],
+ "exportManifest": {
+ "versionTag": "preprint-v2.2.0",
+ "releaseNoteIds": [
+ "rn-101",
+ "rn-102"
+ ],
+ "componentDigests": {
+ "code/export/public-api.js": "sha256:bbbb",
+ "results/figures/figure-5.svg": "sha256:eeee"
+ }
+ }
+ },
+ "evaluation": {
+ "summary": {
+ "repositoryId": "scibase-demo-repo",
+ "versionTag": "preprint-v2.2.0",
+ "reviewedAt": "2026-06-01T12:00:00Z",
+ "decision": "hold_repository_release",
+ "topSeverity": "critical",
+ "releaseNotesReviewed": 3,
+ "changedArtifactsReviewed": 3,
+ "evidenceItemsReviewed": 3,
+ "findingCount": 18,
+ "highOrCriticalFindings": 12,
+ "auditDigest": "sha256:8431cbaec2db5bd8"
+ },
+ "evidenceCoverage": [
+ {
+ "noteId": "rn-101",
+ "claimType": "api",
+ "changedArtifacts": 1,
+ "evidenceIds": [
+ "ev-api-review"
+ ],
+ "passedEvidence": 1,
+ "requiredKinds": [
+ "test",
+ "export"
+ ]
+ },
+ {
+ "noteId": "rn-102",
+ "claimType": "reproducibility",
+ "changedArtifacts": 1,
+ "evidenceIds": [
+ "ev-old-repro"
+ ],
+ "passedEvidence": 0,
+ "requiredKinds": [
+ "reproduction"
+ ]
+ },
+ {
+ "noteId": "rn-103",
+ "claimType": "dataset",
+ "changedArtifacts": 0,
+ "evidenceIds": [
+ "ev-dataset-diff"
+ ],
+ "passedEvidence": 1,
+ "requiredKinds": [
+ "data-diff",
+ "checksum"
+ ]
+ }
+ ],
+ "findings": [
+ {
+ "severity": "critical",
+ "code": "NO_BREAKING_CHANGE_CONFLICT",
+ "message": "Release note rn-101 says there are no breaking changes, but 1 changed artifact(s) are marked breaking.",
+ "refs": [
+ "rn-101",
+ "chg-api-export"
+ ],
+ "action": "remove_no_breaking_claim_or_document_breaking_change"
+ },
+ {
+ "severity": "high",
+ "code": "ALL_RESULTS_OVERCLAIM",
+ "message": "Release note rn-102 uses high-confidence wording without passed reproduction evidence.",
+ "refs": [
+ "rn-102"
+ ],
+ "action": "downgrade_claim_or_add_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "BREAKING_CHANGE_NOT_DISCLOSED",
+ "message": "Breaking artifact code/export/public-api.js has no explicit breaking-change release note.",
+ "refs": [
+ "chg-api-export"
+ ],
+ "action": "document_breaking_change_in_release_notes"
+ },
+ {
+ "severity": "high",
+ "code": "CLAIM_EVIDENCE_NOT_PASSED",
+ "message": "Evidence ev-old-repro for release note rn-102 has status failed.",
+ "refs": [
+ "rn-102",
+ "ev-old-repro"
+ ],
+ "action": "replace_or_rerun_failed_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "CLAIM_KIND_EVIDENCE_MISSING",
+ "message": "Release note rn-101 is a api claim but lacks passed test evidence.",
+ "refs": [
+ "rn-101",
+ "test"
+ ],
+ "action": "attach_required_claim_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "CLAIM_KIND_EVIDENCE_MISSING",
+ "message": "Release note rn-101 is a api claim but lacks passed export evidence.",
+ "refs": [
+ "rn-101",
+ "export"
+ ],
+ "action": "attach_required_claim_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "CLAIM_KIND_EVIDENCE_MISSING",
+ "message": "Release note rn-102 is a reproducibility claim but lacks passed reproduction evidence.",
+ "refs": [
+ "rn-102",
+ "reproduction"
+ ],
+ "action": "attach_required_claim_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "CLAIM_KIND_EVIDENCE_MISSING",
+ "message": "Release note rn-103 is a dataset claim but lacks passed checksum evidence.",
+ "refs": [
+ "rn-103",
+ "checksum"
+ ],
+ "action": "attach_required_claim_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "CLAIMED_COMPONENT_WITHOUT_CHANGE",
+ "message": "Release note rn-103 names data/public/cohort-clean.csv, but the release packet has no matching changed artifact.",
+ "refs": [
+ "rn-103",
+ "data/public/cohort-clean.csv"
+ ],
+ "action": "remove_claim_or_attach_changed_artifact"
+ },
+ {
+ "severity": "high",
+ "code": "EXPORT_DIGEST_MISMATCH",
+ "message": "Export manifest digest for data/restricted/cohort-clean.csv does not match the changed artifact digest.",
+ "refs": [
+ "data/restricted/cohort-clean.csv"
+ ],
+ "action": "update_export_manifest_digest"
+ },
+ {
+ "severity": "high",
+ "code": "REPRODUCIBILITY_OVERCLAIM",
+ "message": "Release note rn-102 uses high-confidence wording without passed reproduction evidence.",
+ "refs": [
+ "rn-102"
+ ],
+ "action": "downgrade_claim_or_add_evidence"
+ },
+ {
+ "severity": "high",
+ "code": "VALIDATION_OVERCLAIM",
+ "message": "Release note rn-102 uses high-confidence wording without passed test evidence.",
+ "refs": [
+ "rn-102"
+ ],
+ "action": "downgrade_claim_or_add_evidence"
+ },
+ {
+ "severity": "medium",
+ "code": "CHANGE_DIGEST_NOT_UPDATED",
+ "message": "Changed artifact results/figures/figure-5.svg does not carry a new post-release digest.",
+ "refs": [
+ "chg-figure"
+ ],
+ "action": "record_post_release_digest"
+ },
+ {
+ "severity": "medium",
+ "code": "CHANGED_ARTIFACT_UNMENTIONED",
+ "message": "Changed artifact data/restricted/cohort-clean.csv is not represented in any public release note.",
+ "refs": [
+ "chg-sensitive-data"
+ ],
+ "action": "add_release_note_or_mark_internal_only"
+ },
+ {
+ "severity": "medium",
+ "code": "CLAIM_EVIDENCE_STALE",
+ "message": "Evidence ev-old-repro for release note rn-102 is 93 days old.",
+ "refs": [
+ "rn-102",
+ "ev-old-repro"
+ ],
+ "action": "refresh_release_evidence"
+ },
+ {
+ "severity": "medium",
+ "code": "CLAIM_PATH_NOT_COVERED_BY_EVIDENCE",
+ "message": "Release note rn-102 cites results/figures/figure-5.svg, but no passed evidence covers that path.",
+ "refs": [
+ "rn-102",
+ "results/figures/figure-5.svg"
+ ],
+ "action": "add_path_specific_release_evidence"
+ },
+ {
+ "severity": "medium",
+ "code": "CLAIM_PATH_NOT_COVERED_BY_EVIDENCE",
+ "message": "Release note rn-103 cites data/public/cohort-clean.csv, but no passed evidence covers that path.",
+ "refs": [
+ "rn-103",
+ "data/public/cohort-clean.csv"
+ ],
+ "action": "add_path_specific_release_evidence"
+ },
+ {
+ "severity": "medium",
+ "code": "EXPORT_MANIFEST_MISSING_RELEASE_NOTE",
+ "message": "Export manifest does not list public release note rn-103.",
+ "refs": [
+ "rn-103"
+ ],
+ "action": "add_release_note_to_export_manifest"
+ }
+ ],
+ "actions": [
+ {
+ "id": "remove_no_breaking_claim_or_document_breaking_change",
+ "severity": "critical",
+ "refs": [
+ "rn-101",
+ "chg-api-export"
+ ]
+ },
+ {
+ "id": "downgrade_claim_or_add_evidence",
+ "severity": "high",
+ "refs": [
+ "rn-102"
+ ]
+ },
+ {
+ "id": "document_breaking_change_in_release_notes",
+ "severity": "high",
+ "refs": [
+ "chg-api-export"
+ ]
+ },
+ {
+ "id": "replace_or_rerun_failed_evidence",
+ "severity": "high",
+ "refs": [
+ "rn-102",
+ "ev-old-repro"
+ ]
+ },
+ {
+ "id": "attach_required_claim_evidence",
+ "severity": "high",
+ "refs": [
+ "rn-101",
+ "test"
+ ]
+ },
+ {
+ "id": "remove_claim_or_attach_changed_artifact",
+ "severity": "high",
+ "refs": [
+ "rn-103",
+ "data/public/cohort-clean.csv"
+ ]
+ },
+ {
+ "id": "update_export_manifest_digest",
+ "severity": "high",
+ "refs": [
+ "data/restricted/cohort-clean.csv"
+ ]
+ },
+ {
+ "id": "record_post_release_digest",
+ "severity": "medium",
+ "refs": [
+ "chg-figure"
+ ]
+ },
+ {
+ "id": "add_release_note_or_mark_internal_only",
+ "severity": "medium",
+ "refs": [
+ "chg-sensitive-data"
+ ]
+ },
+ {
+ "id": "refresh_release_evidence",
+ "severity": "medium",
+ "refs": [
+ "rn-102",
+ "ev-old-repro"
+ ]
+ },
+ {
+ "id": "add_path_specific_release_evidence",
+ "severity": "medium",
+ "refs": [
+ "rn-102",
+ "results/figures/figure-5.svg"
+ ]
+ },
+ {
+ "id": "add_release_note_to_export_manifest",
+ "severity": "medium",
+ "refs": [
+ "rn-103"
+ ]
+ }
+ ]
+ }
+}
diff --git a/repository-release-note-claim-guard/reports/summary.svg b/repository-release-note-claim-guard/reports/summary.svg
new file mode 100644
index 00000000..8a889f36
--- /dev/null
+++ b/repository-release-note-claim-guard/reports/summary.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/repository-release-note-claim-guard/sample-data.js b/repository-release-note-claim-guard/sample-data.js
new file mode 100644
index 00000000..c2e9fd00
--- /dev/null
+++ b/repository-release-note-claim-guard/sample-data.js
@@ -0,0 +1,205 @@
+const cleanReleasePacket = {
+ repositoryId: "scibase-demo-repo",
+ versionTag: "preprint-v2.1.0",
+ reviewedAt: "2026-06-01T12:00:00Z",
+ policy: {
+ maxEvidenceAgeDays: 30
+ },
+ changeSet: [
+ {
+ id: "chg-analysis-script",
+ path: "code/analysis/model-fit.py",
+ component: "code",
+ digestBefore: "sha256:1111",
+ digestAfter: "sha256:2222",
+ noteIds: ["rn-001"],
+ publicImpact: true
+ },
+ {
+ id: "chg-results-figure",
+ path: "results/figures/figure-2.svg",
+ component: "results",
+ digestBefore: "sha256:3333",
+ digestAfter: "sha256:4444",
+ noteIds: ["rn-002"],
+ publicImpact: true
+ },
+ {
+ id: "chg-metadata",
+ path: "metadata.json",
+ component: "metadata",
+ digestBefore: "sha256:5555",
+ digestAfter: "sha256:6666",
+ noteIds: ["rn-003"],
+ publicImpact: true
+ }
+ ],
+ releaseNotes: [
+ {
+ id: "rn-001",
+ claimType: "code",
+ text: "Updated the mixed-effects model fitting script and validated it with repository tests.",
+ componentPaths: ["code/analysis/model-fit.py"],
+ evidenceIds: ["ev-tests-model"]
+ },
+ {
+ id: "rn-002",
+ claimType: "figure",
+ text: "Regenerated Figure 2 from the refreshed analysis output with matching checksums.",
+ componentPaths: ["results/figures/figure-2.svg"],
+ evidenceIds: ["ev-repro-figure", "ev-checksum-figure"]
+ },
+ {
+ id: "rn-003",
+ claimType: "metadata",
+ text: "Aligned repository metadata and export manifest with the preprint-v2.1.0 citation package.",
+ componentPaths: ["metadata.json"],
+ evidenceIds: ["ev-export-manifest", "ev-metadata-review"]
+ }
+ ],
+ evidence: [
+ {
+ id: "ev-tests-model",
+ kind: "test",
+ status: "passed",
+ command: "npm run repository-tests",
+ producedAt: "2026-05-31T15:00:00Z",
+ coversPaths: ["code/analysis/model-fit.py"]
+ },
+ {
+ id: "ev-repro-figure",
+ kind: "reproduction",
+ status: "passed",
+ command: "python code/analysis/model-fit.py --rebuild-figures",
+ producedAt: "2026-05-31T15:20:00Z",
+ coversPaths: ["code/analysis/model-fit.py", "results/figures/figure-2.svg"]
+ },
+ {
+ id: "ev-checksum-figure",
+ kind: "checksum",
+ status: "passed",
+ producedAt: "2026-05-31T15:25:00Z",
+ coversPaths: ["results/figures/figure-2.svg"]
+ },
+ {
+ id: "ev-export-manifest",
+ kind: "export",
+ status: "passed",
+ producedAt: "2026-05-31T16:00:00Z",
+ coversPaths: ["metadata.json", "code/analysis/model-fit.py", "results/figures/figure-2.svg"]
+ },
+ {
+ id: "ev-metadata-review",
+ kind: "metadata",
+ status: "passed",
+ producedAt: "2026-05-31T16:05:00Z",
+ coversPaths: ["metadata.json"]
+ }
+ ],
+ exportManifest: {
+ versionTag: "preprint-v2.1.0",
+ releaseNoteIds: ["rn-001", "rn-002", "rn-003"],
+ componentDigests: {
+ "code/analysis/model-fit.py": "sha256:2222",
+ "results/figures/figure-2.svg": "sha256:4444",
+ "metadata.json": "sha256:6666"
+ }
+ }
+};
+
+const riskyReleasePacket = {
+ repositoryId: "scibase-demo-repo",
+ versionTag: "preprint-v2.2.0",
+ reviewedAt: "2026-06-01T12:00:00Z",
+ policy: {
+ maxEvidenceAgeDays: 30
+ },
+ changeSet: [
+ {
+ id: "chg-api-export",
+ path: "code/export/public-api.js",
+ component: "code",
+ digestBefore: "sha256:aaaa",
+ digestAfter: "sha256:bbbb",
+ noteIds: ["rn-101"],
+ publicImpact: true,
+ breaking: true
+ },
+ {
+ id: "chg-sensitive-data",
+ path: "data/restricted/cohort-clean.csv",
+ component: "data",
+ digestBefore: "sha256:cccc",
+ digestAfter: "sha256:dddd",
+ publicImpact: true
+ },
+ {
+ id: "chg-figure",
+ path: "results/figures/figure-5.svg",
+ component: "results",
+ digestBefore: "sha256:eeee",
+ digestAfter: "sha256:eeee",
+ noteIds: ["rn-102"],
+ publicImpact: true
+ }
+ ],
+ releaseNotes: [
+ {
+ id: "rn-101",
+ claimType: "api",
+ text: "Updated the public API export with no breaking changes.",
+ componentPaths: ["code/export/public-api.js"],
+ evidenceIds: ["ev-api-review"]
+ },
+ {
+ id: "rn-102",
+ claimType: "reproducibility",
+ text: "All results are now fully reproducible and validated.",
+ componentPaths: ["results/figures/figure-5.svg"],
+ evidenceIds: ["ev-old-repro"]
+ },
+ {
+ id: "rn-103",
+ claimType: "dataset",
+ text: "Released a cleaned cohort dataset for reviewers.",
+ componentPaths: ["data/public/cohort-clean.csv"],
+ evidenceIds: ["ev-dataset-diff"]
+ }
+ ],
+ evidence: [
+ {
+ id: "ev-api-review",
+ kind: "review",
+ status: "passed",
+ producedAt: "2026-05-31T10:00:00Z",
+ coversPaths: ["code/export/public-api.js"]
+ },
+ {
+ id: "ev-old-repro",
+ kind: "reproduction",
+ status: "failed",
+ producedAt: "2026-03-01T10:00:00Z",
+ coversPaths: ["results/figures/figure-5.svg"]
+ },
+ {
+ id: "ev-dataset-diff",
+ kind: "data-diff",
+ status: "passed",
+ producedAt: "2026-05-31T12:00:00Z",
+ coversPaths: ["data/restricted/cohort-clean.csv"]
+ }
+ ],
+ exportManifest: {
+ versionTag: "preprint-v2.2.0",
+ releaseNoteIds: ["rn-101", "rn-102"],
+ componentDigests: {
+ "code/export/public-api.js": "sha256:bbbb",
+ "results/figures/figure-5.svg": "sha256:eeee"
+ }
+ }
+};
+
+module.exports = {
+ cleanReleasePacket,
+ riskyReleasePacket
+};
diff --git a/repository-release-note-claim-guard/test.js b/repository-release-note-claim-guard/test.js
new file mode 100644
index 00000000..bd6473bb
--- /dev/null
+++ b/repository-release-note-claim-guard/test.js
@@ -0,0 +1,54 @@
+const assert = require("node:assert/strict");
+const { evaluateReleasePacket, renderMarkdownReport, renderSvgSummary } = require("./index");
+const { cleanReleasePacket, riskyReleasePacket } = require("./sample-data");
+
+function clone(value) {
+ return JSON.parse(JSON.stringify(value));
+}
+
+function codes(evaluation) {
+ return new Set(evaluation.findings.map((finding) => finding.code));
+}
+
+const clean = evaluateReleasePacket(cleanReleasePacket);
+assert.equal(clean.summary.decision, "publish_release_notes");
+assert.equal(clean.findings.length, 0);
+assert.equal(clean.summary.highOrCriticalFindings, 0);
+
+const risky = evaluateReleasePacket(riskyReleasePacket);
+assert.equal(risky.summary.decision, "hold_repository_release");
+assert.equal(risky.summary.highOrCriticalFindings > 0, true);
+assert.equal(codes(risky).has("NO_BREAKING_CHANGE_CONFLICT"), true);
+assert.equal(codes(risky).has("BREAKING_CHANGE_NOT_DISCLOSED"), true);
+assert.equal(codes(risky).has("CLAIM_EVIDENCE_NOT_PASSED"), true);
+assert.equal(codes(risky).has("CLAIMED_COMPONENT_WITHOUT_CHANGE"), true);
+assert.equal(codes(risky).has("CHANGED_ARTIFACT_UNMENTIONED"), true);
+assert.equal(codes(risky).has("EXPORT_DIGEST_MISMATCH"), true);
+
+const stale = clone(cleanReleasePacket);
+stale.evidence[0].producedAt = "2025-12-01T00:00:00Z";
+const staleResult = evaluateReleasePacket(stale);
+assert.equal(staleResult.summary.decision, "revise_release_notes");
+assert.equal(codes(staleResult).has("CLAIM_EVIDENCE_STALE"), true);
+
+const missingRequiredKind = clone(cleanReleasePacket);
+missingRequiredKind.releaseNotes[0].claimType = "performance";
+const missingRequiredKindResult = evaluateReleasePacket(missingRequiredKind);
+assert.equal(missingRequiredKindResult.summary.decision, "hold_repository_release");
+assert.equal(codes(missingRequiredKindResult).has("CLAIM_KIND_EVIDENCE_MISSING"), true);
+
+const missingExport = clone(cleanReleasePacket);
+delete missingExport.exportManifest;
+const missingExportResult = evaluateReleasePacket(missingExport);
+assert.equal(missingExportResult.summary.decision, "hold_repository_release");
+assert.equal(codes(missingExportResult).has("EXPORT_MANIFEST_MISSING"), true);
+
+const markdown = renderMarkdownReport(riskyReleasePacket, risky);
+assert.match(markdown, /Release Note Claim Evidence Review/);
+assert.match(markdown, /hold_repository_release/);
+
+const svg = renderSvgSummary(risky);
+assert.match(svg, /