Q@DQ1@x=mzPH!T^{ZbM^cB#re*4~DJJ+v%RnS*JzxwTad+l7m`c*++
z0sZQ?@9nj7{pwc*eFgNZ-@dok&h@Kb74#L*uYUX9UOU&XepS#{K)?F!dwcC%zt{bK
z{@Dj7Ue^;||M|qg%U52XbNwghX1eX`3F!CDySSe4`p@TDdwtIJpPZYy-tBdw-TFN~
z@3}74n0b5(?=CFR==bLwKL!0B`*?Rrfo1w#^65Affnx#v9{YHgz{~>6^!wra@1A%a
zKbjK*FJBoy^LRz?E-awmgNLi%0~~M7AfVrar=Z^h9B<7apx=Y1px*-=Z_OZ}--D;1
z-vb^X0{R^c
zZaD%$0sRIsjUq5uK)-{*Ek__Ipx+>-Q3M7H=yx!<
-@)LPBM=m5)bEQIUwO?Y;Mc&O7Kl`=-`R@jwV%_s*BABM>#8rgTN$1f
z(C?B(^g7h(+v|(^?RC|c+^q~x3+Q*rB6=O_^zHRU{r0-*OYT;Nrv>!8WD&g%b^7-D
zqJDc_^(A*J!_xx#U9yN?hdO>bKWbUvjrHJT0K#C5z~FsMELC
z7kBG7a$Z9StR&Fr_iwDk?kof%0{V?G8bY8%K))sI79|i7&~Jp%5CSCv`YmC%D1nH8
zej|*A5GWDQZwb3a2}A_+8(}nrK#72UOV}++AR?gO2%{kcN(A&px+X9ixP+k=r_V>2!RrT@%>&_^ddkYCGhi4KY9@$
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pk1PBlyaF@W#mmh!h^u=3GcYVo+2@oJafB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z009C72oSgkeEZGUUIYjP1xEhGa@qDT{vkjhByb5zzadLw2#glc?`U!B5y%PXH)m-Q
zfzbl`9W8D>0yzQw<}6JjFj_#rqs6U9ASa;ToTW(wMhob7w7B&M
z7PlUOoPd6FmL?GxEui1g;?^UO6KK@$d!N3&_Uni5zw_EhAQG{D=lMHfpR4Pn7Xkgw
zlU`&eZuM3I`i)dG&ycNdufythp7bI+aizBs&~K!gd4_CtdmUE4^Q0Hqi7UO8fPN#@
z%rj)G+v~9UohQA>PF(4&1oRuJW}YEi-Cl>)?>y;6cH&BJC7|C(HS-MF>h?OUe&I}3q`fPN#4h7c$b&~FL5MF~U%^c!I`gg}XaeoNRb
zN+2Sj-w2~21WE+-Tf%Np0ucfIMi>ntP$Hn;5_XFchzRI6!e|JA5&`{|uv?TsL_oh0
zMnedc2b*
zzkP47o$FV>D(EYqU;XyIy>_l&{i>j^fPVGc_x9Ske)X$@z5@EyZ{OQ%=la#J3i=A@
zSHFF4ubt~xzbfb}pkMv=y}fp>U;V0}uYi8_+xPa`xqh$v{rs~JPQ0!sy#DivftRnm
zKIi&R&dqe&*Avk1n|EpwX+bG_T^M7#BSeBN_itTFTW6y9A}pwaKo
zIerTIJ@)bLk^;;0yX4bxC<4a<`aSmXE`gZ^mg)Dy_uoD7I({@K24220e&+Ft-d$Kg
zzXuOjzXv$pnn6In2Twu22RPoEK|sF;PeH#2INq8;K)(l1LB9t$-kL!`zXwl2zXv$p
znn6In2Twu22RPoEK|sF;PeH#2INq8;V3~erz_bSfg9Y?E7~FCMf&%&tVj4wYuz-FC
zgIkV3P(Z&yOrr=47SQitaLW-03g|b8X%vCM0{R^cZaD%$0sRIsjUq5uK)-{*Ek__I
z(5T-RFTV0>E#P;?UJFDj)^F<~dTsvt_WD`By>74FYW7+{zl!K3aD99ItlwU@*KRd?
zEuddT^b)wfy?)kjuiI<4n!Og#uOfO0T;E$lhKwOh?z3+Pu7y#%grub=hX>-O5M
zX0HYGtB76#*SFWt`t5al?N+nb0{T@%FM;dZ>u3G;y1jO**=qs)Dx#Ob_3ibuetX?s
zyVdNqfPNLxOW^wU`gym0Bj+`Qz)Av*e*eZw?9M_UBB0+0qag%J1oT_NZczde0sTf8
z4Ixk>px+X9ixP+k=r_V>2!Rp-{g$v>lt4s4zY#`52$Truw}joI1R?_ZjW8NQphQ5w
zCF~X@5E0OCgwYTJB?9^_VYeuOh=6`0jD`>>5zuc5yG02^1oRtWG=xBjfPPEZElMCF
zpx+3iAp}YU^jpGiQ34SG{YDrKAy6Wq-x79<5{L-sH^OKLff51zmatotKtw>l5k^A@
zlnCgzgx#V9A_Dr2Fd9OjL_ohK>=q>u5zudh(GUV90{Sgsw?+{OLt@=(aB@px;Ox^AB0__Bxb)=T9%PLsxxC0sThmn19HUx7VTc
zJAZnS9lGjE3g|ad$NWQ39D0B0F@|mlV)%q>lNAEO~n!O26}`7ulh!zNCPDBX!I_WXap>Q2L!e
zy~qw-^(6)L8>wUdAxqv~huW>*$axJRu#!Nd-@maEyR#682px+X9
zixP+k=r_V>2!Rp-{g$v>lt4s4zY#`52$Truw}joI1R?^B`hDk__1pLM+PQx9tAf4)`qgjW+iU0g)vpTr3g}nAeQ&Rw>sP-j=qsRK
z{r0`RcCKIjs-Ulce)Ze;_S(6A^{axu0{Yc&-`i{F`qi%r`U>b*zkP47o$L3y-_Jk$
z;Kb{C!s|bu70>vOLEO1+$Tg^}A0Iy-up%TLRZ_uUoy(ZuP6*eQx`D
zC)KZh*SppGoJha=-RHKicT)Z8cfDJ^&x!P_-+gZTdMDMde%HIz``rYTYuXj@Y
z>UX_cz0ZmCtKWTY`+6tUuYT9N)%%=Czxv(hwy$?m{pxqUTfNVT^sC=}Zu@#C)vtcn
zyVd)gNWc2s=eDnRQvEL0?=8pi83J1gH2S~a-|D$IEP-1D^m~hv&k&eXK)-W77rP>G
zi-3M_QSuoAa|-Bp&gWuR1a1+~?=4C`Ltst;{m%JZ?25oG0{Xp0$!7@6DWKmupNm})
zxJ5v}wS?hQOQx`knK+*cE|W1oV50lFtyBQ$W9SJ{P+paEpL`Z&C6Y0&@!Jch2Wx
zR|IYm(C;luK0{zm0sYSTT-lF6)1m+YN-|uBbF9HNo0zd!sqZa`J1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF
z5FkK+009C72oNAZfB*pkcL}_F`SC|jU%d5n*Oz>l009C72oNAZfB*pk1PBlyK!5-N
z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+
z0D+6Zx8Hp2MSwt1VB}vcmu>&z9|8nI0+*ol8?rQpz-R&ejuy8bft-MTbCxC%7%iaR
z(c;!4kQ2~v&e9|TqXqOkTHJaBasv9zS(-#(w19p`i(8LCPC&moOOptU7SQi#aqAJt
z3FtRxX%d0a0{R^-Zao4yfkyql_vzbSuLOSh{yVSM0+ERI`>KduKeWEOHh&S&Z?EFA
zyOlt90sUr+=ryhT?X`dX_9`yBTlc@afPS+@^qSWF_S(OGdli@6t^40yK)=}{dQIzo
zd+lGpy^723*8T4;px;88a&~LVgUemhYUi;T?ui~=1b^p5y
z=r>zLuW8+Hul?({S8>_hy8qn;^qVcB*R<}p*Z%d}tGMiL-T&?a`pp*6Yg+f)YyZ3T
z8#%8b1XdDg^!qnfVs{n-5dr;17!4s%BB0+Ac8d~-2px+X9ixP+k
z=r_V>2!Rp-{g$v>lt4tFQNQne@}<`+0l$B>yMTUQUFHpc=ze?cU%wk>`|8N
zzPijC{?PsQ+P{7`%=Xoh-T&?a`h9hoH~gXd?X`dXZkX+>BfJ0I1@!ysGH>`p_uFg#
z`rR
zpx;-QdBY#N-(LII?}pjFIf?
z&$<4S)J*rkyMTTl(M6Y;-EXh`@7C|}dC&cKZH<}7r||B=0*!uu&hb;w@3D_}mlRm0
z-zA@pLlHO@(C@L2cL~fauuQ)nzW?rt*YTq{G4S%0@iUKC^zOm}`aO8K`aQt$)(isr
zJ$MTGJ;3qS3_3@zx9i`aO6G`aQt$)(isrJ$MTGJ;3qS
z3NSHJt*_VrGxU;VCktM@sPe)YT0ZC~%C
z`ql4xw|buw=~ut|-1hZOs$c!CcdPd~k$&~N&uw4tr25tGdbfI?6X{pK``q^RPO4x1
zu6L{VIgx(n@Avfo;jk|fSXH3W@84MU5jYot(*pWEP3MaQ$_4aW{s?SaWzy=7M7SQi$I$tDEE}-A?
zM_>a4P7CPwG@UOJC>I#t?`1_V0t8Y5WBxz?T7>`s0t5&UAV7cs0RjXF5FkK+009C7
z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!CtX
z0xw^F{L#}FZ#}JaCTAf)fB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAJM&R3TzV;$OASf`V+&`>x
z+4e8~AwVD`a0yDkAxmQjj26)EXmRTi$O-5-XK50F(E|D%Ep9ymIRX9VEKMRXT0p;}
z#jQsmC!pV)rAY)v3+Q*Wxb+C+1oWG;G>O1y0sW2^w;q9g}pT6z&tiTW7f9KUgAQG{DpDm)-2U=WRo4g3{b_D1oS(mh+eC7xxMzO-(F>n*{v?R2ntP$Hn;5_XFchzRI6
z!e|JA5&`{|uv?TsL_oh0Mnedc2(Sy4+s-)bECQ
zK6_+cbP>?+vzK|p4|KV`_Nm_u^L+Nmy67UH-)ArLh9Bs1d+k%d8|L}!k#*5UK)=sk
z<_$m4<@VaAemBhX*(2+si-3Ngz04bapvGPyKF~=d(xFMHd16K6{xr{6Lr6YoGev
zFwbX?tcxxJ`hE5?Z}@>Ox7R-Pd!6U!pM7xRbv@zrpHB?DeC72y*MBn4Oa;{n=y%R8
zu6OnN&-$&cW}ek|>-YG)=ejWSSirlJ3pD!uImeRvy;tDa)4MGMmg%>JwoMYq3g|cM
zX&Ql%0?YLK;rs8NcpX!p708Z(bi;oV6D^n36i^m~Bgtr-OLd+-$Wdw}Du83gou
z@D%iWfa9$h1oV6G6!d$5z!1;`d#l{gyk
z>UW>pzTQdotKao*^*$%kuYUKr?dzRXzxrM8R_}8n{pxq0+rHjO^{e0YZuLGV(yxB^
zx$WznRKNON?^f?~BK_)jpWD9PN%gDW^=|b(C(^He_qpxsom9X2UGG-!b0Yng^n3dM
zaM%|KtSZpx_iwEF2%L++X#xG7rt?JtaWzy=7M7SQi$I$tDEE}-A?M_>a4P7CPw
zG@UOJC>PLg`6I9a0;dJ^dz#J{36u-yxBL;<0D;p2`aMnOiv-FA^jrQ2Y=FRN0sWq)
z^F;#X0{Sg~1U5k6w19q3)A=HSasmC8KLQ&da9TjWr|Eo=K)HZ^%O8Oa5I8NM-_vxy
zNT6ImzvYj>1_+!M(C=wFUnEd2px^RGU;_kB3+VSWoi7q77tn9{Bd`Girv>zTn$8yq
zlnadS_p+iF0Rkz3)Bn$WeUShG0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly
zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!Cuu0xw^F{L#}FZ#``b
z;NS!Z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ
zfB*pk1PBlyK!5-N0t5&UAV7cs0RjY03w-;{*Ion&1O-mVc;btfZU5pQ0t7+=m!M8$
zc={{)4Lvtw2>dFbU-y7OUjhC0eRg(EK)>z)fxZIz?fdNPoPd7a0Rnvm^xOB@**O9I
zx&s9I3h1}*v$Jyo`gI2g^cB!=-)Cp%1oZ0;5a=t=sNeTKecS7tzz^Sl=QW!^92!Rp-{g$v>lt4s4
zzY#`52$Truw}joI1R?_ZjW8NQphQ5wCF~X@5E0OCgwYTJB?9^_VYeuOh=6`0jD`>>
z5zuc5yG02^1oRtWG=xBjfPPEZElMCFpx+3iAp}YU^jpGiQ34SG{YDrKAy6Wq-x79<
z5{L-sH^OKLff51zmatotKtw>l5k^A@lnCgzgx#V9A_9&2edm)ez0L{vWvvqm==a=Z
z-tb!!-d*YCcM42q~8roJa=TVOemn=bC-F;Z%uf6ZArfy
zmU!;SVwq4tzvnLVhTod-_S%wuH!Shok;O8hfPT+i<_*6!;qA30{cc#|xg(2ZLIM4r
zyUZJYYr@-WOZwfg#B)a$%Y*{@J$IQm{MLlG*Ov6VVTtFCES3oc^n30yZ}_bVZ?7%s
z_qxQ-Kl|Xs>w3cLKc5(Q`O52auK#47nF^{E(C?gGT<_}jpY>Z?%{;5`*6;Co&vjwu
zv4D3c7ijeRbB-nTd#}K;r*~TjEYoibZJQ*J70_?i(=-Aj1(xae!}s4k@j9kHG4S%0
zHD(^4!n>0Q==b13==T7}TQdmg_uwh$_W;LRGYIJS;3??$0LNQ12MJuU~tP32ny&oh-nmo!2_^+F^wWHSU|sn!7WE1D4^dUrcne2
z3+Q(+xa9~01se7H;>A~91O^K{?)S|`D%S7d`(U|V0{Z>=rypOvE`ME|{%fb#bGO?y
z1oZnV-pddAUE@~oa|-?Hcc0t7-bwYV-}P?wJ}1(ze)qZU>z!1;`d#l{gyk>UW>p
zzTQdotKao*^*$%kuYUKr?dzRXzxrM8R_}8n{pxq0+rHjO^{e0YZuLGV(yxB^x$Wzn
zRKNON?^f?~BK_)jpWD9PN%gDW^=|b(C(>`K-_!qx!@fvhRe?soe`D20;9LYw3+VSW
zoi7q77tn9{Bd`Girv>zTn$8yqlndy${1Mmyfztx|Jx%9}1j+^UTmA@afWT=1{hp@t
zMFQml`YnG1HbCICfPPQY`67XG0sWRg0vjN3T0p<2>3oquxqyDlAAt=JI4z*x({#Q_
zpj<$|<&VGy2%Hws?`b+;Bv3A(-||Ob0|ZVB==U_8FA^vh&~N!8umJ+61@wEG&KC)k
z3+T7}5!e8M(*pWEP3MaQ$_4aW{s?S_`#w86C!k+$g`d6Yf?-g#!AWu!vq;D!jdx)^D#^Cfu!v3I+5#VG+HyRCs$Wy<5ML
z^BO{6C4ol2e`6(fXCV*~&~Jp%5CSCv`YmC%D1nH8ej|*A5GWDQZwb3a2}A_+8(}nr
zK#72UOV}++AR?gO2%{kcN(A&OdQzuC*Y
z;k3frYia#%SX}nVA}SQnZ}u{8IIZyZT3Wvw7MDG;hzbSto4w2%PAj~3*
zTE80>mp!tG3I+6=z04aJtMmUs+@3@hQAJiGY3&9)x}maJ)5xfPN32f_@KhyfuS>
zeh;34eh+ZGHG_bD51xX44{*FSgMfYyo`QZ4aJ)5xfPN32f_@KhyfuS>eh;34eh+ZG
zHG{x1{my`C4+I7a=yx!<-@)LPBM=nO
zZxGWc0)qwgI~d$@1cCzk4PqKaV6cFG2ZLLVKv1Ajzb{^V^(gIjLjnEjcf)JH%#ro0-(@cIh6mBFemA`K%N$w1`d#KSZ+H;>>UYCy
zzs!;KtKVfV^M(h}uYNbY_RAbuzxrL~GH-Yg{pxqaYro8q^{d}yF7t*5(XW0ty!Oi+
zS-<*S<}z=15dG?R!)w3Hk@c(JWiIoE2hp#7H@x=C99h2y{d$W4fxjit==X2@t)2T<
z0tEEy4iM-opx?gF&dv$w*Bv0xS3tjgpPii((62i{ps#>_`#w86C!k+kbg;E1=)L&(6*X=+_+}&{sgeeV?736VR_aK%lRHe)~Q0yzQw<}6JjFj_#rqs6U9ASa;ToTW(wMhob7
zw7B&M7PlUOoIs;~-~04!uK@x-eE*%-0D(xv`W;Y2uO$XtT}xa9
z^xLb8F1r=i7yHIp+3SMZdke=(1a7jSeWsMQg?|{p^;SytRuT}KBVO#@_tgJBt`WGJ$MlMJ;3qS
z3_3@zx9i`aO6G`aQt$)(isrJ$MTGJ;3qS3_3@zx9i%k(<~racfCETG@P;Fcp06wq%F(>vx&^;2^yO^xNxs*)4%<0sUTU_?bX20sZ!RUUp01T0pI?^y##jaH*#J>2&^Q~
z==X1|#O^EvA_Dr2Fd9OjL_ohK>=q>u5zudh(GUV90{SgswntP$Hn;5_XFchzRI6
z!e|JA5&`{|uv?TsL_oh0Mnedc2fisB%g@*U{%`!*
z-}%kIzxaRt?r;A6-T&=h{>{Jt`~She`kR0MkH7xce)I4D>HqYv|K{KS;opDzH~;>R
z{;z-YH~;>RKlrzP^Y8!dpZwdu`S*|i&v$~jo3EY#
literal 0
HcmV?d00001
diff --git a/repository-version-tag-governor/reports/repository-version-tag-packet.json b/repository-version-tag-governor/reports/repository-version-tag-packet.json
new file mode 100644
index 00000000..4541f944
--- /dev/null
+++ b/repository-version-tag-governor/reports/repository-version-tag-packet.json
@@ -0,0 +1,137 @@
+{
+ "mixed": {
+ "generatedAt": "2026-05-28T08:10:00.000Z",
+ "repositoryId": "synthetic-repository-versioning",
+ "status": "hold_tag_publication",
+ "summary": {
+ "status": "hold_tag_publication",
+ "proposalCount": 3,
+ "blocked": 1,
+ "manualReview": 1,
+ "approved": 1,
+ "blockerCount": 6,
+ "warningCount": 6
+ },
+ "proposals": [
+ {
+ "proposalId": "reused-preprint-tag",
+ "tag": "preprint-v2.0",
+ "releaseType": "preprint",
+ "decision": "block_release_tag",
+ "blockers": [
+ {
+ "code": "version_tag_collision",
+ "message": "preprint-v2.0 already points to a different commit or DOI."
+ },
+ {
+ "code": "non_monotonic_version_tag",
+ "message": "preprint-v2.0 does not advance beyond the latest preprint version."
+ },
+ {
+ "code": "metadata_version_drift",
+ "message": "metadata.json version preprint-v1.9 does not match tag preprint-v2.0."
+ },
+ {
+ "code": "doi_metadata_mismatch",
+ "message": "Repository DOI and metadata DOI differ for the proposed version."
+ },
+ {
+ "code": "artifact_hashes_missing",
+ "message": "Missing hash locks for data."
+ },
+ {
+ "code": "fork_attribution_missing",
+ "message": "Derived repository release lacks parent project attribution."
+ }
+ ],
+ "warnings": [
+ {
+ "code": "citation_version_stale",
+ "message": "Citation text references preprint-v1.9 instead of preprint-v2.0."
+ },
+ {
+ "code": "results_hash_missing",
+ "message": "Results output hash is absent for a reproducibility-bearing tag."
+ },
+ {
+ "code": "changelog_entry_missing",
+ "message": "No changelog entry is attached to the proposed tag."
+ }
+ ],
+ "actions": [
+ "Create a new monotonic tag instead of reusing a published repository version.",
+ "Advance the semantic version or preprint version before publication.",
+ "Update metadata.json before DOI or citation release.",
+ "Reconcile DOI fields before publishing the tag.",
+ "Regenerate citation text and Cite this project metadata for the tag.",
+ "Lock manuscript, data, code, and metadata hashes before release.",
+ "Add result/figure hash evidence or mark the release as metadata-only.",
+ "Attach parent repository DOI and fork lineage before publishing the tag.",
+ "Add a concise changelog entry that explains version-scoped changes."
+ ]
+ },
+ {
+ "proposalId": "minor-release-needs-review",
+ "tag": "v1.3.0",
+ "releaseType": "release",
+ "decision": "manual_review_tag",
+ "blockers": [],
+ "warnings": [
+ {
+ "code": "citation_version_stale",
+ "message": "Citation text references v1.2.0 instead of v1.3.0."
+ },
+ {
+ "code": "results_hash_missing",
+ "message": "Results output hash is absent for a reproducibility-bearing tag."
+ },
+ {
+ "code": "changelog_entry_missing",
+ "message": "No changelog entry is attached to the proposed tag."
+ }
+ ],
+ "actions": [
+ "Regenerate citation text and Cite this project metadata for the tag.",
+ "Add result/figure hash evidence or mark the release as metadata-only.",
+ "Add a concise changelog entry that explains version-scoped changes."
+ ]
+ },
+ {
+ "proposalId": "major-release-ready",
+ "tag": "v2.0.0",
+ "releaseType": "release",
+ "decision": "approve_tag",
+ "blockers": [],
+ "warnings": [],
+ "actions": []
+ }
+ ],
+ "auditDigest": "9dc7b2c8ec29eb7ea6d6c580bf662d2030955576c69344176e6d2caf58bd38f9"
+ },
+ "ready": {
+ "generatedAt": "2026-05-28T08:10:00.000Z",
+ "repositoryId": "synthetic-ready-repository-versioning",
+ "status": "ready",
+ "summary": {
+ "status": "ready",
+ "proposalCount": 1,
+ "blocked": 0,
+ "manualReview": 0,
+ "approved": 1,
+ "blockerCount": 0,
+ "warningCount": 0
+ },
+ "proposals": [
+ {
+ "proposalId": "major-release-ready",
+ "tag": "v2.0.0",
+ "releaseType": "release",
+ "decision": "approve_tag",
+ "blockers": [],
+ "warnings": [],
+ "actions": []
+ }
+ ],
+ "auditDigest": "1c36ac737eb497dbfb0259fc5d5066df268d68105657f60afd1419ef4484f00e"
+ }
+}
diff --git a/repository-version-tag-governor/reports/repository-version-tag-report.md b/repository-version-tag-governor/reports/repository-version-tag-report.md
new file mode 100644
index 00000000..ef0684e7
--- /dev/null
+++ b/repository-version-tag-governor/reports/repository-version-tag-report.md
@@ -0,0 +1,34 @@
+# Repository Version Tag Governor Report
+
+Synthetic semantic tag governance packet for SCIBASE issue #10.
+
+## Summary
+
+| Packet | Status | Blocked | Manual Review | Approved | Digest |
+| --- | --- | ---: | ---: | ---: | --- |
+| mixed | hold_tag_publication | 1 | 1 | 1 | 9dc7b2c8ec29 |
+| ready | ready | 0 | 0 | 1 | 1c36ac737eb4 |
+
+## Proposal Decisions
+
+### reused-preprint-tag: block_release_tag
+- blocker version_tag_collision: preprint-v2.0 already points to a different commit or DOI.
+- blocker non_monotonic_version_tag: preprint-v2.0 does not advance beyond the latest preprint version.
+- blocker metadata_version_drift: metadata.json version preprint-v1.9 does not match tag preprint-v2.0.
+- blocker doi_metadata_mismatch: Repository DOI and metadata DOI differ for the proposed version.
+- blocker artifact_hashes_missing: Missing hash locks for data.
+- blocker fork_attribution_missing: Derived repository release lacks parent project attribution.
+- warning citation_version_stale: Citation text references preprint-v1.9 instead of preprint-v2.0.
+- warning results_hash_missing: Results output hash is absent for a reproducibility-bearing tag.
+- warning changelog_entry_missing: No changelog entry is attached to the proposed tag.
+
+### minor-release-needs-review: manual_review_tag
+- warning citation_version_stale: Citation text references v1.2.0 instead of v1.3.0.
+- warning results_hash_missing: Results output hash is absent for a reproducibility-bearing tag.
+- warning changelog_entry_missing: No changelog entry is attached to the proposed tag.
+
+### major-release-ready: approve_tag
+
+## Non-Overlap Notes
+
+This module focuses on semantic version tag governance and citation-version integrity before publication. It does not implement repository foundations, a broad integrity ledger, release readiness, DOI tombstones, restore rehearsal, access review, environment drift, merge queue governance, retention legal holds, sensitive artifact scanning, license compatibility, branch lineage, component-owner approval, or export contracts.
diff --git a/repository-version-tag-governor/reports/summary.svg b/repository-version-tag-governor/reports/summary.svg
new file mode 100644
index 00000000..0a7f63de
--- /dev/null
+++ b/repository-version-tag-governor/reports/summary.svg
@@ -0,0 +1,20 @@
+
+
diff --git a/repository-version-tag-governor/requirements-map.md b/repository-version-tag-governor/requirements-map.md
new file mode 100644
index 00000000..b8c2b81b
--- /dev/null
+++ b/repository-version-tag-governor/requirements-map.md
@@ -0,0 +1,14 @@
+# Requirements Map
+
+| Issue #10 requirement | Coverage |
+| --- | --- |
+| Semantic versioning | Validates `vX.Y.Z` and `preprint-vX.Y` formats plus monotonic version progression. |
+| Hash-based integrity | Requires manuscript, data, code, metadata, and result hash evidence where relevant. |
+| DOI assignment per tagged version | Blocks DOI and metadata DOI mismatch before publication. |
+| Auto-generated citations | Flags stale citation versions that do not match the proposed tag. |
+| Forking with attribution | Blocks derived releases that omit parent repository lineage. |
+| Version history, rollback, tagging | Detects tag reuse and collision against prior tag history. |
+
+## Non-Overlap
+
+This is not a repository foundation module, broad integrity ledger, release readiness gate, DOI tombstone gate, restore rehearsal guard, access review, environment drift check, merge queue governance workflow, retention legal hold, sensitive artifact guard, license compatibility checker, branch hypothesis lineage gate, component-owner approval guard, or API export contract. It focuses narrowly on semantic tag governance and citation-version integrity.
diff --git a/repository-version-tag-governor/sample-data.js b/repository-version-tag-governor/sample-data.js
new file mode 100644
index 00000000..96d1fbfb
--- /dev/null
+++ b/repository-version-tag-governor/sample-data.js
@@ -0,0 +1,103 @@
+"use strict";
+
+const sharedHistory = [
+ {
+ tag: "preprint-v1.9",
+ commitHash: "a100",
+ doi: "10.5555/scibase.preprint.v1.9"
+ },
+ {
+ tag: "preprint-v2.0",
+ commitHash: "b200",
+ doi: "10.5555/scibase.preprint.v2.0"
+ },
+ {
+ tag: "v1.2.0",
+ commitHash: "c300",
+ doi: "10.5555/scibase.release.v1.2.0"
+ }
+];
+
+const mixedRepositoryTagPacket = {
+ generatedAt: "2026-05-28T08:10:00Z",
+ repositoryId: "synthetic-repository-versioning",
+ proposals: [
+ {
+ id: "reused-preprint-tag",
+ releaseType: "preprint",
+ tag: "preprint-v2.0",
+ commitHash: "d400",
+ doi: "10.5555/scibase.preprint.v2.0-reused",
+ metadataVersion: "preprint-v1.9",
+ metadataDoi: "10.5555/scibase.preprint.v2.0",
+ citationVersion: "preprint-v1.9",
+ changelogEntry: false,
+ artifactHashes: {
+ manuscript: "sha256:manuscript-v20",
+ code: "sha256:code-v20",
+ metadata: "sha256:metadata-v20"
+ },
+ forkAttribution: {
+ required: true,
+ present: false
+ },
+ history: sharedHistory
+ },
+ {
+ id: "minor-release-needs-review",
+ releaseType: "release",
+ tag: "v1.3.0",
+ commitHash: "e500",
+ doi: "10.5555/scibase.release.v1.3.0",
+ metadataVersion: "v1.3.0",
+ metadataDoi: "10.5555/scibase.release.v1.3.0",
+ citationVersion: "v1.2.0",
+ changelogEntry: false,
+ artifactHashes: {
+ manuscript: "sha256:manuscript-v130",
+ data: "sha256:data-v130",
+ code: "sha256:code-v130",
+ metadata: "sha256:metadata-v130"
+ },
+ forkAttribution: {
+ required: false,
+ present: false
+ },
+ history: sharedHistory
+ },
+ {
+ id: "major-release-ready",
+ releaseType: "release",
+ tag: "v2.0.0",
+ commitHash: "f600",
+ doi: "10.5555/scibase.release.v2.0.0",
+ metadataVersion: "v2.0.0",
+ metadataDoi: "10.5555/scibase.release.v2.0.0",
+ citationVersion: "v2.0.0",
+ changelogEntry: true,
+ artifactHashes: {
+ manuscript: "sha256:manuscript-v200",
+ data: "sha256:data-v200",
+ code: "sha256:code-v200",
+ metadata: "sha256:metadata-v200",
+ results: "sha256:results-v200"
+ },
+ forkAttribution: {
+ required: true,
+ present: true
+ },
+ history: sharedHistory
+ }
+ ]
+};
+
+const readyRepositoryTagPacket = {
+ generatedAt: "2026-05-28T08:10:00Z",
+ repositoryId: "synthetic-ready-repository-versioning",
+ proposals: [mixedRepositoryTagPacket.proposals[2]]
+};
+
+module.exports = {
+ mixedRepositoryTagPacket,
+ readyRepositoryTagPacket
+};
diff --git a/repository-version-tag-governor/test.js b/repository-version-tag-governor/test.js
new file mode 100644
index 00000000..529a9f6b
--- /dev/null
+++ b/repository-version-tag-governor/test.js
@@ -0,0 +1,61 @@
+"use strict";
+
+const assert = require("assert");
+const { assessRepositoryVersionTags, parseTag } = require("./index");
+const { mixedRepositoryTagPacket, readyRepositoryTagPacket } = require("./sample-data");
+
+function testRiskyTagIsBlocked() {
+ const result = assessRepositoryVersionTags(mixedRepositoryTagPacket);
+ const risky = result.proposals.find((proposal) => proposal.proposalId === "reused-preprint-tag");
+ const codes = risky.blockers.map((blocker) => blocker.code);
+
+ assert.strictEqual(result.status, "hold_tag_publication");
+ assert.strictEqual(risky.decision, "block_release_tag");
+ assert(codes.includes("version_tag_collision"));
+ assert(codes.includes("metadata_version_drift"));
+ assert(codes.includes("doi_metadata_mismatch"));
+ assert(codes.includes("artifact_hashes_missing"));
+ assert(codes.includes("fork_attribution_missing"));
+}
+
+function testManualReviewTagWarnsOnly() {
+ const result = assessRepositoryVersionTags(mixedRepositoryTagPacket);
+ const review = result.proposals.find((proposal) => proposal.proposalId === "minor-release-needs-review");
+ const codes = review.warnings.map((warning) => warning.code);
+
+ assert.strictEqual(review.decision, "manual_review_tag");
+ assert.strictEqual(review.blockers.length, 0);
+ assert(codes.includes("citation_version_stale"));
+ assert(codes.includes("results_hash_missing"));
+ assert(codes.includes("changelog_entry_missing"));
+}
+
+function testReadyTagIsApproved() {
+ const result = assessRepositoryVersionTags(readyRepositoryTagPacket);
+ const ready = result.proposals[0];
+
+ assert.strictEqual(result.status, "ready");
+ assert.strictEqual(ready.decision, "approve_tag");
+ assert.strictEqual(ready.blockers.length, 0);
+ assert.strictEqual(ready.warnings.length, 0);
+}
+
+function testTagParsingAndDigestAreDeterministic() {
+ const first = assessRepositoryVersionTags(mixedRepositoryTagPacket);
+ const second = assessRepositoryVersionTags(mixedRepositoryTagPacket);
+
+ assert.deepStrictEqual(parseTag("v1.2.3").parts, [1, 2, 3]);
+ assert.deepStrictEqual(parseTag("preprint-v2.1").parts, [2, 1, 0]);
+ assert.match(first.auditDigest, /^[a-f0-9]{64}$/);
+ assert.strictEqual(first.auditDigest, second.auditDigest);
+}
+
+function run() {
+ testRiskyTagIsBlocked();
+ testManualReviewTagWarnsOnly();
+ testReadyTagIsApproved();
+ testTagParsingAndDigestAreDeterministic();
+ console.log("4 tests passed");
+}
+
+run();