7;PaJZ zy7|nKzXn>L*YDVa?oKDPqR0ua20P!z;SDmD-SH!M`^nwhXUbcAlEbDR}+!UDOHy4vBwz#}>`MBj;eu!_YlaeB*d;k^>Z)Tu=&<*mc$q6$TKG*l@b^x^?KlTvuN`_YWS~A#}5f z)JHniC>hf;jyP(oTDt?b@(Ad&_mcSNT#SO9L*YAR;p`fChGlB*2KPypW2npx%|u$I z4SFPuCrHqqAc6wyu{3RCx%{lpHn6qVtst-Te;KWDN(iXTPlz+0cT-mIa;Mx7kPOOU z5(*-Biw&@E^=uwr!pTw*#oM;q&MMS@lSuYbfc9Ye?xMU+DbBBA&K!JEboW_`P4NVl z3S(W3<7%=%#Jb2mhAiF2xJnB0E^ ^{gg!T!s%*19D3S`0EOd#yvfUoL+)g2PvnP$0Ho5SqFuY4+Ixyo z4^PKM3BxjlV99pn(T@3zWgWEm!}|0pVY=S+K(DgX;nHXdqU92Hg~Z(4^zuu?AhdL# zjUjc0=j0ctlBr728w1? zThKFQ?whm@DOR7q* T1jb3^9nB=EWvioH%Fq&j5tZ}D8eWm9N z549?alrp)wV7|b&W0lqwE1IOH>=QM Ef8WI@PFMS<=@6A%fV798rFf3=0g_(I`jqNuV|u#RiVpwr>tp&E`i3 zM&E#CZ&C2qqX2;&@Iih7_D!{#mFwElXs#+2ZTgEZ7htH`S#-v;p_E_d%!WotUgk<< zbT-|vY+!3g_dVBGHujzBxcRZby9}A_qJ>FxwA_3TP=^a;n^&L-Wa-xC%V};*_!|=_ zBjAYJfoM-b3{RrZb%aD|*50c)HF&rCXdMyglw;xF6JnxRimdD_OV-|0Kb4L_lh+9| z5hICg$_`51(D{5@%=s$r?Pq`GF}8L25WnrHgNo6kHt s25uO^hwM1{fqdv3A1_ zg?gs!Qa;2-mNPObAIYx?>&zsZNeZyf;EqfN7lj6}CFVH9It>v}e4H9MX?(soNBV6@ zp9h39tuXFvz*e=RFL_~7IYdJtztdmDGjhwTp5Dr5oRwwl|4c;4ZM#6H$`Gb;DE5MY zM2ps47tBhq36_}^Qi(QQPgSvMo7(_$;eyC!OpVO<=^{Nsn0Bh(m6K~^Wimuo(Xw?v zagS;~<7iBP47!%WtZk+fJqzzXhJlEZ?26YHcokGqx`5@WKS&sGp!jBKk2na&F|%<5 zd~K$jURuaH-4HE0OA@mGg<4^!LD57%GqiqUtZi f6q z|22U#GU$qXyX4En_{GkxXh05vQy4o~yy_|i4uX4s5yQbEWOxOTA>(wB*N r+wo0jl> z8q?{LX^6%f!BDTZtD4!a2i+3!wD?(^coD`dY-E14!?B7WX?Q`wZ2OwfY+Z_9W)A?I z#VjX51nZ~G$vIAa{lfKq2;2K?03q_QR*F|n%8u|WloLn+#eI}`@EC9%_(1$w#)wpX zlZ1C3y q zu&PfIU#NL_yP671L=3F?BJaNfpb?ISDd1N8T$cHUWfEh=(J5;o*Pm=6uFJ^FPvYwd zYXD|GHP#CsAUoFm%T#&mwd*|LienH*cn`!egz9q%FO>{W)%uaPKevu!S05!yokBl+ z-V@66DXRvZrCIr6bW-`p# zMCd4xE(4h -Gyd*k&O}?$x7#nlD30gIuijAdH@hQ_A(Zf7+?DuXj@7_FFjMskioUg6{V0( zrH<^%`;&1_M nKZQeFA^jPn|N`#f3Beuw3l zrliM9xR?u#D^BF%U1&H5I%`>Pc3!#SouW+oxit+_=s7M1eA)`0xI>|``-1j@XfkFy zQjaKUaC7sW=Ke;>=PUAPC-%KCA8gIBy*a3X7#y{;jc?_+LoCY?K%a% ;aIX# z!Ztacadf31tCFr%XfA?9CQ{nmKH!dQSv z&Rpu%VUKjeYt|0~sy@}_i5hrD8+bPl$Vc$kXFF}9$Ud5yzT4TZ&}`E{XMOv~Y3Jj3 zWdI&T-MO2uo%Si#iTJ`^Zms(go`zFg7SQI )<>{yib0 z6R -&Zpz{xVTA7X*wk!z*v*3!5YNRWSczzt-sf2csiV zA>*S{YLBDgWT&&+A09F&L_e@J4Gm%frzjarZzMkLhxw@o{D!SY=eF;?2x;eAM>RIf zuSyLT^Wm64Qe^0%(bR5l)-MfE>&R_nu#J0^=i@~9UI$dteUuv9u)DTcEo;Jrb4weW zux_UxtfewFn?g{-#V*8lq|(9}8b|3n{cUo_Wtbc+_f(#7S@tH}K_?Ow7?zEKYpUrk z+i7Z-!dlUS8RX)UC=7JZps%kK=5bMk(%E;4`mkT$6af`nd5f6s&^g?Uwu2D4j)BcT zwGr#@+Qiep5%oO@y>?9p*2}+$ytd|abolgx`WT1!X6IFr$wcexq{L^$a-q&Lf!L3u z_u5|m%L;p>-w!^i*j?KAtBwx03N)w({-OZOZ0T*48A|StkwHHzq;KiHz9F64Cj1B{ zCEOs)S}^GW@jChxE4M@bqbZ99hsy$WWpug#eWX&3*`)?uxS%J+wtDDWRv&{1=J(AR z3s$bj@a5hR7G)|5tq^mZk+192;pnM|pS#>>iLHRh-ffMpcmCGtSj{i-9VCQ?9dMA7 zY98G%T`mqamDfR4zGJ>w`SAq1o{96};ZQ<;#4Jww=QL)2s=~615dgTf<9fVu5B$Fr zdiw}@zs0+;!0A7-2ZMVw=v$H9IM&L@^4ZTMm*8yNw^2q_o) -^*@ZYk6%o)Wf}Qw|-OTkgDuM*PE)S?!Jq|L_vNeVYM8y3y#)h_9E#ri6!LW-XRD z7EG&M_#=W-&a3yg4pbW}G
M52h3Llk8JUQ7EIPW$8$U#9Y zzoi9aqLGNX@#Qbb$5HfG34h21O`hV!Kb|vSRlV`1WGM)e{k(D*{BA;*DhWgduI%f4 zI&}E6isv1K%g`s2LIg`d`jb4GDJ3{Qu-l-&k!Cn3Q_)@{XAHMkA*KvcMNXgL$E%P- z>XH>)|IgDeuIS3ol~d8T$ssRROt&OY@dTer7sEeP)S AF zV{Hrnnra-r_>zZZ|4w1EJ-^Fj*&J_yNCH)8LP6~#%Ki#wL$@UO>*}aWpZW2*b(HE0 zjF5AEi GvLFFM;R7~_1$3B2g!a)qWZWv?wjDq1g5z>CNjR`M zz?MAut8f-y$m=zrr=>@bUx&d%Xp0xWST(GF^c<3(x fW71otYRW|h--j34ZoEUks84HMi_ z5v*s6|7nj4%b*tVbUObuW(;cd+}ntTowYYS^`xCN$-hRHGh)+BnlhC$FtB6{4w;g- z5$X!Sg;*`y-bkXwVK9&q3oW>h$EqQGPQM`l1RP|+xk7ToP@>OUVg7vioZQ8=?f5zo zot{u6I`SDlFg1~8S%szu*)gA_l>zgkFQCCiRRQ{9O>K^tPsI4;#njN<1v_ThA@m_^ zDd1&&Mk~2ji^Xi5@?|GPUZaH~+IwtyM(KLIXd$Dn?uzA_`?sG$oe#CrFStRMON=G* z0GtKLU{A*xwpeeuo8lP>_!Xm`j%P|~Wcf!%toGMySlHa)1N>~rHWO|CFZf`+EOd3U zyWoiEzn%Rnk9QhpACT2?TtBkg1o@E6ZRcRB);69-w&bOnc|%!jV$2AV(3L>UISok% zGm?FL=BPz~>3ow`Z7+k*Yj;!&Hk;E4`!LL#m>ETFk(&LL0jU;r?!t)qg3xu1l+%T@ z0HG&+5dixWE!+=wWi1sPzP`%H)H4IU8_pGUpaTMcDd!!t2jM2z13LC1NaY9mq7M4B zXC(^yklrbpMPV;W^4drj1ORX0<_Bmq(pt^e&uji*VcHy$;?CqU8 zjzRG%-pjNP{4PV Q~DcUbE+7q0r$oc%gf8vbbCo#~cGA=W^Z(*nnZyWAOr*d3vpnDw?E1 zRv*cDXj0=Hy_#J4BwSkCA5R*yaSbm+qpm%r-yEkG*icyj*a7h$n8+`C(3|#%j_9db z`&WaKzmompU&N+5xM)fy8kPA|j;pyVLTeaQj7+ZWbAAIAw5RH#ERh%?B( 6Rm^dzVF7JACrzBAy^M_o5Rn!8YbC7#*1Z zcV0N0eY8roi2AwPw$+YBn>`{jEdijQuzwOy2g3_tYo>S&C_LZ4M1%9|q6;w)aifiK zKi!Q-VNU3cGjs-f8OL|WLB+H<3Fv2>CKCP>{UdSKE+Vgpl=FF2pg x_}jiT5ZdSQF=)=d4pRdD)J#X~`HK1wj1ZZKvY0lwpD!YWh;fE#!88 z>?Tcmh`gQW$<$GWA}QEcR&qx1w4x%%Gcc8qQon^ 8{?oxv98|6l{ zVdoaBJ-L?CNta6?>~@ruIP8Cu-E#W-JpM_vBrxRzOMMNL=t=2o!4%pd3ytY+O0CEd z{K#d9)RLxj^}{Se_eqY&IT!a@28A?&(N9;Z*SO-Ac;>4^A@hQptdOs}t@8CJX*C!#17;B%v?m zEgzhJbf9m?H#%zBD&cz|F&i`TDSzWhb_&0l|EcP>g_52FKfCpGUwc!8z9!2_&czqN zotE8DMcy7!XOlpQ8!Y9kEBlm==1fd#&;0q$pQHysZ5Us0X7)?I_yd&m%&~2vE`MY% zw;;S{?}x{}4o8-k!p1`(IPX@)(v7$RnAoF*H31mfmJwbDE|wa{m f&c(OmS~=scO>X!7mOXO3Zglu;(o*jhRa9L zY(^JjQk!Tb>P9gI3sb(x>dhmT^f4j`+M@q%%r~hR>|BvYr~HNzK%fD>7?2^u9z!01 z@iwDR^?TxspcW7c%_11DR6TLRzMluY2ego&Ayg4ZAUiciZ?uu~a`g_%j{8IT!y=UB z2j`m=OLlit`&j?lGW@BJ5le*iH;y3%LEc4z<(asPcLM H$}2!yx2w!aFRGYhyk!BvD|et9T5M z?6{}dYj@Um%A}W32pK`(cIBu~6&E(T8w{!}3hO5m-(Dy`c+Hd#q`=Q{DyGd9ly+^w zc)z8 Qs0Qe82DIa{$r<8jQ#t zsrh1Bm|T4&ZbD(G$B1axcwvNz-sCJ+R){O)8H~7>Yew~B&n&uvsLn*?`gXZm8>KmR zg1W=IUmAxUP~{`4uRmj>WcC6NB0*>CT7p5YDT?NWOQJFGgW>`XiVkZEO!-7Tm>E%w z9eMFbaGNlQh4hKgO{)(Z-{Y&~^cDKRx!ZkHVcRbv$w_`@pA(I}gO9&zgh8j)K=Y9; zXq^NIf#rSs@80{W3fb{LdoTB(O5 rt#Gc7diOD{JRRpUeFP^Owp_| zP;c31Dqt$0{inN><)ncKr$I|nGmH}kvoYUl2W8tu&kq^ U=PY*3fO&wlP|w1 zqy* ouY3ffX#Exs>x0nU+Ih8oEA`0{y 5Bwwq7bY_zYTKM{%Jh+dILY$? =KWv -hRDVGAD0R@(ubgbBu4X^`k(_c@}=loy!hE$LAUN3*(6F**@C zbwT{6mTRw*h}j=+mmnwb!+lptnuhn#p2Uz-?e8K@Iu!V{JkFRlmeahaFSY!8QHpI$ zjeFF`MwbDmZ79+*VXR8SEW(V;S@7ZX38!n4&_3ZOSeS;ZHkL_cGo#gp0k?B-vo=Ad7fxzy6M$}L(R-rG|n0*TSrj0N!!5x<{l`4z9-cZH|ZS@Di!@oN1{(;FWD zwCwQ9sB`XBtL)drYqC&LJA=mJa-9X!_9$;^TwCk|3JEF%Y#zsky)kDTrG_VgI$Yhg zPn@~<6>gj7NtPrK=Z0oT7nLY{<=1 gOK<5)+3iGrjnsD~M*VyQY z#eu%VS3q&@XShxraa9ca;Y?b ?H@ABntJeXIKKm8oV2wcXx%Fu7w~^8uGl$p zlj@Ze_sd3}rzs~B{k=h9V>P+#u6moY0fd%ij*> qp9{k3r1EbYv;0zgXyRDJAaX8@29hvZ ztHH(L!LmS9V(G@T+n;V5m>a(6gu%ry_C=*$w62@qua`s@V+~j*rB!~>2`%VJy(|=R zLRsHpwkMFhoUqusnPtK#{mA4KqP=%+BlKhQf@m|pbMDSyD#c@>gKF>=1N8oCHXF-R ziR(y6FHbx`!P&xDBS2^K_v6E3< SalE-*!U-vk7Tlf{0v-{E>8+x0dM*1|Ie9k`OQ`D4KIK&;* zg460)O62> wx~*TBC7yt$NMo5eBKT zLZ9&O{3y0V7nN0KBNXDG|*lbX>`T#a7LPW0^W5lQ$o_PU3k zzB3HN$>gk-DlL%~LAbau)G%6{?n2LlHf604HTw@3p5@TmTAbR4r52{^Osd3ya2J zkJar(srv9m+_a1b=hoBbzNe-HmLxRCXW^Ahca7bboNavwRiB6wTJMJ08Cs~H9R{1z zCx&)641Tl~3>BD3jZ~+k#$ii ?WYZy5c=rt_TB8cn#*&HAn zZMufME4?4)e|;BQ!(eI1+5@X5W<32T5iKhr`@*YAZ-9s7^%-H&Dz&thso7NIYjtMd zf-hu^#t~x}FR_+sxEZ)Z#B1S=n^VzO%;zkK?ww{10)LD96`dIyWiq&H!6_gl)7P@W z3Ac(XZ?mxgEf}0T=jR!}PFKl@Sa1%tU~MuULPdyS&8SG7A@vJRuBqD-$gN%Yu 7~lc zNtG-w8Tc3x=>mdBW%bbI&rA3!(*N*rx^g<;gVQQC-GtwQ<6?R5Win!U_}d-hw;=uP z3FHIN?%7f_Yg~2{Ue$sH82R>EuJM-=^ui47 ZJeCelaXQsFH?XZNPhw@l%nwahL>^2;E_ct(uN7YLlk#K(+yZ8p_a-Iv5N9m~{ zXq=!g6?`yFUxcZ)&0gl%SH 2XgWX+-pq)}e4%b{J(c$h_p z@0-_Ql$G+URzx8Lk{MfS%lW}m$nqJhnVxA+ceNy>5DCq@#xg7?5rg~d?7LEX{ga@C z_of)gBbgCLR5)aqX6zw5a`jIHPRFi=xI!eTIwf&3igE vd6LG(Ns A>2pd*3Pddl(I`( zv~}?z?b_^ Q%yfbN;LIV zxm=O1P kDo~eBLYmOuKO$NdeQh8QPB~3H`pxPuyY7sj58qRe0 z#|9gr^@vTOjpISV4@2ium&Ipu&CKCs3!SOain9`z2xk^hFuqI-DAmvqjM%CYMlf1& z8f?3N0n>17vWooZSzL;u8XVn#r={ApIVl_Z2MK}oF*-RaW Xx*)?#eg&G}==R477CW}rXUw7+j?qD1qKB{>v? zK^q!qr)Rk) ?b }&U zu S}MmJc+{Ig=ju8;l7CZP!B zkM*vtTd7jKQsEKmANwrT$HZQvA%XcEd8Bi9g;AWgI^Xy7cI 39x*`|K7*F?}F*`=YJ~Z<@yv8Ttn}U7hL0pQUTBavrk~qQac(pvJ9eO!y11~ zR5TSwKD_q<`6ft@t!OsNKYL%y3-SL<{Tf0J+}e=#f2aO y@`tRlW?0qv^y^P0@v$gt%dk?8Us0EDb1{JKKWVX)BaB}uRh{gH=IksdY zO+ViC-Df~0D2TwHM6+4`Kg(5tk9Q&@yn}Iqz(F*33k?xkNA5;8RmTSk2OLY9ubE2Q z&*|9|BT9dW#^0idtW(9D@;NnQ5K|~znJQf(z%KYFM}H;8V&T}_su=_f?9Cx7Vvpmk zDJ=}i&ic4W8=gXU0a{#0elpO;v$C%nN>7d+2lD%!m!+t$sSCEUEX@T6;?GiUQY`@h zM~y(aRw`!G!nbW$3oT^b7vuKOX<1HVvCn%L&y|EO)Y9`3vu{eMBgJ52@$O()__9=M zu*4&|G!e6A@NT9NryWS0%t_HSFM4oJBl>OikDK@kxKVJc 8A^S;&ElOkN`~4wQO6a)M{rw#8cyW~vx+Sj1lQ$j5h2vYilB6^1 zT3kcmQ5?nSsVRh-ZK}NN76@ x!@+To<8VK$@M#1OT{BfVl zb}Z6N ?H`cB&r8lFX}+T5x1WV@pW0RX{w$$|9hHZ0tI?bbR Z8NSe}3QzYaxYz%s;_Cszjmw4^j4&hVUHnGuTOC?5%QT~}u zswqSmb(&e+X{2e4QrYZ3y%KIKt_rCy`da=s_8|bXAX8wfqS-34o5<0rJs~9jepdVZ zI&cB@$A2<^;WbGdY9WR7H;y}2a5?err%|$?3cbo}timWkp8{vLP|e>EZ+NysFpC!Q z5*~%0qvpHOwg?w+elz!E-gr1o!lGfOfMGXL@rM6=U7scV (fxckp=3F z_QGO?<6iNTxa!iuMoR%w@&p1Vc3s;<=@D(>IAq)vjr}=!Y{?w;Md;ZK>I~WmVhmPp z+G1K*R1qn?kKa~00YjC0fjr{NcH-d) A>WLZNy9&H`NegnEfzPqEHKn*oK9 z?pHFFwd5V&p6(X~HD?cNgs~ff!cbwOiar1PP0;zhjb9A&L;*7C+*v
VzswTPT#tu$!M!Wfe zX63dKVtjqAyMu%{7c#eB@(bBt3B=w!&~YTmBS|1GK7&r%zP~L63lq&w{o7GcXacDJ zSuvviwON3^eGBbKLIpUe85BC9FYm!D_^i7ASjqiTLKjD>@0;2hYvwry`Y>e~!)%?z z6R|1WrFSD-$I;$u2BlLFRw5;<7=9aK4-pdW-p3=MN+rF4NuSlS(`cx)0JPLuNSUah z3;NCcbJA(e0HLiSK9q$aFXiXw_!F^KI1HwIB=7BkRZg+iP=1WYo7@NaJSuO)^I!7S zlf0dd8<|!^fjhwqtr{EMdNK7!4Np2Ab^^zZx_%v4 ~$_+=+d{mpngXAK$DzlQx zfLAy0B}E3uwbqszutxOAUHwuK?F<7f6S3OoaqLQ(A0u~KM (q3*B#TR7&FjO#nm0nykro@dQc2r%8 z+~|Z`Yzd|- +ev$xt8nu z(zLzdMb&>p>$GKBmtW(Lt@mRN8c6xwQBE&&(qE$Z59G}%v7fBctf;;6=5zZ&U&5p1 z=@;MHr=?U8fVidO1$fA0Ja>(w%X4-XyNErpnR8Q(%|W(It|OJ@4*m|$m`$-;(`GHE zzweQU{KO;PGzqpD(Xkb0RJ0h>$23~U!4vY;+ZuGz$17RUawj>{)4xrD0W~pNG=EMK zP2l~#`oFu6P1Hby%gXd=8(UQr7pq9JUB*ut+;cc@` q5#+c5$E|MiA!qCI@D0`Mo}qtz``maVCo4N}P#-Ux2+{ + diff --git a/collaborative-alt-text-export-guard/reports/manifest.json b/collaborative-alt-text-export-guard/reports/manifest.json new file mode 100644 index 00000000..100fd1e7 --- /dev/null +++ b/collaborative-alt-text-export-guard/reports/manifest.json @@ -0,0 +1,23 @@ +[ + { + "scenario": "clean", + "decision": "export_ready", + "exportAllowed": true, + "findings": 0, + "digest": "be08d841061e2814c94ee6109490d10134a94cf4a1968fc0c3691eb15af4b16c" + }, + { + "scenario": "revision", + "decision": "revise_before_export", + "exportAllowed": false, + "findings": 12, + "digest": "607b93c7dd47480c767a1c1c82689037a95376fdeb97edea7a45d46941ff1fb3" + }, + { + "scenario": "hold", + "decision": "hold_accessible_export", + "exportAllowed": false, + "findings": 3, + "digest": "76c61e090701f62d8a84fb15d6cb8120d1e3183c47c83c62f8560c6a6e16f00f" + } +] diff --git a/collaborative-alt-text-export-guard/reports/revision-report.md b/collaborative-alt-text-export-guard/reports/revision-report.md new file mode 100644 index 00000000..f909565b --- /dev/null +++ b/collaborative-alt-text-export-guard/reports/revision-report.md @@ -0,0 +1,112 @@ +# Collaborative Alt Text Export Guard + +- Document: Shared wetland recovery manuscript +- Decision: revise_before_export +- Export allowed: no +- Findings: 12 +- Digest: 607b93c7dd47480c767a1c1c82689037a95376fdeb97edea7a45d46941ff1fb3 + +## Summary + +- Critical: 0 +- High: 7 +- Warning: 5 +- Info: 0 + +## Findings + +### 1. THIN_OR_GENERIC_ALT_TEXT + +- Severity: warning +- Path: artifacts[0].altText +- Message: Alt text for fig-2 is too thin or generic. +- Evidence: Detected 4 words and generic visual phrasing. +- Remediation: Describe the key variables, trend, and scientific conclusion without relying on sight. + +### 2. COMPLEX_VISUAL_NEEDS_LONG_DESCRIPTION + +- Severity: high +- Path: artifacts[0].longDescription +- Message: Complex figure fig-2 lacks a long description or data summary. +- Evidence: Dense scientific visualizations need a structured non-visual explanation. +- Remediation: Add a long description or machine-readable data summary for complex figures. + +### 3. COLOR_ONLY_ENCODING + +- Severity: high +- Path: artifacts[0] +- Message: Artifact fig-2 relies on color-only interpretation. +- Evidence: Color-only distinctions are not accessible to all readers or output formats. +- Remediation: Add shape, pattern, label, or textual encoding alongside color. + +### 4. ALT_TEXT_STALE_SOURCE_HASH + +- Severity: high +- Path: artifacts[0].altTextSourceHash +- Message: Alt text for fig-2 was reviewed against an older source artifact. +- Evidence: Expected source hash sha256:map-current; found sha256:map-old. +- Remediation: Regenerate or re-approve alt text after the source data or rendering changes. + +### 5. ALT_TEXT_REVIEW_STALE + +- Severity: high +- Path: artifacts[0].altTextReviewedAt +- Message: Alt text for fig-2 predates the latest artifact edit. +- Evidence: Artifact updated at 2026-05-31T18:00:00.000Z; alt text reviewed at 2026-05-31T17:00:00.000Z. +- Remediation: Re-review alt text after collaborative edits. + +### 6. UNRESOLVED_ACCESSIBILITY_COMMENT + +- Severity: high +- Path: comments.c-2 +- Message: Blocking accessibility comment remains open on fig-2. +- Evidence: Alt text must describe the nutrient gradient and not just the visual map. +- Remediation: Resolve or explicitly waive blocking accessibility comments before export. + +### 7. LOCKED_SECTION_ALT_TEXT_UNAPPROVED + +- Severity: high +- Path: artifacts[0].altTextChangedInLock +- Message: Alt text changed inside locked section results without approval. +- Evidence: Final review locks should protect export wording for scientific artifacts. +- Remediation: Collect a locked-section accessibility approval or reopen the section for review. + +### 8. MISSING_ACCESSIBLE_EXPORT_APPROVAL + +- Severity: warning +- Path: accessibilityApprovals +- Message: Artifact fig-2 has no accessible-export approval. +- Evidence: A reviewer sign-off makes export readiness auditable. +- Remediation: Record accessibility review approval after alt text and descriptions are current. + +### 9. THIN_OR_GENERIC_ALT_TEXT + +- Severity: warning +- Path: artifacts[1].altText +- Message: Alt text for tbl-2 is too thin or generic. +- Evidence: Detected 7 words and too little detail. +- Remediation: Describe the key variables, trend, and scientific conclusion without relying on sight. + +### 10. TABLE_HEADER_SCOPE_MISSING + +- Severity: high +- Path: artifacts[1].columns[0] +- Message: Table tbl-2 column 1 lacks header or scope metadata. +- Evidence: Screen readers depend on explicit header scope for scientific tables. +- Remediation: Add header text and row/column scope metadata. + +### 11. TABLE_SUMMARY_MISSING + +- Severity: warning +- Path: artifacts[1].tableSummary +- Message: Table tbl-2 has no concise table summary. +- Evidence: A summary helps readers understand table purpose before navigating cells. +- Remediation: Add a short table summary that names rows, columns, units, and main comparison. + +### 12. MISSING_ACCESSIBLE_EXPORT_APPROVAL + +- Severity: warning +- Path: accessibilityApprovals +- Message: Artifact tbl-2 has no accessible-export approval. +- Evidence: A reviewer sign-off makes export readiness auditable. +- Remediation: Record accessibility review approval after alt text and descriptions are current. diff --git a/collaborative-alt-text-export-guard/reports/revision-result.json b/collaborative-alt-text-export-guard/reports/revision-result.json new file mode 100644 index 00000000..490888e7 --- /dev/null +++ b/collaborative-alt-text-export-guard/reports/revision-result.json @@ -0,0 +1,117 @@ +{ + "guard": "collaborative-alt-text-export-guard", + "evaluatedAt": "2026-06-01T10:15:00.000Z", + "document": { + "id": "manuscript-ecology-002", + "title": "Shared wetland recovery manuscript", + "exportFormat": "docx" + }, + "artifactCount": 2, + "decision": "revise_before_export", + "exportAllowed": false, + "summary": { + "critical": 0, + "high": 7, + "warning": 5, + "info": 0 + }, + "findings": [ + { + "code": "THIN_OR_GENERIC_ALT_TEXT", + "severity": "warning", + "message": "Alt text for fig-2 is too thin or generic.", + "evidence": "Detected 4 words and generic visual phrasing.", + "path": "artifacts[0].altText", + "remediation": "Describe the key variables, trend, and scientific conclusion without relying on sight." + }, + { + "code": "COMPLEX_VISUAL_NEEDS_LONG_DESCRIPTION", + "severity": "high", + "message": "Complex figure fig-2 lacks a long description or data summary.", + "evidence": "Dense scientific visualizations need a structured non-visual explanation.", + "path": "artifacts[0].longDescription", + "remediation": "Add a long description or machine-readable data summary for complex figures." + }, + { + "code": "COLOR_ONLY_ENCODING", + "severity": "high", + "message": "Artifact fig-2 relies on color-only interpretation.", + "evidence": "Color-only distinctions are not accessible to all readers or output formats.", + "path": "artifacts[0]", + "remediation": "Add shape, pattern, label, or textual encoding alongside color." + }, + { + "code": "ALT_TEXT_STALE_SOURCE_HASH", + "severity": "high", + "message": "Alt text for fig-2 was reviewed against an older source artifact.", + "evidence": "Expected source hash sha256:map-current; found sha256:map-old.", + "path": "artifacts[0].altTextSourceHash", + "remediation": "Regenerate or re-approve alt text after the source data or rendering changes." + }, + { + "code": "ALT_TEXT_REVIEW_STALE", + "severity": "high", + "message": "Alt text for fig-2 predates the latest artifact edit.", + "evidence": "Artifact updated at 2026-05-31T18:00:00.000Z; alt text reviewed at 2026-05-31T17:00:00.000Z.", + "path": "artifacts[0].altTextReviewedAt", + "remediation": "Re-review alt text after collaborative edits." + }, + { + "code": "UNRESOLVED_ACCESSIBILITY_COMMENT", + "severity": "high", + "message": "Blocking accessibility comment remains open on fig-2.", + "evidence": "Alt text must describe the nutrient gradient and not just the visual map.", + "path": "comments.c-2", + "remediation": "Resolve or explicitly waive blocking accessibility comments before export." + }, + { + "code": "LOCKED_SECTION_ALT_TEXT_UNAPPROVED", + "severity": "high", + "message": "Alt text changed inside locked section results without approval.", + "evidence": "Final review locks should protect export wording for scientific artifacts.", + "path": "artifacts[0].altTextChangedInLock", + "remediation": "Collect a locked-section accessibility approval or reopen the section for review." + }, + { + "code": "MISSING_ACCESSIBLE_EXPORT_APPROVAL", + "severity": "warning", + "message": "Artifact fig-2 has no accessible-export approval.", + "evidence": "A reviewer sign-off makes export readiness auditable.", + "path": "accessibilityApprovals", + "remediation": "Record accessibility review approval after alt text and descriptions are current." + }, + { + "code": "THIN_OR_GENERIC_ALT_TEXT", + "severity": "warning", + "message": "Alt text for tbl-2 is too thin or generic.", + "evidence": "Detected 7 words and too little detail.", + "path": "artifacts[1].altText", + "remediation": "Describe the key variables, trend, and scientific conclusion without relying on sight." + }, + { + "code": "TABLE_HEADER_SCOPE_MISSING", + "severity": "high", + "message": "Table tbl-2 column 1 lacks header or scope metadata.", + "evidence": "Screen readers depend on explicit header scope for scientific tables.", + "path": "artifacts[1].columns[0]", + "remediation": "Add header text and row/column scope metadata." + }, + { + "code": "TABLE_SUMMARY_MISSING", + "severity": "warning", + "message": "Table tbl-2 has no concise table summary.", + "evidence": "A summary helps readers understand table purpose before navigating cells.", + "path": "artifacts[1].tableSummary", + "remediation": "Add a short table summary that names rows, columns, units, and main comparison." + }, + { + "code": "MISSING_ACCESSIBLE_EXPORT_APPROVAL", + "severity": "warning", + "message": "Artifact tbl-2 has no accessible-export approval.", + "evidence": "A reviewer sign-off makes export readiness auditable.", + "path": "accessibilityApprovals", + "remediation": "Record accessibility review approval after alt text and descriptions are current." + } + ], + "digest": "607b93c7dd47480c767a1c1c82689037a95376fdeb97edea7a45d46941ff1fb3" +} diff --git a/collaborative-alt-text-export-guard/reports/revision-summary.svg b/collaborative-alt-text-export-guard/reports/revision-summary.svg new file mode 100644 index 00000000..5be186a0 --- /dev/null +++ b/collaborative-alt-text-export-guard/reports/revision-summary.svg @@ -0,0 +1,18 @@ + + diff --git a/collaborative-alt-text-export-guard/sample-data.js b/collaborative-alt-text-export-guard/sample-data.js new file mode 100644 index 00000000..b565f02f --- /dev/null +++ b/collaborative-alt-text-export-guard/sample-data.js @@ -0,0 +1,150 @@ +"use strict"; + +const cleanPacket = { + document: { + id: "manuscript-cardiometabolic-001", + title: "Collaborative cardiometabolic atlas draft", + exportFormat: "pdf-html" + }, + sections: [ + { id: "results", title: "Results", locked: true }, + { id: "supplement", title: "Supplementary tables", locked: false } + ], + artifacts: [ + { + id: "fig-1", + type: "figure", + sectionId: "results", + caption: "Figure 1. Multimodal cardiometabolic risk clusters across three cohorts.", + altText: + "Three cohort panels show the same upward risk gradient from cluster A to C, with cluster C carrying the highest inflammatory marker load.", + longDescription: + "Each panel contains three clusters ordered by risk score. Cluster C is consistently highest for CRP and fasting glucose, while cluster A remains lowest across cohorts.", + visualization: "multi-panel scatter", + visualChannels: ["position", "shape", "color"], + nonColorEncoding: true, + sourceHash: "sha256:figure-one-current", + altTextSourceHash: "sha256:figure-one-current", + updatedAt: "2026-05-31T15:00:00.000Z", + altTextReviewedAt: "2026-05-31T16:00:00.000Z", + altTextChangedInLock: true + }, + { + id: "tbl-1", + type: "table", + sectionId: "supplement", + caption: "Table 1. Cohort sample counts and assay coverage.", + altText: + "The table compares sample counts, assay coverage, and missingness rates, showing cohort B has the broadest assay coverage and lowest missingness.", + tableSummary: + "Rows are cohorts and columns summarize sample count, proteomics coverage, metabolomics coverage, and missingness percentage.", + columns: [ + { id: "cohort", header: "Cohort", scope: "row" }, + { id: "n", header: "Samples", scope: "col" }, + { id: "proteomics", header: "Proteomics coverage", scope: "col" }, + { id: "missingness", header: "Missingness", scope: "col" } + ], + sourceHash: "sha256:table-one-current", + altTextSourceHash: "sha256:table-one-current", + updatedAt: "2026-05-31T15:15:00.000Z", + altTextReviewedAt: "2026-05-31T16:00:00.000Z" + } + ], + comments: [ + { + id: "c-1", + artifactId: "fig-1", + status: "resolved", + blocking: true, + summary: "Alt text now names the cross-cohort trend." + } + ], + accessibilityApprovals: [ + { artifactId: "fig-1", scope: "accessible-export", status: "approved" }, + { artifactId: "fig-1", scope: "locked-section-alt-text", status: "approved" }, + { artifactId: "tbl-1", scope: "accessible-export", status: "approved" } + ] +}; + +const revisionPacket = { + document: { + id: "manuscript-ecology-002", + title: "Shared wetland recovery manuscript", + exportFormat: "docx" + }, + sections: [{ id: "results", title: "Results", locked: true }], + artifacts: [ + { + id: "fig-2", + type: "figure", + sectionId: "results", + caption: "Figure 2. Nutrient recovery map after restoration.", + altText: "Figure showing a map.", + visualization: "heatmap", + visualChannels: ["position", "color", "intensity"], + nonColorEncoding: false, + sourceHash: "sha256:map-current", + altTextSourceHash: "sha256:map-old", + updatedAt: "2026-05-31T18:00:00.000Z", + altTextReviewedAt: "2026-05-31T17:00:00.000Z", + altTextChangedInLock: true + }, + { + id: "tbl-2", + type: "table", + sectionId: "results", + caption: "Table 2. Sensor calibration ranges.", + altText: "Sensor calibration ranges by station and month.", + columns: [{ id: "station", header: "Station" }], + sourceHash: "sha256:table-two", + altTextSourceHash: "sha256:table-two", + updatedAt: "2026-05-31T16:00:00.000Z", + altTextReviewedAt: "2026-05-31T16:30:00.000Z" + } + ], + comments: [ + { + id: "c-2", + artifactId: "fig-2", + status: "open", + blocking: true, + summary: "Alt text must describe the nutrient gradient and not just the visual map." + } + ], + accessibilityApprovals: [] +}; + +const holdPacket = { + document: { + id: "manuscript-neuro-003", + title: "Blinded neuroimaging collaboration", + exportFormat: "html" + }, + sections: [{ id: "blind-results", title: "Blind review results", locked: false }], + artifacts: [ + { + id: "fig-3", + type: "figure", + sectionId: "blind-results", + caption: "Figure 3. Functional connectivity change after protocol revision.", + altText: + "Dr. Smith noted this image of cohort changes in /Users/reviewer/private/holdout, including patient-id 8842 from the reviewer-only packet.", + longDescription: "Reviewer-only note should not leave the blinded workspace.", + visualization: "network", + visualChannels: ["position", "color", "edgeWidth"], + nonColorEncoding: true, + sourceHash: "sha256:network-current", + altTextSourceHash: "sha256:network-current", + updatedAt: "2026-05-31T15:00:00.000Z", + altTextReviewedAt: "2026-05-31T15:10:00.000Z" + } + ], + comments: [], + accessibilityApprovals: [{ artifactId: "fig-3", scope: "accessible-export", status: "approved" }] +}; + +module.exports = { + cleanPacket, + revisionPacket, + holdPacket +}; diff --git a/collaborative-alt-text-export-guard/test.js b/collaborative-alt-text-export-guard/test.js new file mode 100644 index 00000000..202af133 --- /dev/null +++ b/collaborative-alt-text-export-guard/test.js @@ -0,0 +1,61 @@ +"use strict"; + +const assert = require("node:assert/strict"); +const { + evaluateAccessibleExportPacket, + renderMarkdownReport, + renderSvgSummary, + digest +} = require("./index"); +const { cleanPacket, revisionPacket, holdPacket } = require("./sample-data"); + +const fixedNow = "2026-06-01T10:15:00.000Z"; + +function clone(value) { + return JSON.parse(JSON.stringify(value)); +} + +function codes(result) { + return new Set(result.findings.map((finding) => finding.code)); +} + +const cleanBefore = clone(cleanPacket); +const cleanResult = evaluateAccessibleExportPacket(cleanPacket, { now: fixedNow }); +assert.equal(cleanResult.decision, "export_ready"); +assert.equal(cleanResult.exportAllowed, true); +assert.equal(cleanResult.findings.length, 0); +assert.deepEqual(cleanPacket, cleanBefore, "evaluation must not mutate the packet"); + +const revisionResult = evaluateAccessibleExportPacket(revisionPacket, { now: fixedNow }); +assert.equal(revisionResult.decision, "revise_before_export"); +assert.equal(revisionResult.exportAllowed, false); +assert.equal(codes(revisionResult).has("THIN_OR_GENERIC_ALT_TEXT"), true); +assert.equal(codes(revisionResult).has("COMPLEX_VISUAL_NEEDS_LONG_DESCRIPTION"), true); +assert.equal(codes(revisionResult).has("COLOR_ONLY_ENCODING"), true); +assert.equal(codes(revisionResult).has("ALT_TEXT_STALE_SOURCE_HASH"), true); +assert.equal(codes(revisionResult).has("ALT_TEXT_REVIEW_STALE"), true); +assert.equal(codes(revisionResult).has("UNRESOLVED_ACCESSIBILITY_COMMENT"), true); +assert.equal(codes(revisionResult).has("LOCKED_SECTION_ALT_TEXT_UNAPPROVED"), true); +assert.equal(codes(revisionResult).has("TABLE_HEADER_SCOPE_MISSING"), true); + +const holdResult = evaluateAccessibleExportPacket(holdPacket, { now: fixedNow }); +assert.equal(holdResult.decision, "hold_accessible_export"); +assert.equal(holdResult.exportAllowed, false); +assert.equal(codes(holdResult).has("PRIVATE_PATH_OR_DATA_LEAK"), true); +assert.equal(codes(holdResult).has("REVIEWER_IDENTITY_LEAK"), true); + +const report = renderMarkdownReport(holdResult); +assert.match(report, /Collaborative Alt Text Export Guard/); +assert.match(report, /hold_accessible_export/); +assert.match(report, /PRIVATE_PATH_OR_DATA_LEAK/); + +const svg = renderSvgSummary(revisionResult); +assert.match(svg, /^<\?xml version="1\.0"/); +assert.match(svg, /Accessible Export Guard/); +assert.match(svg, /revise_before_export/); + +assert.equal(digest(cleanPacket), digest(clone(cleanPacket))); +assert.notEqual(digest(cleanPacket), digest(revisionPacket)); +assert.throws(() => evaluateAccessibleExportPacket(null), /packet object/); + +console.log("All collaborative alt text export guard tests passed.");