^0qX^IZ49WI!j;6TB%sGZwvK08X&G5Nd
zC;(jM&A#Lta^Goi5i3tFtuA0qKs
z$V6)U&Vmq<5QN%+2|_HlZe9vSb|4hW=m!cYFrvXRc|-6h-)
z%5^89>b#x#knP|~sp%(v^;?vZGg;>ts;(a>9S^Fal=GHuLP;H(?$@rDo8!>jJpy2V
z4r=>+Vg2K!W!_iXuvQufwRMt>bl=l$#Eg8Qdj}z1ND#L-+D`$A%qY;gM0jm4YtvJG
z+qXP)44>O4cScOz{F_n7jlFpo6*X{RA_oJEMZSSmRe$J{Hw8qz&9W$;volGmq4Wu}
zG>T*g?G?G6kJRtAuq1}BdOn(IKyR?P@akvjMI;O<;=ONAms~mbdzjfw1tcr*aRgils8ai9%Eso9HhLyJ2OFH7jlq4FLtKmTlx;i)>WIC?
zI>Oxbu3-osi2%{fG)XxNwu-Zr=#ujIra9xjpn>(YT-m#W$wxp9k#utqWB$_GgUXaU
zIle}sW%6jz`3cN|_`ub)ZxYp$#le!w1zHS(<_CU3wNF8RHp$PApMC*YlF7V&z*U}^
z-~bHni2)R>&Y~9*?e3FR>RQ3=L&<1SFx&5oH5%xP*{SUrEzriZnk6T)F7bOMvIXd~
z%&TxJxwSCSYb}i*0K9>d7vQ8Q)jwA~KezD$7GQ4rNq`?CdHhCxt(sAo1hK7dkiv2_
zH7Lmus)0w|ENaFx=DkbOgC;U~
zyZkw)z+un84lwgjB)0f;)9j=8G681f_GeT^qoZ%^cKZeEWzL2gTwz=;Mp}hr>(t@vGRi44
zQ{&oMw5WJnL^}P01yiMeS%J+siyi!Cw&92~JS>i7b=EqOZbA>3=j+Yy$5SOfoY06R
z3zcaCl9la}mD{Riyx_CgPih|QEFSyTQ@bsu3|~~xn?|2u<5Tf5N2^LhR6>3Ypjxk}
zec|Q8K@BtfGsYhwiUdhSPzX6eH?LpW63in_x5qB?rp=_jA=_BNr
zKK!i1!$wXpZZ^0-KgR4hy(}tPV7WhKg&b@k$>L3(@?v6O%RZqQH2%RN$#0xEXTw%0
zjr6+4TD1GCht^j5HXUc#Q-N}6k`DLTX{!6{HCw)3wRX@KT1v2f>09AU32>}%kEj>Y
zh%&*+x>@oNpFXXpAk0G~SxW#QP~p9kFh)|g9r-;Efx!xP=h
zBjqX~;m?;>jd`GOO7IoC-!}$K%6jisf9H^KP0wEDxY*4@YiRgPH%CKpwpg6b2Slin(iY#`LMqOghf%Si4@J^EA3mOjG?mES$mXHYY5y8G
zc1^(9?8c%pZ(nK_I@nbo5bO#?Y+m5~enjP)sa~b4_cj1Q3oTL`%{mqy>tNJ|`;^k;
zo?A!C04~r5x$@XwM;pZNF=erj5dDQ&UCKO_j3joSRM<$MPskVAD>qNcZ=Zc&5Kb~2
z=1DArPq+NU=faMMS5ZELWi`wtnO-;fY3n6H&~sbh_U^pa#a>no{8^Y6-{~|9LOx!I
z1@#u3Jx8
z(N6TuUj-0f=w%Q`CKbCYs@E}oyRct;=Yg1I%#&(AapAUDL#<{R`*q&e2(~0v%(2T;
zX8Fq%Z?OyQ>DGOcO+Vw;hn8gv`AtuwFKa%l$2r$zf3&qL{jm6YF7spxpM@ew%<_t?
zSgeAQ;cMY#d!iEyp-$AC;)v;tr@?Lj*N^WP*ruyH7q}63EH9Bo9l~o$s4*QgNXUG;D#1;$hz`#(mn10XX>@u)_M9%r?(xUAR;8IX6SWxv_5Xju*z`h
z#-W|$>yHm_&z*i_MC8`vwr^9X>ynT9
z4R@;*^+HrO{2K-KE#L<;3d075=`=yQqw$<$xNGbHY_oXwW4dW~uT&3j<|QnSNR7!}>W*6fzfDuPl4&jpFuW@sZV=RC(f;Nl7GR&X57@y_>U
zU5>u?8@Q%q(7waZFsjG{
zvs?q!;TR1C5_ha%Z4gW}B!=^`H}rI+a^@yuTh%r08CQX42o7L8U_hQeH4-RB-I^91`P>wF6rd#HM%K%Mr`yMePIw+p{_WT~x`^_iWk6qgp!=dmo
zTB~Bb3LOnhO(@UaQzgXH38ys_4PMj=lo8D`6zi99^RIKE?rXG=FkI#i+w~`@SKP`*
z!A9HOiGeKMLR#KKCyeDRdF>R9ED7bpgu^p-XX$sP7e(;ReNcf=-)QjWYUagg{dQW{
zuAL_X163Rn3{x|2$fo8QvsU#!q?+o?{5CRfJ)w=EQpavZ`I6tNJ}guuVw~={ctwC+
zp&7j&K|W77O4Hk%Sp(&TMWw>|;b2}THhF9In~z3ZUIBh>-2;PREHC*J73qEygl~6`
z!HDd2#_h9Si7mwY*UvA9$005v@I77tkJLsGgj1}xxyg}rfJ!Qxe9eC>2-5({t
zb|OuxEK-~iId>uoc9Ky9zZ70@kZkG2=>tf%1Ew6a^Gs2?d*nZGq6g_yWrVPt+ltN|
zp+7>Ad&SPB(&Qox>PCu93qfl|x!Ct>?eLgydiJz)?Iz)shw8`nkA`&G<>V1;K;C%!
zR_~loB6;I3-BYu8_|BbCIu%n04HP&)PLUZR=eY;R`((??VR`zWBa^b-i0#t8P;_yb
zW8WmB1G3)z0Q~j`IbsBsVc;a%4gkO>U*YyIag%-Hy&djiKUd=*(t0ZF=WnRgt*k8r
z+#7cBpx;Nc!9P9I2tm|@x$zmhU!#bYPCA@=D>+Fi<27Un@M*3<##yUvNe1$_Y_+gC
z8~K4wDM0{Wxj@bMfMpn2zBdDEch!bI$+y^H$PgB5-jqCend`x1#-=XbKi)$gH9hJpzyN>r3Q9!4kz-EP0&At=}o2SUYlD++%xq>M4
zZyKVcIJuc-2VT`tlI66$|V1X=lU_454NTm%5ajfeb*XPD=zP$TJal9qcHe>n9p
z_%fqfA4(vQ(pTBrL_Yh1BY2mbMkf~Xc^@_rOU?(^vFVW;!P?XaaImcy{Ub})Wx*1Y
z^ENGgh|T_ZbLvdpo_;F}DA8Zab#CsrnmIZzr_4s>)XX$Ib)d|qu%{@9E@|DjN}%4-
znnWmQkE$EoS>u7(Fiw+ra4#(jy`1Gz|FT@%>em>E8oZ8@&3j;Es@z8O?0uon1%K2K
z5G6k(&ND(zcmk!Bh`-}zRae_1ixs6QYPv?OQ1;
z=PMy}1M#W;0BpKeOOJJ=DKOQ77z3BPQWA-`x3}?@&g{|=&>FfJ*~Pv}L=r2=1<#T)
zNf7$lEAn6zS$}YEzq~?8Bm_)tIQ2@lK-DD~+H7By7kTcRsr3@2u3%)Ux-_@JjCh+C
zB@+PR)nKb@Um5_0TDD8b3_&A>)~=?w(e9K$!e_>V?6O9QP!QZjp_6Q8#4>(^7QZ#7
zACV+GXO3~1{{z-gSVz460y3C}VlD7JPBibw&!U7gg2YkjctL{^6t=45yd95d*pt*8
z(}%U%+D%!a^Pfp5D4Nqt`BXv^6L|pbqX|Xz9hc0>!_g0m(sQkSJ@#RJNQOuBY)yko
z^AcZqnmW*YZT9Dkmuc~mYG04g`r1SWGKsN}Xdo^+2!gx&1!2F-7M7&~D%6L~eYmVq
ze%BgdG-@Wof7X9D06qm|k|R_D^H
zAB&rpe$gwem{ge3ou6V&w$wCg*IzfNK%POT3a1u=x~z4I42BHqfAv`fDFsRJ-eoK|
z4%4(ZTta;25FevO(&UFFajguZ7aXu}B=@3QSca_LtH#K&D`Vq%qZtXuJZ~Mz!i_+%
zlzXPF^~Q2dp_o9|97Yi%*Ge=uA->OX1@S(J{AD+yUAV}M^NqI2!lgXp>KQuD+>C22
zgJn-ojgHRe7cxma#i8FrrE$3Xb{>kd92EDUGA!Gz-URm1Pu#7B7P;f*xXRxQ=!os%
zq|I@u`k(dK0E0Vpce>23n21UCWaH%ar%siOqgxTAq785h@qHNdGKW$1b4cJ
zd`$fYZ`%Cf(Y4tjM!VHjFl&K80>bY%yf&
z^)#@DU4nlng=YXAA~X0Tu(h_uq)pXQ4koKW3&!Qc3}Xrx{^?_4URAG6T&(NI-%<;`
z6ZJ@-oIDJc4i8g}e|_0EP~p
z@O*-(vtbu=C|xQev3zgntj|*~w?B%;KGe-S?unNu+3)10Ln}H5*td8a{RC$+_VRnL+tzTNAIZGEimRuVRPRiR8z@_
zkxo|J-;UNVEtWG^jDG?jmM{Z~q2u>nfvpS7QYXFR`!S}XUcxU=s>K-5KP!W~t3)){
z^D_b9UIUWT&{{RwK(bTDiTMOwS$qbAv)~;Q!>(h@R?q7L+0WI@(tIjncd&SIo6OnP
zUr#vjeSdQMs%X<69!^<*+R=lcOfh9a7f&G$a~auY`n9ujv`=Oe|Dk9AYGXK?wL$8X
zR7r|WJuf8SPf{-I!@-n#Mzotg&hLDIfv{#2tWxRjabKL>U%k~y&*#m$ecRzuZ@VjG
zJnINes6JY_3dqXkT(wlud%OhYG`ot8a(-!Q6A+t})>2ZzM>;ANXZQ%;OD&WH!h#4(
z=^Z1e(zDLy&2jT0a$-f;k&~Qwy>r%!4s{5Vu1ce7BiOn315P?{RhxbVSUzXj`cx=K
z-V^4r&-M^jn-X9~a0e$tY#J^^fWJ_ic144Z=RyFytJmmd)B_nI%4uoO42xL@RqbO|
z^e}zKz#5!mjb5}j)EtOB8?Z({H|TiHInv_N+WYjCYhWxir;CLrJd~{tr>8DWxD^2ZW-|+*9DLq`KvbS>
zNG0IcMBrFi*F~vG@!_(0Q{m>rPKFKG`VmWi)!1)c0M2@yeK
zr0dJ!F-ir}cpHT9-Yaa+k5aD#9uulE0{c@m6^q`=eW6});NO1?$tYWmwBfcMP+!%7Rf>DL7{?mJ@*pfw7^EY(&MmKs
zEMC8B1_h_wByQ|rI}7#J$+tup;{XeuVIs9j+Pi70(gB1naIt(N{B6Git4qi6TBi=0
z3WIxE4g?n07y;?g$8&7?kK8ZupVJT;_{xockcfWugZ6;Jo2d5;*x~Zl;mP5-y=`wb
zNHUU4oQ9`4&WY%ZuHuJ!e7@?lC(=mL=^h*=|M+7b=GPC6yDi|a3R*Rq<|xYN>NGOv
z=fr(NT3O5#rzpOh&o3@tHg>JzB>BOXEwD!=XdLAk5vO`G
z1RFTdxjydJ$CJr_@Y))#vqZq4tcl2`1*K{QCB?f=%jw87aHr783f?erNq4`jjpX?*
z0FcixE}s_=c&&xS8!<_9G;Qr!#n&j1)xrvyx^bRGwqDW_XO3U|Bq%+_Rur@;+W1
zU(QMK&vhoIvsnp?HpV@^Ti3|?YH=L`dJt@^vlwIaxfd&N_qR+uL9IU%d(UBM)atIf@
zQncnz1&?_Z$=n_zvth7_Hu|hRWJNVGdPE-g
zI~IiLu}HkPVndMAkql{APHCf);vmOf#*OaNU5&6Stf$BXhsEm(x^CkJ@oUU=(?&)Y
zT!S!;gQk(_jT`g5T~EJ7D&>r~))sMI*!o%}p|Fqk-%Ve8v&!Mn;>apab{7VtcLjW5
zc?|x7<@4ja<-zyaE-Kn594?^lGPdydmY=IWBul78yKHdD-cb#I-|0x8i7YPt3(j+hTX#7s-Ryv%})8{so94X~7Bjz=Jgb!$}gkqz4{
zsddvD@b<3}lMRB0@}(et1L_R6FBfU1^iU;JNC8T#<9`RLSy381c(WF-E#;)LC9l8z
zpjlfD`#pMAhW%i@G#{AH_9s{2*B*v+llPuB$*`dXVN}agLx6B|$oIp&?zv48oNLVB
zz>*hNjSS=C!5^N_G9;QkI?!QW9=^3{C$*=qTvsSEiFm0-+z`VM=>SN3g$U6AFuWC6
zKD7E=0-ah|N#z?t=aitx2qp&oy5}kbJ~c;7A>nGb#jlJKr30UmN?}-e^znt*`)2ws
z@eHp-g?Fl`?WmQuLyr97p-?LfQZYs%sHEv_;H}(QjA=5IZe*RM+K)(Bj?gxcY}7r9
zzRrH-7Y`ny8#yz~a$5Q29wq8as4f%^^^pLf9PQYshwj_Qw!mnl?-_=NK?chG{O4jz
zYkMP9N>TLv*7$cg?8xO?as7-6Z{`VE0xhukc!O4cP4eF
zloA+ULnmt~
z{S=W-7aNIEJJl83TJC?6v{%h-ShdjAJmC;25^u|cX=!7~pXXYUSORNL;&Lv67XO1+
z`xX0lWoIK33Eo~A}<{S7n;l;m4Ifkvmx2Pj=d74B@tlrMDLg34pgb-VjoqwyI
zoX^rp<~zQ6)nG{w!{$dK8L~5p&>)^Ig#SD(R+H_ZpZtv*A@NTZZ6K{xS&@(AoIQ8_)2GWoCj&DS-MJj)Xon;#}r#hsy7R2HvRrpF1
zlc02rT#Q^O`rGfX*}(u_MjLX7BL!9H0?!1Lr&ed>4y5m(f1p7CAfOWA+GKLIE)v2$
z7~0>uBX`Kn8;BW}C}i^;?hYJNp%M6wMQaOF%ukd$K7>pY^Rk6J%!g9m%37mDP}*Ve
zv^HDUXh&h@XL;dLQ%ipGSJ-1l8{(E;b|MIqXnxaHa`uYI#FPdjMvS56ml1~MQoP{(-O
zQ7&49w;#CFkqx$Yqu&Xb$ZT?ro$~XJN$=u2{aIne9E_)Ttp*h6YUJ^TKWuO#KT))S
z1pPB>?2m-?Zh`4+2|Bl=`VL(@e6k{i5o^Ou)y5Iy6q}=OJ7Jznre<
zbL;KimWHM|Dy_%=gDDX=sE|eEvMhd6yBGB5g?DL$LXfnSa2n(P+DRaMOUsHuP@G_m
z4gB)hQQJMR8DJQt47p*s`CAF7BCh+TV_1j%w)@2%R=u%(NEGc|8!~9E2z4TyrtrUB
z2ntf5v2&KR!R$IKL73bT@z-QH-Zu@J62Twm(*1abz3FZVNVa
zwKIWaMW77yj8!u}Hk$oIv-x8IMv?-V(`de*(8R~0$L@<{RAhI%vqtd2ouK)Qf6<5j
z+v0??G`<3(Szx3ok`9lfm($zvQ`~C8Ols~-{mKgpx&C<77?|RtJ
zGx|`TxQ6IBquMY^N33~IZ?m;m>?3*44>fVx84n2MWvpu-5jOC)DyhTML|
z0?5L0h@Jb_Pj*UY>GN(BkNdcMU1_@FZ|@2YCWt?1b;5vO9%^`JL^j*K-l>YChG2@_oK(Qw@aBRlmZ@Wu0b|y%
zLzo~_;WP(kK}{!aJ(gd)z7!WT61v@Q%vo2|nA}aJPTwVTk6T;PpiR+hWP@?l^C`8H
zof?MBk7uW_d69)HLfk(&ln#|W17AiZz;NgA&_S4itX0)pY|l1ZvFf<$**|&p0BkJ>
zxjRN|esNSm;64J=O|E#RhTfSI$6-^U3H6jc!!!ttz&
z4^MY}W#wxkaPG9aXTcj9*Y5)Bg7=&5s1uDK^>4g*aV=xWIAloT>p!`14GaPN*&$r~
zieCS4)0l53<{`&~N$Lwa?XK$0Pr4=Uk`k(bH}bkzLC*q_?`
zTE!*WqoTerxpi#e99Bb?|8ky$2uaucfvP#&oY_%HH-HN@@`{Nv_4q}hfyTwvpTqhi
z&P{57E7I+3R_#TEULqg3GWenjLEr@M0U^CjisFJPB@y}EGs2QyXva`**d4B%w=$t`
z0W8vnT}A+rUjC;Ga|Vr*;S0)ZtCxvZ5=r(qHLX1DLSj|u)t`=f3~P;X9E^G*sXb~r
zoaKs=f=}dBL~oHwEXr`n{7`5)t!KU%r%;A$reitIw{BqU{2
zq&PhsES6FK{KGH!v$%K$S*rzvYu;))CX;$8OvBJKQx@*RMvBEYoHGv3P@4`Kd9_5w
zlOr-yqpG}9w?i%IhhmMkrt~g=3wRkDa$i70e79;J(KFU)WVRDNQ#gb&s-iANJ*?3h
zf}M=ct+$j#_;-j`K3XBrlLO+>3R_+{(?xEtLSQmur>_1YZ$9;7c|!aowcoxT8AQ4t
z2mifB$v3nG7-?aDH5P0FpBQElHwFb7euT@wWkl|46*Nf6B4}y!9Aly;K4r<8hu%xA
zCqmu>7&upoD_md?(l_^vSvgj@QBTQI;#qFUrc#Zii0GvS_d|@*f^fcGZ41IrZ}}J8
z<-htBnPXAR=o?j*HF>oB#9GtK9vS-CvcoUHkbB^9XM|NQfoIV7ioXK9iWtt_M%u`5Yr-=qKt}r_uaY>rKXk%l&SAHsfm%y{3tMdV$(wxu
z542jwt=2%fBldyNzNv7Ho?=paT59uB!Cx7Y9Mxh6?exM$@NIHD$Yz{r;YqKWU-V7S
zN%ADo;8g8ZX4cgN4CUk5Cy4`3`J&KVI|vcY$-)d{NEU|kZhc{>i=;h{Y%m%17^=hS
zGwTp$T|R#G&uoJg?0@SNOCV;8K~BzU)jO7lq6m%HKVEIltQ&ZL;8SOUuC~Hyj{lyc
z2>!DVE$MTmR}Edbc48T7^miXxG+hNWKQU1TIO;wbaAqsKgg4cBG~8&E)NT5K4q&8^
zF5@pe!vZfpbmz;mr-a?QsYWRGjxSHTga%-bTJco+w+^K4j{
zDDY|vp`BXJ&$1^U8w0@bJ;PFG!wLug=xxJFbas*oQuCmq&5iqaJLiuWz%WR`5|wy@
zDY>U^@<6^W!@98l9C`{vaa`_KBaY+DHuePfbBt^QR_oC22_M903u#4MT@*ba3ac^S
zB)g|FFTb&l>yKzVwd>Cxw%CV5@9eIcwSp0;V-(2MGax9uR5+dauY=
zl_5Xf75LA75B(Al-Ninp%vmPY(H%UzqwO~@YS
zG-qhh_?Xe+AjM-2qZysmxpu4j)vd+C_(KuQC2X2GLmQuiS(jWfM83LrUa<+#1+0~)
zWfZt+LrddL6l9+A-q>ZpmtORLiaEL<7|LGi*xC>Fd6PQ)iXUy_YqE^KjN5wWA4$`o
zWjc5P;bQ&VZn?)#j{eCQ$ix4kRMGv_Zj@tFC#K$C7!b=d{_*yh#u)WmBRl+ca{J7T
zN}12c#9T?r)5a$By@4jaqxO7Ks%H)A*u$JTB_AMCEvHAs+`XSPJGQHW>7`Pa+A+m>
zp@0Q%v>6kd2ebUdZ;X14B#h1&8FIa!c#h*94X<(In$
z{MO4sConV12mQz0_M!KldBld?>S14&6&5TX%I7e15*}>jJ9`^=&0>RT6O#h=c|m}l
zy~JfXHH{F>pTkEl1E18Ai7a*0@&rXH(t@4-bpSZ%6BOuU-LDwX4D?@L2*7aO_bQ6=
z{s5?~IeeC&oTT5xhNwF-{5{O`07OOksg1Sj9c01q0uI8jFj=XuyW)`S-o*#TI16h-
zA*b?8zqI`uaHMo0yj(O7{WMPo`tH;TY=Bu3NmQHqB8`nH&z(WA&vOtc+fc>iaXWn{kj`+Ui(g8>*H9MS9lSnrf7l{|S
zyR)gN2dB9V?&@p-P%*oNL|<_Ed^v76o<|3bBDi5oeL4#%|YdE|0hQEe^UNWBUQy|AT9sUw^KhQD9a&NQq
z{oN*jf)HrUMJt?E^S`FU%)kD%qhUI?es@DMPjJ)Knk8Pc*g9>zmF7hU=LbfYBc6yO
zv$b7z4ZJdV!~F5ak+qfa8W)u|?{UF8i#}lFHq@L~vPY!5<45bUNb!3dJGbfx7tMEK
z3CEG3@yijH|K2v*>Fxw;)Ihy(&F`VCXgU
zUZjH{U78PpP=p8w5BdC^@16Jl-m~}2**!CJXYTIcmGMJCo>DwsFO`;!4qxQBKCida
z{yd)ES4Fyq*1nD2a1=LB@aRs9@*BD#j`%^sPS#~y>$+f6b&fA`4>{i!v;)DwoExN_qQ~_{25|<&6ExFr4@?1TP2Lzu)TQY`X{dd+1A?
ze`#bK8_iBos$QZT1uo&XrvvNO|1z{bI+r6YV90t;|LsI=_w>%)R*&kPu7HFVl8_>!
zH6$z0VsYch_lOR!rM-wN37*WrmOVO7=F*H;aF!^~Y7eW@Hd>DuN5*oKs)zUi5$>4A
zV4-iPw^9{wrWyu(q)@m~fq}6B;$2QIF0bdyng7ZFy?KArk~=M%HRk~SUYUx?%bD_C
zP6tZM7)#W3fo
zOb+=+6yhkB}I<-rP^T-v4k3Yn;XY
zrP{W!_!a4o?ujjNq3c36PIfk(8$#_XpxuX$FT(iN-4}NMziu6-^-Xl_6@t
zZ2EuPJn@fwIF(}o(44MjyBN}%&D6hBFHfR3JHcC`t0NaQOepAO_PyB25Tv5o`32~V){16B&=z)%42Xm^A>{W+-k$$EM23UZ+fQT
z+&(W(XJ3J`c1YsHJ5Ejwc8T{`fwj_CW9qx%;61uB3XCRU1M?Qm`KLe{P~wGgJuOyd
zvGZf0;pl#9xsEVyx&iXb&g*Gp?3A;5C6>u|fOAF0eADC}n;%~WKq~?Oq<6F3QpwQ}
znAe(sb|19^J^OoEN&728y@_Dy+TRI{?dEO74a6911YORL?^~V2Fa(Q|FV6F9PufVe
zWb`qN%RkA9i#etD2!cjKr3W^tqL@WN4p5739`fbcHdViXI0=Nmx*~3pQ&+z*)+I_+
zTlHoNn5Q?TMjz{+D4I0{Dxr|o`_ZisByB1g7J2%wp#pyIu-CVqawk1!?qFE$I1toF
z6nzyw+4aiNgxEf&ZC;9ZDI`fltc(=45!T>fEwB~w
zV1!2&e!5F5Y}Xvj#I-8QKYFO6{1xzE47&J1Da44WD28zx=0&eR+3DMu{tQ9dw?7}a
z@Pz>tNv~keSREs;Y|#TkskKD)>IHUO3sL^{q0F?vcu1Gi{0_dmA{JY4f1{+ttU;CH
z2n)92-lIr;EuhIxzfRSBuY|C)FKi&Wcx&1+$LrZ>-KPtpT#GQ}{h54V>nnm5do|et
z68EKy;mr)c?_GF_@nabW>gFzJ{D-!KX>)lZ^%d;;5i;3EZ>QVABvOhLP4&R!7;{&p
zSC9&rMfuMk10v2xVgY5?f?5gE&Jd-_<^v4ZcG`AaVWXJXA^)r8tEX;6j7+aUoBMkr
zH>wE`3VI~6%(%kTm@%^+cmQ2P*!oveJUTx<*(RmQU-~a^&2az)@Ku6RODqprWoT2f
zDPN!j7$AwpgIFq<%xlHEstGhLSLj$8tYJGoxxj@e8zc>U&}p9RnEYiUgK&E_%|DWC
zoEwlYtYb6Lho^U@s*esmCnQ@=jFx$wRp!wAbA;D=?l5$wCeVnXU6O?{$kEN+d!iR1
zWOd1~tK_0U_8oY{X?;jT$<+XRa#Xtm?-=xuh2LMDqd_q{8*A`3_>vEz}-+n0?
z$&oy54{IU7w+|$`JZL?;uo~y@qm^ONC-leDs`FMAqfo*Wb*CAfJ-nGkctb^W2Ra*j
z=JR9~=}thHbHQ%I>YLc8?MSLO6=?tX=Mi{~RJ|otgp+f7qNSdCi^&)Ed0)PkaaQhd
z?EB^tW{fQ~eX+y}sUsgTjNN%dDU&z~`8{n>*VUc5q30U4F1lUkuZr!-z(#g*
z*iieBz@N^oyh%8WMw41SERHpp7S22_IL`AP&OO#4T49*-{sEy$9J~%b_<)S7@IzRs
zkS_Z--61ajY01X@79{d^n*WcMY^MCeNN!UUaK;g{tuxElMiXn&6O$LhOU}{}{f-OKUC%%0>!0M`m3x*p@>|V{^&Zxs;^aw!mpK)j
zCx1z)!t&Of`zozN{9AtJHkmxSNS6z|7e4LMFTlBQ(|H$@TAuUj3BkGV<^oEE+81J4
zFs4KMz4gx`)Pkm3jR>-sjg*W|1VnuXO)bj|@xn59PnW8M;zOAnc1>TUE6IuJXG?q>
zcMOGgxjHqXVc>LT7<0)*&C}RLOy<}0`c)Oy2slhMqsy$}Cfa)cVkH9J+r{bXmZ|2rVGx;RW}-ke+yXfBmNQniMw&DxcQxE^?{IE
zE|lraD-}Lu!5e(<2Ipy!y*(>(GL~a7zgwBvax&Ehb9gsJV+88|h%D^?Tq8W*>&Qdmcv!Eu6qNpee)iMR)IJoPy
z!z~^Vv3QDupJn23TD!3wDcX!IWhL@KVsP+44TEDXd8$&*ge+)o969WCs)Y3mfy{Xl
z-h(8Ta8~0`3&_|T57-)MX_R^S(V$%@Z(4$=E!$dzU44O&HK=96#vCq{Zi^6
zo7JqKCY&Tg>7H#^eUXtNgBw%N{(ybDHICBD)PPqD5m7~3FKTB)xAns%i@0TRma)zl
zWS`(w%eMm_Tp!(>3WTiJv%ibgk<)yEEGbD?g$s0J3f5-#-@YoT>wtBrm&Qx7680xN
z@)_7K?y04JN=LXVO-sOC3J)eQ+sz3-<0t5&QybHnX(!{b!M@XBP?oq
z(SW)0Y|Rb=+0>gtp
zzWF9LzLPiDHj{gwrnUwb-Oi__il}EG7bvZb^-uE9zY|X#;+20;8g;jTZi1>AOseNO
zP*n9s)lejvj#taSUucR3eVwLaX
zAKO@pGBpl=y%#;SIn_Eqc!6h+P9r&|iFl#NU|6|QrZhyZ;{(}>Y@cMlkdpFRQ+-Pf
z{q0PAC9!3{dADIi@3No9*S#T{5)1G{^I;@U=r}5vIh;DJw_>e0g~()WL$Lql4*PWD
zZbz74>m!nzOY17$6EAsiZaio}8S@%d9KbT@<&CR>Pi!kc?^34UI}0)_u+}{y5Iy4P+
zt*NrL_*#U(iEu+(xe|thih;@at?4RS3cLC|N$SxpgPWm}!cZ<0u8l
z-zA0W195L({xgn1oxR>AZyT!6aEFUgLXI`u|HW-4=W8mQ{p5-Y|J7{zHeB}pXf_EJ
zmz!BE``af`NuH(jLnuI98J+9z&s1?!E2kULMUl;SH3-nCJ51+ZhFi_2Io5G4
z=AH0a@{9aW!wwVb=On0&onaq#jUL@f1x`?$oWP3WSfJmHN&cS4HX*y~-CRB;1OW}#
z%`(D;D?`M9+1`I;9w6h36N*WpMVsiil9gnMMRD?<1)vx1*Eo5%x2`M?>?zvS6ovDBM1xxE
zMvr>EZRQdFK
ztbqFp*;)Vp?6K)jwbC25qvPN_&Ch!78dPX!;XQD9o)F#?*-`7pznd*ghv)!#e_yH<
x`h!7~e+3}~%x?MTjIPU_D*(NLA~mQ2bo*Vvb2JIi^J#vo3`HWz^`5A}e*;y5$iM&q
literal 0
HcmV?d00001
diff --git a/geospatial-spatial-autocorrelation-assistant/reports/manifest.json b/geospatial-spatial-autocorrelation-assistant/reports/manifest.json
new file mode 100644
index 00000000..c0dd10c0
--- /dev/null
+++ b/geospatial-spatial-autocorrelation-assistant/reports/manifest.json
@@ -0,0 +1,14 @@
+{
+ "generatedAt": "2026-06-01T10:30:00.000Z",
+ "artifacts": [
+ "risky-audit.json",
+ "clean-audit.json",
+ "risky-review.md",
+ "summary.svg",
+ "demo.mp4"
+ ],
+ "riskyStatus": "HOLD",
+ "cleanStatus": "READY",
+ "riskyFingerprint": "e036107e72f70a7e",
+ "cleanFingerprint": "aa2c187bd4b36628"
+}
diff --git a/geospatial-spatial-autocorrelation-assistant/reports/risky-audit.json b/geospatial-spatial-autocorrelation-assistant/reports/risky-audit.json
new file mode 100644
index 00000000..0ea41478
--- /dev/null
+++ b/geospatial-spatial-autocorrelation-assistant/reports/risky-audit.json
@@ -0,0 +1,285 @@
+{
+ "generatedAt": "2026-06-01T10:30:00.000Z",
+ "status": "HOLD",
+ "summary": "HOLD: 1 manuscript(s) produced 13 finding(s): 2 critical, 8 high, 3 warning, and 2 research gap prompt(s).",
+ "findingCounts": {
+ "critical": 2,
+ "high": 8,
+ "warning": 3
+ },
+ "findings": [
+ {
+ "code": "SPATIAL_SPLIT_LEAKAGE",
+ "severity": "critical",
+ "message": "urban-heat-random-split/rf-heat-risk has train/test samples only 0.6 km apart without spatial blocking.",
+ "evidence": "Policy requires at least 35 km or explicit spatial block validation.",
+ "path": "manuscripts[0].models[0].splitStrategy",
+ "remediation": "Use spatial block, leave-site-out, or regional holdout validation and regenerate performance claims.",
+ "owner": "model reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ },
+ {
+ "code": "TEST_SET_TUNING",
+ "severity": "critical",
+ "message": "urban-heat-random-split/rf-heat-risk tunes model choices on the test/holdout set.",
+ "evidence": "Reviewer-facing performance claims require a locked final test set.",
+ "path": "manuscripts[0].models[0].hyperparameterTunedOn",
+ "remediation": "Move tuning to inner validation folds and rerun the locked test set once.",
+ "owner": "model reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ },
+ {
+ "code": "DATA_MANIFEST_MISSING",
+ "severity": "high",
+ "message": "urban-heat-random-split is missing reproducibility artifact dataManifest.",
+ "evidence": "Geospatial results depend on data, code, and environment parity.",
+ "path": "manuscripts[0].reproducibilityArtifacts.dataManifest",
+ "remediation": "Attach a data manifest with sample ids, coordinates, split labels, and hashes.",
+ "owner": "reproducibility reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null
+ },
+ {
+ "code": "ENVIRONMENT_SPEC_MISSING",
+ "severity": "high",
+ "message": "urban-heat-random-split is missing reproducibility artifact environmentSpec.",
+ "evidence": "Geospatial results depend on data, code, and environment parity.",
+ "path": "manuscripts[0].reproducibilityArtifacts.environmentSpec",
+ "remediation": "Attach a pinned environment or container digest for spatial libraries.",
+ "owner": "reproducibility reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null
+ },
+ {
+ "code": "FULL_DATASET_PREPROCESSING_LEAKAGE",
+ "severity": "high",
+ "message": "urban-heat-random-split/rf-heat-risk fits spatial preprocessing on the full dataset.",
+ "evidence": "Raster normalization, imputation, or feature selection must be learned inside each training fold.",
+ "path": "manuscripts[0].models[0].preprocessingFitScope",
+ "remediation": "Refit preprocessing inside training folds and attach fold-specific transformation hashes.",
+ "owner": "reproducibility reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ },
+ {
+ "code": "HIGH_SPATIAL_AUTOCORRELATION_RANDOM_SPLIT",
+ "severity": "high",
+ "message": "urban-heat-random-split/rf-heat-risk reports Moran's I 0.62 with a random split.",
+ "evidence": "High spatial autocorrelation inflates random train/test validation.",
+ "path": "manuscripts[0].models[0].moransI",
+ "remediation": "Run spatial block cross-validation or leave-region-out validation before presenting performance as reviewer-ready.",
+ "owner": "spatial statistics reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ },
+ {
+ "code": "MISSING_CRS_EVIDENCE",
+ "severity": "high",
+ "message": "urban-heat-random-split does not declare a coordinate reference system.",
+ "evidence": "Spatial distances, joins, and raster overlays cannot be reviewed without CRS evidence.",
+ "path": "manuscripts[0].spatialDesign.crs",
+ "remediation": "Declare the source CRS/EPSG code and any analysis projection used for distance or area operations.",
+ "owner": "geospatial methods reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null
+ },
+ {
+ "code": "MISSING_EXTERNAL_SPATIAL_VALIDATION",
+ "severity": "high",
+ "message": "urban-heat-random-split/rf-heat-risk lacks external spatial validation for broader deployment claims.",
+ "evidence": "Broad geographic or deployment claims should be checked outside the training geography.",
+ "path": "manuscripts[0].models[0].externalValidationSites",
+ "remediation": "Add an out-of-region validation site or limit the manuscript claim to the sampled geography.",
+ "owner": "methods reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ },
+ {
+ "code": "OVERBROAD_GEOGRAPHIC_CLAIM",
+ "severity": "high",
+ "message": "urban-heat-random-split makes a broad geographic claim with only 1 observed region(s).",
+ "evidence": "The model generalizes across continental urban heat islands.",
+ "path": "manuscripts[0].claims[0]",
+ "remediation": "Limit the claim to sampled regions or add external validation sites covering the claimed geography.",
+ "owner": "methods reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null
+ },
+ {
+ "code": "SENSITIVE_COORDINATE_OVERPRECISION",
+ "severity": "high",
+ "message": "urban-heat-random-split exposes sensitive locations at 6 decimal places.",
+ "evidence": "Human-subject or protected-species locations should be generalized before reviewer packets or public summaries.",
+ "path": "manuscripts[0].spatialDesign.coordinatePrecisionDecimals",
+ "remediation": "Round or jitter coordinates to 4 decimals or provide an approved restricted-location access path.",
+ "owner": "privacy reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null
+ },
+ {
+ "code": "COVARIATE_SOURCE_MISSING",
+ "severity": "warning",
+ "message": "urban-heat-random-split/rf-heat-risk covariate NDVI has no source citation or artifact id.",
+ "evidence": "Raster/vector covariates should be traceable for reproducibility and recency review.",
+ "path": "manuscripts[0].models[0].covariates[0].source",
+ "remediation": "Attach a source DOI, artifact id, or repository path for each spatial covariate.",
+ "owner": "data steward",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ },
+ {
+ "code": "SPATIAL_BLOCK_MAP_MISSING",
+ "severity": "warning",
+ "message": "urban-heat-random-split has spatial validation claims without a block map artifact.",
+ "evidence": "Reviewers need the held-out geometry or block map to audit leakage.",
+ "path": "manuscripts[0].reproducibilityArtifacts.spatialBlockMap",
+ "remediation": "Attach a block-map artifact id, geometry hash, or leave-site-out manifest.",
+ "owner": "geospatial methods reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null
+ },
+ {
+ "code": "STALE_COVARIATE_WINDOW",
+ "severity": "warning",
+ "message": "urban-heat-random-split/rf-heat-risk covariate NDVI spans 540 acquisition days.",
+ "evidence": "Long covariate windows can hide temporal drift in geospatial models.",
+ "path": "manuscripts[0].models[0].covariates[0].acquisitionWindowDays",
+ "remediation": "Use period-matched covariates or report temporal-drift sensitivity checks.",
+ "owner": "methods reviewer",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk"
+ }
+ ],
+ "reviewDecisions": [
+ {
+ "manuscriptId": "urban-heat-random-split",
+ "decision": "HOLD",
+ "reasonCodes": [
+ "SPATIAL_SPLIT_LEAKAGE",
+ "TEST_SET_TUNING",
+ "DATA_MANIFEST_MISSING",
+ "ENVIRONMENT_SPEC_MISSING",
+ "FULL_DATASET_PREPROCESSING_LEAKAGE",
+ "HIGH_SPATIAL_AUTOCORRELATION_RANDOM_SPLIT",
+ "MISSING_CRS_EVIDENCE",
+ "MISSING_EXTERNAL_SPATIAL_VALIDATION",
+ "OVERBROAD_GEOGRAPHIC_CLAIM",
+ "SENSITIVE_COORDINATE_OVERPRECISION",
+ "COVARIATE_SOURCE_MISSING",
+ "SPATIAL_BLOCK_MAP_MISSING",
+ "STALE_COVARIATE_WINDOW"
+ ],
+ "reproducibilityScore": 0
+ }
+ ],
+ "researchGapOpportunities": [
+ {
+ "id": "urban-heat-random-split-regional-replication",
+ "title": "Prioritize out-of-region replication before broad geographic claims",
+ "rationale": "urban-heat-random-split samples 1 region(s), below the 3-region policy for broad claims.",
+ "firstAction": "Recruit or simulate a holdout site in the least represented claimed region."
+ },
+ {
+ "id": "urban-heat-random-split-spatial-validation-gap",
+ "title": "Add spatial block validation benchmark",
+ "rationale": "High autocorrelation with random validation means reported accuracy may be optimistic.",
+ "firstAction": "Create a leave-region-out benchmark and compare it to the random split baseline."
+ }
+ ],
+ "remediationActions": [
+ {
+ "code": "SPATIAL_SPLIT_LEAKAGE",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "model reviewer",
+ "action": "Use spatial block, leave-site-out, or regional holdout validation and regenerate performance claims."
+ },
+ {
+ "code": "TEST_SET_TUNING",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "model reviewer",
+ "action": "Move tuning to inner validation folds and rerun the locked test set once."
+ },
+ {
+ "code": "DATA_MANIFEST_MISSING",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null,
+ "owner": "reproducibility reviewer",
+ "action": "Attach a data manifest with sample ids, coordinates, split labels, and hashes."
+ },
+ {
+ "code": "ENVIRONMENT_SPEC_MISSING",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null,
+ "owner": "reproducibility reviewer",
+ "action": "Attach a pinned environment or container digest for spatial libraries."
+ },
+ {
+ "code": "FULL_DATASET_PREPROCESSING_LEAKAGE",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "reproducibility reviewer",
+ "action": "Refit preprocessing inside training folds and attach fold-specific transformation hashes."
+ },
+ {
+ "code": "HIGH_SPATIAL_AUTOCORRELATION_RANDOM_SPLIT",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "spatial statistics reviewer",
+ "action": "Run spatial block cross-validation or leave-region-out validation before presenting performance as reviewer-ready."
+ },
+ {
+ "code": "MISSING_CRS_EVIDENCE",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null,
+ "owner": "geospatial methods reviewer",
+ "action": "Declare the source CRS/EPSG code and any analysis projection used for distance or area operations."
+ },
+ {
+ "code": "MISSING_EXTERNAL_SPATIAL_VALIDATION",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "methods reviewer",
+ "action": "Add an out-of-region validation site or limit the manuscript claim to the sampled geography."
+ },
+ {
+ "code": "OVERBROAD_GEOGRAPHIC_CLAIM",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null,
+ "owner": "methods reviewer",
+ "action": "Limit the claim to sampled regions or add external validation sites covering the claimed geography."
+ },
+ {
+ "code": "SENSITIVE_COORDINATE_OVERPRECISION",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null,
+ "owner": "privacy reviewer",
+ "action": "Round or jitter coordinates to 4 decimals or provide an approved restricted-location access path."
+ },
+ {
+ "code": "COVARIATE_SOURCE_MISSING",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "data steward",
+ "action": "Attach a source DOI, artifact id, or repository path for each spatial covariate."
+ },
+ {
+ "code": "SPATIAL_BLOCK_MAP_MISSING",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": null,
+ "owner": "geospatial methods reviewer",
+ "action": "Attach a block-map artifact id, geometry hash, or leave-site-out manifest."
+ },
+ {
+ "code": "STALE_COVARIATE_WINDOW",
+ "manuscriptId": "urban-heat-random-split",
+ "modelId": "rf-heat-risk",
+ "owner": "methods reviewer",
+ "action": "Use period-matched covariates or report temporal-drift sensitivity checks."
+ }
+ ],
+ "fingerprint": "e036107e72f70a7e"
+}
diff --git a/geospatial-spatial-autocorrelation-assistant/reports/risky-review.md b/geospatial-spatial-autocorrelation-assistant/reports/risky-review.md
new file mode 100644
index 00000000..e4579b20
--- /dev/null
+++ b/geospatial-spatial-autocorrelation-assistant/reports/risky-review.md
@@ -0,0 +1,64 @@
+# Geospatial Spatial-Autocorrelation Review Assistant
+
+Packet: geo-review-risky-2026-06
+Status: HOLD
+Fingerprint: e036107e72f70a7e
+
+## Summary
+
+HOLD: 1 manuscript(s) produced 13 finding(s): 2 critical, 8 high, 3 warning, and 2 research gap prompt(s).
+
+## Manuscript Decisions
+
+- urban-heat-random-split: HOLD; reproducibility score 0/100; 13 finding(s)
+
+## Findings
+
+- CRITICAL SPATIAL_SPLIT_LEAKAGE: urban-heat-random-split/rf-heat-risk has train/test samples only 0.6 km apart without spatial blocking.
+ - Evidence: Policy requires at least 35 km or explicit spatial block validation.
+ - Remediation: Use spatial block, leave-site-out, or regional holdout validation and regenerate performance claims.
+- CRITICAL TEST_SET_TUNING: urban-heat-random-split/rf-heat-risk tunes model choices on the test/holdout set.
+ - Evidence: Reviewer-facing performance claims require a locked final test set.
+ - Remediation: Move tuning to inner validation folds and rerun the locked test set once.
+- HIGH DATA_MANIFEST_MISSING: urban-heat-random-split is missing reproducibility artifact dataManifest.
+ - Evidence: Geospatial results depend on data, code, and environment parity.
+ - Remediation: Attach a data manifest with sample ids, coordinates, split labels, and hashes.
+- HIGH ENVIRONMENT_SPEC_MISSING: urban-heat-random-split is missing reproducibility artifact environmentSpec.
+ - Evidence: Geospatial results depend on data, code, and environment parity.
+ - Remediation: Attach a pinned environment or container digest for spatial libraries.
+- HIGH FULL_DATASET_PREPROCESSING_LEAKAGE: urban-heat-random-split/rf-heat-risk fits spatial preprocessing on the full dataset.
+ - Evidence: Raster normalization, imputation, or feature selection must be learned inside each training fold.
+ - Remediation: Refit preprocessing inside training folds and attach fold-specific transformation hashes.
+- HIGH HIGH_SPATIAL_AUTOCORRELATION_RANDOM_SPLIT: urban-heat-random-split/rf-heat-risk reports Moran's I 0.62 with a random split.
+ - Evidence: High spatial autocorrelation inflates random train/test validation.
+ - Remediation: Run spatial block cross-validation or leave-region-out validation before presenting performance as reviewer-ready.
+- HIGH MISSING_CRS_EVIDENCE: urban-heat-random-split does not declare a coordinate reference system.
+ - Evidence: Spatial distances, joins, and raster overlays cannot be reviewed without CRS evidence.
+ - Remediation: Declare the source CRS/EPSG code and any analysis projection used for distance or area operations.
+- HIGH MISSING_EXTERNAL_SPATIAL_VALIDATION: urban-heat-random-split/rf-heat-risk lacks external spatial validation for broader deployment claims.
+ - Evidence: Broad geographic or deployment claims should be checked outside the training geography.
+ - Remediation: Add an out-of-region validation site or limit the manuscript claim to the sampled geography.
+- HIGH OVERBROAD_GEOGRAPHIC_CLAIM: urban-heat-random-split makes a broad geographic claim with only 1 observed region(s).
+ - Evidence: The model generalizes across continental urban heat islands.
+ - Remediation: Limit the claim to sampled regions or add external validation sites covering the claimed geography.
+- HIGH SENSITIVE_COORDINATE_OVERPRECISION: urban-heat-random-split exposes sensitive locations at 6 decimal places.
+ - Evidence: Human-subject or protected-species locations should be generalized before reviewer packets or public summaries.
+ - Remediation: Round or jitter coordinates to 4 decimals or provide an approved restricted-location access path.
+- WARNING COVARIATE_SOURCE_MISSING: urban-heat-random-split/rf-heat-risk covariate NDVI has no source citation or artifact id.
+ - Evidence: Raster/vector covariates should be traceable for reproducibility and recency review.
+ - Remediation: Attach a source DOI, artifact id, or repository path for each spatial covariate.
+- WARNING SPATIAL_BLOCK_MAP_MISSING: urban-heat-random-split has spatial validation claims without a block map artifact.
+ - Evidence: Reviewers need the held-out geometry or block map to audit leakage.
+ - Remediation: Attach a block-map artifact id, geometry hash, or leave-site-out manifest.
+- WARNING STALE_COVARIATE_WINDOW: urban-heat-random-split/rf-heat-risk covariate NDVI spans 540 acquisition days.
+ - Evidence: Long covariate windows can hide temporal drift in geospatial models.
+ - Remediation: Use period-matched covariates or report temporal-drift sensitivity checks.
+
+## Research Gap Opportunities
+
+- urban-heat-random-split-regional-replication: Prioritize out-of-region replication before broad geographic claims
+ - Rationale: urban-heat-random-split samples 1 region(s), below the 3-region policy for broad claims.
+ - First action: Recruit or simulate a holdout site in the least represented claimed region.
+- urban-heat-random-split-spatial-validation-gap: Add spatial block validation benchmark
+ - Rationale: High autocorrelation with random validation means reported accuracy may be optimistic.
+ - First action: Create a leave-region-out benchmark and compare it to the random split baseline.
diff --git a/geospatial-spatial-autocorrelation-assistant/reports/summary.svg b/geospatial-spatial-autocorrelation-assistant/reports/summary.svg
new file mode 100644
index 00000000..1e389e92
--- /dev/null
+++ b/geospatial-spatial-autocorrelation-assistant/reports/summary.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/geospatial-spatial-autocorrelation-assistant/sample-data.js b/geospatial-spatial-autocorrelation-assistant/sample-data.js
new file mode 100644
index 00000000..60fd69d9
--- /dev/null
+++ b/geospatial-spatial-autocorrelation-assistant/sample-data.js
@@ -0,0 +1,122 @@
+"use strict";
+
+const riskyPacket = {
+ id: "geo-review-risky-2026-06",
+ policy: {
+ minSpatialHoldoutKm: 35,
+ highMoransI: 0.35,
+ maxSensitivePrecisionDecimals: 4,
+ minRegionsForBroadClaims: 3,
+ maxCovariateWindowDays: 365
+ },
+ manuscripts: [
+ {
+ id: "urban-heat-random-split",
+ title: "Continental urban heat risk from neighborhood satellite features",
+ field: "environmental epidemiology",
+ sensitivity: "human-subjects",
+ spatialDesign: {
+ crs: "",
+ projection: "web map tiles",
+ coordinatePrecisionDecimals: 6,
+ samplingFrame: "three volunteer neighborhoods"
+ },
+ claims: [
+ {
+ id: "claim-generalization",
+ scope: "continental",
+ claimedRegions: ["Northeast", "Midwest", "South", "West"],
+ text: "The model generalizes across continental urban heat islands."
+ }
+ ],
+ samples: [
+ { id: "s-001", lat: 40.712776, lon: -74.005974, split: "train", region: "Northeast", site: "NYC-A" },
+ { id: "s-002", lat: 40.734112, lon: -73.98742, split: "test", region: "Northeast", site: "NYC-B" },
+ { id: "s-003", lat: 40.75891, lon: -73.98513, split: "train", region: "Northeast", site: "NYC-C" },
+ { id: "s-004", lat: 40.76172, lon: -73.97864, split: "test", region: "Northeast", site: "NYC-D" }
+ ],
+ models: [
+ {
+ id: "rf-heat-risk",
+ splitStrategy: "random",
+ moransI: 0.62,
+ preprocessingFitScope: "full_dataset",
+ hyperparameterTunedOn: "test",
+ deploymentContext: "national heat-risk triage",
+ spatialCovariates: true,
+ externalValidationSites: [],
+ covariates: [
+ { name: "NDVI", source: "", resolutionMeters: 1000, acquisitionWindowDays: 540 },
+ { name: "impervious_surface", source: "city-open-data:impervious-v1", resolutionMeters: 30, acquisitionWindowDays: 90 }
+ ]
+ }
+ ],
+ reproducibilityArtifacts: {
+ dataManifest: "",
+ codeCommit: "2f7c91e",
+ environmentSpec: "",
+ spatialBlockMap: ""
+ }
+ }
+ ]
+};
+
+const cleanPacket = {
+ id: "geo-review-clean-2026-06",
+ policy: riskyPacket.policy,
+ manuscripts: [
+ {
+ id: "rangeland-blocked-validation",
+ title: "Regional rangeland recovery forecasts with blocked spatial validation",
+ field: "ecology",
+ sensitivity: "public-environmental",
+ spatialDesign: {
+ crs: "EPSG:4326 WGS84 source coordinates; EPSG:5070 equal-area analysis projection",
+ projection: "EPSG:5070",
+ coordinatePrecisionDecimals: 3,
+ samplingFrame: "blocked stratified ecological sites"
+ },
+ claims: [
+ {
+ id: "claim-regional",
+ scope: "regional",
+ claimedRegions: ["Colorado Front Range", "New Mexico Plateau", "Utah Basin"],
+ text: "The blocked model supports regional recovery forecasts for sampled western rangeland systems."
+ }
+ ],
+ samples: [
+ { id: "co-001", lat: 39.739, lon: -104.99, split: "train", region: "Colorado Front Range", site: "CO-A" },
+ { id: "co-002", lat: 39.231, lon: -105.02, split: "train", region: "Colorado Front Range", site: "CO-B" },
+ { id: "nm-001", lat: 35.084, lon: -106.65, split: "test", region: "New Mexico Plateau", site: "NM-A" },
+ { id: "ut-001", lat: 40.760, lon: -111.89, split: "test", region: "Utah Basin", site: "UT-A" }
+ ],
+ models: [
+ {
+ id: "blocked-gbm-recovery",
+ splitStrategy: "spatial_block_leave_region_out",
+ moransI: 0.18,
+ preprocessingFitScope: "training_fold",
+ hyperparameterTunedOn: "inner_validation",
+ deploymentContext: "",
+ spatialCovariates: true,
+ externalValidationSites: ["New Mexico Plateau", "Utah Basin"],
+ covariates: [
+ { name: "soil_moisture", source: "doi:10.1234/soil-moisture-v3", resolutionMeters: 250, acquisitionWindowDays: 30 },
+ { name: "burn_severity", source: "artifact:burn-severity-2026-05", resolutionMeters: 30, acquisitionWindowDays: 12 }
+ ]
+ }
+ ],
+ reproducibilityArtifacts: {
+ dataManifest: "artifact:geo-sample-manifest-v2",
+ codeCommit: "d6b0e3c",
+ environmentSpec: "container:ghcr.io/scibase/geo-review@sha256:abc123",
+ spatialBlockMap: "artifact:block-map-v2"
+ }
+ }
+ ]
+};
+
+module.exports = {
+ riskyPacket,
+ cleanPacket
+};
diff --git a/geospatial-spatial-autocorrelation-assistant/test.js b/geospatial-spatial-autocorrelation-assistant/test.js
new file mode 100644
index 00000000..9628888f
--- /dev/null
+++ b/geospatial-spatial-autocorrelation-assistant/test.js
@@ -0,0 +1,60 @@
+"use strict";
+
+const assert = require("node:assert/strict");
+const {
+ evaluateGeospatialReviewPacket,
+ renderMarkdownReport,
+ renderSvgSummary,
+ haversineKm
+} = require("./index");
+const { riskyPacket, cleanPacket } = require("./sample-data");
+
+assert.throws(() => evaluateGeospatialReviewPacket(null), /expects a packet object/);
+
+const risky = evaluateGeospatialReviewPacket(riskyPacket, { now: "2026-06-01T10:30:00.000Z" });
+assert.equal(risky.status, "HOLD");
+assert.equal(risky.reviewDecisions[0].decision, "HOLD");
+assert.ok(risky.findings.some((item) => item.code === "SPATIAL_SPLIT_LEAKAGE"));
+assert.ok(risky.findings.some((item) => item.code === "MISSING_CRS_EVIDENCE"));
+assert.ok(risky.findings.some((item) => item.code === "HIGH_SPATIAL_AUTOCORRELATION_RANDOM_SPLIT"));
+assert.ok(risky.findings.some((item) => item.code === "TEST_SET_TUNING"));
+assert.ok(risky.researchGapOpportunities.length >= 2);
+assert.ok(risky.reviewDecisions[0].reproducibilityScore < 50);
+
+const riskyRepeat = evaluateGeospatialReviewPacket(riskyPacket, { now: "2026-06-01T10:31:00.000Z" });
+assert.equal(risky.fingerprint, riskyRepeat.fingerprint);
+
+const clean = evaluateGeospatialReviewPacket(cleanPacket, { now: "2026-06-01T10:30:00.000Z" });
+assert.equal(clean.status, "READY");
+assert.equal(clean.findings.length, 0);
+assert.equal(clean.reviewDecisions[0].reproducibilityScore, 100);
+
+const invalidCoordinatePacket = {
+ id: "invalid-coordinate",
+ manuscripts: [
+ {
+ id: "bad-coordinate",
+ spatialDesign: { crs: "EPSG:4326", coordinatePrecisionDecimals: 2 },
+ claims: [],
+ samples: [{ id: "bad", lat: 110, lon: -74, split: "train", region: "X" }],
+ models: [],
+ reproducibilityArtifacts: { dataManifest: "m", codeCommit: "c", environmentSpec: "e" }
+ }
+ ]
+};
+const invalid = evaluateGeospatialReviewPacket(invalidCoordinatePacket, { now: "2026-06-01T10:30:00.000Z" });
+assert.ok(invalid.findings.some((item) => item.code === "INVALID_COORDINATE"));
+
+const markdown = renderMarkdownReport(risky, riskyPacket);
+assert.ok(markdown.includes("Spatial-Autocorrelation"));
+assert.ok(markdown.includes("SPATIAL_SPLIT_LEAKAGE"));
+assert.ok(markdown.includes("Research Gap Opportunities"));
+
+const svg = renderSvgSummary(risky);
+assert.ok(svg.includes("