KyUGnIcs#li)*?2$HMCcsp;ntMwuthj#!wg
z2EUk;w>_;pnU|FdFT;1L#=3lKnL+6)w;fSZ@n!T1+1V-WhyCjUxouK6Ur}STUX6)G|9a@HtBu^!Z`wRo9U-XDJ6^|LD#<6?Ps9VR`kW
zy15bXA?hR)QMNvrT!2doL5uNPs6NDccTV&F+d2n
z@w}((rO^9&fg|lv&&k8FwRT|9Dp=YLeJ(j=;08v?z7^Y+{=2&TT>M3++G+x0Yb
z8j|CxZS;iK7>JbJ%Zp9Ne>@ifawr6?ZQXf-vvI;WJ!ng~@ZPbLb^~{5Sz`I1?-P0T1J@QGKMUeLZK2_h1ibz(jJ?}oX
z`s-6pOV_soa#_UTxao2iWBObjE0a`v-osK7CPrUTThsC){guyX)l2_{hZPsP7lrue
zZUxP%SH6KT(PM(<*WLnE7%c6JKJTVa9;fK+V~9~be>@I3cVlJ7awp@ckJ<7TdmRTd
zxtR(fi=?sCtv)fTsi7pXVBP2|06-Z*-@5J`A!ORX4MSfdw6{GmB=-)XB)g5#WlBjd
zpZfipw!6R~@UEQ2Aw^yCzyh&-2>SLl_b~mG;#MxBT=&jbDe&f#exDP%jr)jE3V#$Q
zv^B%waVC%9Bfx$No3mrXBb)Ii8uxuk$B?SWTO8g9^&0qMMKAWKqScV%TPHs3{nF{$
z+{1cE`ln;aFvg|~ulHKjL7y1WkShD=xb^tc)b^|%Gt<0pMPV;F(rOAhcTl!0R7A;9
zew#-AzWgYCM_aI&RP1&*W?Ha0%7$a?76c{H(e;!e4R%6cmAZS*e7we7H~TE9dv~3@
z%9c~e-D#RZ7Q}e$kqn#Fcy*thNCbsAHA?{4E
z+Q3%sSzw1XmVQiM#dtmT>5qTaXZ+yGNRzMHx$S&s4sOKT?PisxD6;v4Mf;+!xM>de
zj2L8C&=zffQToC>Ce*jWQjs^eG*Kl89Tc7ac;skm3tdbMQhp1N4_vdA?kASk3$V+nci3flHrPGg;uzd>+`Gz>O@lc}})0DW&U6=dVgvcv$Y~kdg{C>v%rNb%*@m8}=cf0rVvy{-7w$k*T
zDi|o`>2*kS4RJO{s69SSul#ylt!08;3(7H}UcSb7>r9*Q7&Fb(kc30X?32&<6n
zYIqm~89DqrZH!pt&is5e^<>COy{$pxok&7;vo21p?6!3;mA4YeV-WPJbp!j!^EY4K
zJVS(Rcm)=)Ze2W()KYCHyD#19`FXZ;A|C5v_lP88I~(}@&(|fsnCvg%Jk_nl?|yzER{b-5OA(0`
z!RJFfFQk$C7Z^f6ByKMKviO>K`bNuU4a(ZGOQtzSl>({Qv
zivGSW^zY?gsCeD_=$Z4HqdQu0Og(N*k34&y|0&?Jj
zJG1Vc3}wlZJ4t+IxjywD@_T;{YNJ0m*t;C$eD}<#D`;t(QB&c9f2MedlKyV3n*+K#EUjh>ZaST3A}-UsuyXH!I7%g0B^wn(|rNPc*zS
zRBDtbXjS&VlH31n;9=?fkJh_Fzwad@xOYCUqKYW3J0o}98G)?s=x4U^*HB7BehZk>
zAw$bYPp6DB8KK*~mZvh9+h*g;r(6;W?wjw!0XZcE?b^CC0+ppPG8NW!a@gC0lZ{_E
zZY#H6msEY1P$>H@^?jOBAzCi1B?SRLAvj^P3;*qlhS{#uokMmH9@n3g?}|%2=z!20
zn~)!dtZOeqx%UsGXFY
zXZE!x7h20%*4XT+f11UFb^B`OR*;f4cFz4#3nODHo}Uv^={1Yr_E@%P#7w}Vbo84@
ziPzMRoIutE*}&{`c1?Uq2{ropcP%n0%XM0o{0|-B%;1Qnv;MWW8g{iwqa#lju6>XQ
z>&L|JyF7LIUK0Q9SMs!bXZGKO1di_P`sK#Jzvpc^@zQgHcVg(rH#ozbAB|fcU!aYV
z?i6vGGDN@oF<#%$*344M&cAI5D8M4<((Ai#gDH8aO+{bp_(L&neE;lQtV&K;XR@$!
z<`t2-5MAb&*JW{5lVybLU4K3kg&RInZVG;KCQYO7Nk#rHiBfKgS-kDaf%C8Zg=5KOM*pw9LJdk$bfLfy?c8!$IUdRkYpDMvWq{*_UqT
zGMJ!z%x-MC4iHojbnkUX%53D6xETM~_sl!9M#qFF=Z7sX$9(gSmi6}+pT4RVTCdfjP}Q}A@cwWm&qbv~`B73p(P=VGFrMYd6Lz?7sR
z>p-PgP6ua+a^D~Io;xz$+)Ohz{zYlQv*~-C%y=QWJOHnTr6+A3B|hE4o;zW)%c=2|
z<8~k7by^Sb<5xqysXqc$)cF|hTyiahgz|65c{SQK0!euUy<*)czEhqEnI8&N`mKOysJVaUwQSV{71q8sQUG+~(zzG^hYfe0ZI?}1;e=_SYeX$br
z;jS>!nclZ#M9m&FpOYHOM41tSrDzK6A2OygYikVQb!-
zo^9~sDHcob-P~UY;vbR!QV5^i6^trVy2H&Ik>UfDQ*SUuo8cv2>B-RDC59t3M4`Fnrvk2T0
zKN4eXO3X-L1u}aP^!YVUz%mkzDG8>JBImw#yr`B|mpfek@(}eMF%sv8an3v(4!DnF
zbAJBQ4M2>WUNowu!Mf+WvgmuLi0#$MmDO^_FF04jnHN(ZT1mzQm(DE|xT>CdQ8g%h
z%>S0igRkj}wE|-jvBct)S&nx2-*n)Oc-P)$v2^aSVei4}uhE~#I7iaV)_abNi>
zt_fRtfelf;ec8tEX{r+(1E{sR*pZYy(SijGJdqy_6qXv%=O!JI$GEce89)6Ee
zh9?=wpb@mtzf5G|ln*Y>P6`S?A}M0a(X6Or5Y_6Kt3IO3oNPN~^(5$RzCha$d^;V&
zW;<^fxbY+o6mp5=UJ(zrB|f3rRPl~0{nRXj}%;0yj$;9Xz
z!Ba%kUN(RO?tI$c8_F>NHZ(A2M=RG>%|e$biPyGIG&1}c4;ZKC*syuOyPg6GD0ya8
zGI{BnRNLtSad`yo!RDs&1oRtlqlbO}oT*>fGpboyth2p);W+NjX@;h#Ll!Gc=fZ%%
zJ_M~{)7qWy8&h;r5C6U*-!a*LYmbRs2HjjMx+RD4DY1p=Q`R@Y4(|Eu8)lMLSBj$H
z?;?=MXmEj9?GX$ogA0S)Y+^&M>g1h9f7cF<;OhZb(s$^*Es8(658d6XVYkf}SHS&D
zKc@0*d1At!Arrfl0X^SWzfKw5oirTYvcE0eqxr`VSAtay^JpRAZjl{$%6=qEA2t
zF*-=L5J)%gealMHj#4>rMup6yD!OjT*rw?_9XV$nrUZ&8h{;yry9?RlUfpa@`OzY}
z6x)4nF;5VD-h&hnw0D0QZr3sGJ9x%XIV|6B=~tigTjIUCDAI?Gnsm*8b!>CAseDEDi>(?a&DY`?FqOl!%!##2=(
zL?)F4R`qPxNq&<@XLW!CNWoH@3+EnswrZ`qD00}r!R^DGZ+PBkhP#Zx4^5K4atvq;
JS+nM#{vW%#*gF6K
literal 0
HcmV?d00001
diff --git a/collaborative-statistical-reporting-guard/reports/risky-statistical-reporting-handoff.md b/collaborative-statistical-reporting-guard/reports/risky-statistical-reporting-handoff.md
new file mode 100644
index 00000000..49172b59
--- /dev/null
+++ b/collaborative-statistical-reporting-guard/reports/risky-statistical-reporting-handoff.md
@@ -0,0 +1,32 @@
+# Collaborative Statistical Reporting Handoff
+
+Decision: hold_collaborative_export
+Analyses reviewed: 3
+Held analyses: 2
+Audit digest: sha256:6043784e7d3140a56405bd2bc05ea1cb61c66dac75ea83ca824907f266668a62
+
+## Priority Findings
+
+| Severity | Code | Remediation |
+| --- | --- | --- |
+| critical | LOCKED_SECTION_STAT_EDIT_UNAPPROVED | approve_or_revert_locked_section_statistical_edit |
+| critical | SAMPLE_SIZE_DRIFT | reconcile_sample_size_text_table_and_figure |
+| high | BLOCKING_STAT_REVIEW_COMMENT_OPEN | resolve_blocking_statistical_review_comment |
+| high | BLOCKING_STAT_REVIEW_COMMENT_OPEN | resolve_blocking_statistical_review_comment |
+| high | CONFIDENCE_INTERVAL_MISMATCH | reconcile_confidence_interval_text_and_table |
+| high | CONFIDENCE_INTERVAL_MISMATCH | reconcile_confidence_interval_text_and_table |
+| high | EFFECT_SIZE_MISMATCH | reconcile_effect_size_text_and_table |
+| high | P_VALUE_TABLE_MISMATCH | sync_text_and_table_p_values |
+| high | P_VALUE_WORDING_CONTRADICTION | align_p_value_threshold_wording |
+| high | P_VALUE_WORDING_CONTRADICTION | align_p_value_threshold_wording |
+| high | SAMPLE_SIZE_SOURCE_MISSING | add_text_table_or_figure_sample_size_evidence |
+| medium | CI_SIGNIFICANCE_WORDING_CONFLICT | review_ci_and_statistical_significance_wording |
+
+## Analysis Actions
+
+| Analysis | Status | Actions |
+| --- | --- | --- |
+| analysis-survival-primary | hold_collaborative_export | reconcile_sample_size_text_table_and_figure, align_p_value_threshold_wording, sync_text_and_table_p_values, reconcile_effect_size_text_and_table, reconcile_confidence_interval_text_and_table, sync_figure_statistic_with_text_and_table, approve_or_revert_locked_section_statistical_edit, resolve_blocking_statistical_review_comment |
+| analysis-biomarker-subgroup | needs_statistical_review | add_text_table_or_figure_sample_size_evidence, align_p_value_threshold_wording, add_effect_size_to_text_and_table, review_ci_and_statistical_significance_wording, attach_figure_statistic_value_and_anchor |
+| analysis-exploratory-dose | hold_collaborative_export | attach_structured_p_value_or_mark_descriptive_only, reconcile_confidence_interval_text_and_table, resolve_blocking_statistical_review_comment |
+
diff --git a/collaborative-statistical-reporting-guard/reports/risky-statistical-reporting-report.json b/collaborative-statistical-reporting-guard/reports/risky-statistical-reporting-report.json
new file mode 100644
index 00000000..c0eebb96
--- /dev/null
+++ b/collaborative-statistical-reporting-guard/reports/risky-statistical-reporting-report.json
@@ -0,0 +1,210 @@
+{
+ "summary": {
+ "decision": "hold_collaborative_export",
+ "manuscriptId": "MS-COLLAB-STATS-RISK",
+ "analysesReviewed": 3,
+ "heldAnalyses": 2,
+ "findingCount": 16,
+ "criticalFindings": 2,
+ "highOrCriticalFindings": 11,
+ "auditDigest": "sha256:6043784e7d3140a56405bd2bc05ea1cb61c66dac75ea83ca824907f266668a62"
+ },
+ "analyses": [
+ {
+ "id": "analysis-survival-primary",
+ "label": "Survival model primary endpoint",
+ "sectionId": "locked-results",
+ "status": "hold_collaborative_export",
+ "sampleSizeDelta": 5,
+ "requiredActions": [
+ "reconcile_sample_size_text_table_and_figure",
+ "align_p_value_threshold_wording",
+ "sync_text_and_table_p_values",
+ "reconcile_effect_size_text_and_table",
+ "reconcile_confidence_interval_text_and_table",
+ "sync_figure_statistic_with_text_and_table",
+ "approve_or_revert_locked_section_statistical_edit",
+ "resolve_blocking_statistical_review_comment"
+ ]
+ },
+ {
+ "id": "analysis-biomarker-subgroup",
+ "label": "Biomarker subgroup model",
+ "sectionId": "results-subgroups",
+ "status": "needs_statistical_review",
+ "sampleSizeDelta": 0,
+ "requiredActions": [
+ "add_text_table_or_figure_sample_size_evidence",
+ "align_p_value_threshold_wording",
+ "add_effect_size_to_text_and_table",
+ "review_ci_and_statistical_significance_wording",
+ "attach_figure_statistic_value_and_anchor"
+ ]
+ },
+ {
+ "id": "analysis-exploratory-dose",
+ "label": "Exploratory dose trend",
+ "sectionId": "results-exploratory",
+ "status": "hold_collaborative_export",
+ "sampleSizeDelta": 0,
+ "requiredActions": [
+ "attach_structured_p_value_or_mark_descriptive_only",
+ "reconcile_confidence_interval_text_and_table",
+ "resolve_blocking_statistical_review_comment"
+ ]
+ }
+ ],
+ "findings": [
+ {
+ "severity": "critical",
+ "code": "LOCKED_SECTION_STAT_EDIT_UNAPPROVED",
+ "message": "analysis-survival-primary changed statistics inside a locked section without approval.",
+ "refs": [
+ "analysis-survival-primary",
+ "locked-results"
+ ],
+ "action": "approve_or_revert_locked_section_statistical_edit"
+ },
+ {
+ "severity": "critical",
+ "code": "SAMPLE_SIZE_DRIFT",
+ "message": "analysis-survival-primary reports inconsistent sample sizes across manuscript surfaces.",
+ "refs": [
+ "analysis-survival-primary",
+ "text",
+ "table",
+ "figure"
+ ],
+ "action": "reconcile_sample_size_text_table_and_figure"
+ },
+ {
+ "severity": "high",
+ "code": "BLOCKING_STAT_REVIEW_COMMENT_OPEN",
+ "message": "analysis-survival-primary has unresolved blocking statistical review comment stat-cmt-9.",
+ "refs": [
+ "analysis-survival-primary",
+ "stat-cmt-9"
+ ],
+ "action": "resolve_blocking_statistical_review_comment"
+ },
+ {
+ "severity": "high",
+ "code": "BLOCKING_STAT_REVIEW_COMMENT_OPEN",
+ "message": "analysis-exploratory-dose has unresolved blocking statistical review comment stat-cmt-13.",
+ "refs": [
+ "analysis-exploratory-dose",
+ "stat-cmt-13"
+ ],
+ "action": "resolve_blocking_statistical_review_comment"
+ },
+ {
+ "severity": "high",
+ "code": "CONFIDENCE_INTERVAL_MISMATCH",
+ "message": "analysis-survival-primary has inconsistent confidence intervals in text and table.",
+ "refs": [
+ "analysis-survival-primary"
+ ],
+ "action": "reconcile_confidence_interval_text_and_table"
+ },
+ {
+ "severity": "high",
+ "code": "CONFIDENCE_INTERVAL_MISMATCH",
+ "message": "analysis-exploratory-dose has inconsistent confidence intervals in text and table.",
+ "refs": [
+ "analysis-exploratory-dose"
+ ],
+ "action": "reconcile_confidence_interval_text_and_table"
+ },
+ {
+ "severity": "high",
+ "code": "EFFECT_SIZE_MISMATCH",
+ "message": "analysis-survival-primary has inconsistent effect sizes in text and table.",
+ "refs": [
+ "analysis-survival-primary"
+ ],
+ "action": "reconcile_effect_size_text_and_table"
+ },
+ {
+ "severity": "high",
+ "code": "P_VALUE_TABLE_MISMATCH",
+ "message": "analysis-survival-primary has different text and table p-values.",
+ "refs": [
+ "analysis-survival-primary"
+ ],
+ "action": "sync_text_and_table_p_values"
+ },
+ {
+ "severity": "high",
+ "code": "P_VALUE_WORDING_CONTRADICTION",
+ "message": "analysis-survival-primary has p=0.041 but the manuscript wording says not significant.",
+ "refs": [
+ "analysis-survival-primary"
+ ],
+ "action": "align_p_value_threshold_wording"
+ },
+ {
+ "severity": "high",
+ "code": "P_VALUE_WORDING_CONTRADICTION",
+ "message": "analysis-biomarker-subgroup has p=0.12 but the manuscript wording says significant.",
+ "refs": [
+ "analysis-biomarker-subgroup"
+ ],
+ "action": "align_p_value_threshold_wording"
+ },
+ {
+ "severity": "high",
+ "code": "SAMPLE_SIZE_SOURCE_MISSING",
+ "message": "analysis-biomarker-subgroup has fewer than 2 sample-size sources.",
+ "refs": [
+ "analysis-biomarker-subgroup"
+ ],
+ "action": "add_text_table_or_figure_sample_size_evidence"
+ },
+ {
+ "severity": "medium",
+ "code": "CI_SIGNIFICANCE_WORDING_CONFLICT",
+ "message": "analysis-biomarker-subgroup confidence interval crosses zero while wording says significant.",
+ "refs": [
+ "analysis-biomarker-subgroup"
+ ],
+ "action": "review_ci_and_statistical_significance_wording"
+ },
+ {
+ "severity": "medium",
+ "code": "EFFECT_SIZE_SOURCE_MISSING",
+ "message": "analysis-biomarker-subgroup lacks a paired text/table effect-size value.",
+ "refs": [
+ "analysis-biomarker-subgroup"
+ ],
+ "action": "add_effect_size_to_text_and_table"
+ },
+ {
+ "severity": "medium",
+ "code": "FIGURE_STATISTIC_INCOMPLETE",
+ "message": "analysis-biomarker-subgroup has incomplete figure statistic metadata.",
+ "refs": [
+ "analysis-biomarker-subgroup"
+ ],
+ "action": "attach_figure_statistic_value_and_anchor"
+ },
+ {
+ "severity": "medium",
+ "code": "FIGURE_TEXT_STATISTIC_MISMATCH",
+ "message": "analysis-survival-primary figure statistic differs from the manuscript effect size.",
+ "refs": [
+ "analysis-survival-primary",
+ "fig-4"
+ ],
+ "action": "sync_figure_statistic_with_text_and_table"
+ },
+ {
+ "severity": "medium",
+ "code": "P_VALUE_MISSING",
+ "message": "analysis-exploratory-dose has no structured p-value for export validation.",
+ "refs": [
+ "analysis-exploratory-dose"
+ ],
+ "action": "attach_structured_p_value_or_mark_descriptive_only"
+ }
+ ]
+}
diff --git a/collaborative-statistical-reporting-guard/reports/statistical-reporting-dashboard.svg b/collaborative-statistical-reporting-guard/reports/statistical-reporting-dashboard.svg
new file mode 100644
index 00000000..373aa4e4
--- /dev/null
+++ b/collaborative-statistical-reporting-guard/reports/statistical-reporting-dashboard.svg
@@ -0,0 +1,30 @@
+
diff --git a/collaborative-statistical-reporting-guard/sample-data.js b/collaborative-statistical-reporting-guard/sample-data.js
new file mode 100644
index 00000000..cdc051fe
--- /dev/null
+++ b/collaborative-statistical-reporting-guard/sample-data.js
@@ -0,0 +1,191 @@
+const cleanPacket = {
+ manuscriptId: "MS-COLLAB-STATS-CLEAN",
+ reviewDate: "2026-06-01",
+ policy: {
+ alpha: 0.05,
+ sampleSizeTolerance: 0
+ },
+ analyses: [
+ {
+ id: "analysis-primary-response",
+ label: "Primary response difference",
+ sectionId: "results-primary",
+ sampleSizes: {
+ text: 128,
+ table: 128,
+ figure: 128
+ },
+ pValue: {
+ reported: 0.031,
+ table: 0.031,
+ wording: "statistically significant at alpha 0.05"
+ },
+ effectSize: {
+ text: 0.42,
+ table: 0.42
+ },
+ confidenceInterval: {
+ text: { low: 0.08, high: 0.76 },
+ table: { low: 0.08, high: 0.76 }
+ },
+ figureStatistic: {
+ figureId: "fig-2",
+ value: 0.42
+ },
+ lockedSection: {
+ sectionId: "results-primary",
+ locked: true,
+ lastStatisticEditAt: "2026-05-28T10:00:00Z",
+ approvedAt: "2026-05-29T09:00:00Z"
+ },
+ reviewerComments: [
+ {
+ id: "stat-cmt-1",
+ blocking: true,
+ resolvedAt: "2026-05-30T12:00:00Z"
+ }
+ ]
+ },
+ {
+ id: "analysis-secondary-response",
+ label: "Secondary response difference",
+ sectionId: "results-secondary",
+ sampleSizes: {
+ text: 96,
+ table: 96
+ },
+ pValue: {
+ reported: 0.18,
+ table: 0.18,
+ wording: "not statistically significant"
+ },
+ effectSize: {
+ text: 0.11,
+ table: 0.11
+ },
+ confidenceInterval: {
+ text: { low: -0.07, high: 0.29 },
+ table: { low: -0.07, high: 0.29 }
+ },
+ figureStatistic: {
+ figureId: "fig-s1",
+ value: 0.11
+ },
+ lockedSection: {
+ sectionId: "results-secondary",
+ locked: false
+ },
+ reviewerComments: []
+ }
+ ]
+};
+
+const riskyPacket = {
+ manuscriptId: "MS-COLLAB-STATS-RISK",
+ reviewDate: "2026-06-01",
+ policy: {
+ alpha: 0.05,
+ sampleSizeTolerance: 0
+ },
+ analyses: [
+ {
+ id: "analysis-survival-primary",
+ label: "Survival model primary endpoint",
+ sectionId: "locked-results",
+ sampleSizes: {
+ text: 214,
+ table: 209,
+ figure: 214
+ },
+ pValue: {
+ reported: 0.041,
+ table: 0.064,
+ wording: "not significant after adjustment"
+ },
+ effectSize: {
+ text: 0.38,
+ table: 0.51
+ },
+ confidenceInterval: {
+ text: { low: 0.04, high: 0.72 },
+ table: { low: -0.02, high: 0.78 }
+ },
+ figureStatistic: {
+ figureId: "fig-4",
+ value: 0.6
+ },
+ lockedSection: {
+ sectionId: "locked-results",
+ locked: true,
+ lastStatisticEditAt: "2026-05-31T19:00:00Z",
+ approvedAt: ""
+ },
+ reviewerComments: [
+ {
+ id: "stat-cmt-9",
+ blocking: true,
+ resolvedAt: ""
+ }
+ ]
+ },
+ {
+ id: "analysis-biomarker-subgroup",
+ label: "Biomarker subgroup model",
+ sectionId: "results-subgroups",
+ sampleSizes: {
+ text: 48
+ },
+ pValue: {
+ reported: 0.12,
+ table: 0.12,
+ wording: "statistically significant subgroup effect"
+ },
+ effectSize: {
+ text: 0.27
+ },
+ confidenceInterval: {
+ text: { low: -0.18, high: 0.61 },
+ table: { low: -0.18, high: 0.61 }
+ },
+ figureStatistic: {
+ figureId: "",
+ value: null
+ },
+ lockedSection: {
+ sectionId: "results-subgroups",
+ locked: false
+ },
+ reviewerComments: []
+ },
+ {
+ id: "analysis-exploratory-dose",
+ label: "Exploratory dose trend",
+ sectionId: "results-exploratory",
+ sampleSizes: {
+ table: 72,
+ figure: 72
+ },
+ pValue: {},
+ confidenceInterval: {
+ text: { low: -0.01, high: 0.32 },
+ table: { low: -0.04, high: 0.35 }
+ },
+ lockedSection: {
+ sectionId: "results-exploratory",
+ locked: false
+ },
+ reviewerComments: [
+ {
+ id: "stat-cmt-13",
+ blocking: true,
+ resolvedAt: ""
+ }
+ ]
+ }
+ ]
+};
+
+module.exports = {
+ cleanPacket,
+ riskyPacket
+};
diff --git a/collaborative-statistical-reporting-guard/test.js b/collaborative-statistical-reporting-guard/test.js
new file mode 100644
index 00000000..406d70eb
--- /dev/null
+++ b/collaborative-statistical-reporting-guard/test.js
@@ -0,0 +1,39 @@
+const assert = require("node:assert/strict");
+const { evaluateStatisticalReporting, sha256 } = require("./index");
+const { cleanPacket, riskyPacket } = require("./sample-data");
+
+const clean = evaluateStatisticalReporting(cleanPacket);
+assert.equal(clean.summary.decision, "release_collaborative_export");
+assert.equal(clean.summary.findingCount, 0);
+assert.equal(clean.summary.analysesReviewed, 2);
+assert.equal(clean.summary.heldAnalyses, 0);
+assert.ok(clean.summary.auditDigest.startsWith("sha256:"));
+
+const risky = evaluateStatisticalReporting(riskyPacket);
+assert.equal(risky.summary.decision, "hold_collaborative_export");
+assert.equal(risky.summary.analysesReviewed, 3);
+assert.equal(risky.summary.heldAnalyses, 2);
+assert.ok(risky.summary.findingCount >= 13);
+assert.ok(risky.summary.criticalFindings >= 2);
+assert.ok(risky.summary.highOrCriticalFindings >= 8);
+
+const findingCodes = new Set(risky.findings.map((finding) => finding.code));
+assert.ok(findingCodes.has("SAMPLE_SIZE_DRIFT"));
+assert.ok(findingCodes.has("SAMPLE_SIZE_SOURCE_MISSING"));
+assert.ok(findingCodes.has("P_VALUE_WORDING_CONTRADICTION"));
+assert.ok(findingCodes.has("P_VALUE_TABLE_MISMATCH"));
+assert.ok(findingCodes.has("EFFECT_SIZE_MISMATCH"));
+assert.ok(findingCodes.has("EFFECT_SIZE_SOURCE_MISSING"));
+assert.ok(findingCodes.has("CONFIDENCE_INTERVAL_MISMATCH"));
+assert.ok(findingCodes.has("FIGURE_TEXT_STATISTIC_MISMATCH"));
+assert.ok(findingCodes.has("FIGURE_STATISTIC_INCOMPLETE"));
+assert.ok(findingCodes.has("LOCKED_SECTION_STAT_EDIT_UNAPPROVED"));
+assert.ok(findingCodes.has("BLOCKING_STAT_REVIEW_COMMENT_OPEN"));
+assert.ok(findingCodes.has("P_VALUE_MISSING"));
+
+const firstDigest = evaluateStatisticalReporting(riskyPacket).summary.auditDigest;
+const secondDigest = evaluateStatisticalReporting(riskyPacket).summary.auditDigest;
+assert.equal(firstDigest, secondDigest);
+assert.equal(sha256({ b: 2, a: 1 }), sha256({ a: 1, b: 2 }));
+
+console.log("collaborative statistical reporting guard tests passed");
diff --git a/collaborative-statistical-reporting-guard/verify-video.js b/collaborative-statistical-reporting-guard/verify-video.js
new file mode 100644
index 00000000..39af983c
--- /dev/null
+++ b/collaborative-statistical-reporting-guard/verify-video.js
@@ -0,0 +1,37 @@
+const assert = require("node:assert/strict");
+const fs = require("node:fs");
+const path = require("node:path");
+const { spawnSync } = require("node:child_process");
+
+const videoPath = path.join(__dirname, "reports", "demo.mp4");
+assert.ok(fs.existsSync(videoPath), "reports/demo.mp4 must exist");
+assert.ok(fs.statSync(videoPath).size > 5000, "reports/demo.mp4 should not be empty");
+
+const probe = spawnSync(process.env.FFPROBE_PATH || "ffprobe", [
+ "-v",
+ "error",
+ "-select_streams",
+ "v:0",
+ "-show_entries",
+ "stream=codec_name,width,height,r_frame_rate:format=duration",
+ "-of",
+ "json",
+ videoPath
+], { encoding: "utf8" });
+
+if (probe.status !== 0) {
+ process.stderr.write(probe.stderr || "ffprobe failed\n");
+ process.exit(probe.status || 1);
+}
+
+const metadata = JSON.parse(probe.stdout);
+const stream = metadata.streams && metadata.streams[0];
+assert.equal(stream.codec_name, "h264");
+assert.equal(stream.width, 960);
+assert.equal(stream.height, 540);
+assert.equal(stream.r_frame_rate, "18/1");
+
+const duration = Number(metadata.format && metadata.format.duration);
+assert.ok(duration >= 3.9 && duration <= 4.2, `unexpected duration ${duration}`);
+
+console.log(`demo.mp4 verified: ${stream.codec_name}, ${stream.width}x${stream.height}, ${duration.toFixed(3)}s, ${stream.r_frame_rate}`);